directive design: pass through <input> attributes?

598 views
Skip to first unread message

Nikita Tovstoles

unread,
Apr 18, 2014, 6:05:07 PM4/18/14
to ang...@googlegroups.com
Hi, I am building an edit-in-place directive - similar to x-editable - a widget that toggles between an anchor displaying a value and an input editing it. Would appreciate some feedback on the following impl choices:

I would like to support arbitrary ng-x validation attributes that work with input[text]. Seems like the most elegant way to do so would be to restrict: 'A' the directive, and leverage ng-model, so that as client programmer I could enable this directive participate in AJS form validation and 

<form name="myForm"><input name="foo" type="text" ng-model="foo" required ng-pattern="" editable></form>

...can call scope.myForm.foo.$valid

However, I think that won't work because I can't specify a directive template (or templateUrl) with an <input> since that element cannot have children. So I must use restrict: 'E', right? (Unless I ditch the template and instead provide a compile() fn that appends template DOM to <input>, but that would be harder to maintain)?

So now I have an element directive:

<editable name="foo" ng-model="foo" required ng-pattern=""></editable>

and associated template:

<ng-form name="innerForm" ng-show="edit">
<input type="text">
</ng-form> 
<a ng-show="!edit" ng-click="edit=true">

But now I have to enable support input[text] validation attr support on <editable>. One way to do so would be by copying attributes from <editable> onto the inner <input> in directive's compile() fn ie:

<editable name="foo" ng-model="foo" e-ng-pattern="/expr/"></editable>

...would produce the following template:

<ng-form name="innerForm" ng-show="edit">
<input type="text" ng-pattern="/expr/">
</ng-form>
<a ng-show="!edit" ng-click="edit=true">

...by having compile() find attributes with 'e-' prefix and replicate them on inner <input>. Is there a more elegant approach? One where I could somehow stick with attribute directive (on <input>)?


thanks
-nikita


Luke Kende

unread,
Apr 19, 2014, 1:52:12 AM4/19/14
to ang...@googlegroups.com
<editable type="username" value="userr.name"></editable>

template:
<span class="user-info" ng-hide="edit">{{value}}</span>

<ng-form name="innerForm" ng-show="edit">
<div ng-switch on="type">
<input ng-switch-when="username" ng-model="username" type="text" ng-pattern="/username pattern/">
<input ng-switch-when="password" ng-model="password" type="text" ng-pattern="/password pattern/"> 
<input ng-switch-when="email" type="text" ng-pattern="/email pattern/">
</div> 

Nikita Tovstoles

unread,
Apr 19, 2014, 9:54:05 AM4/19/14
to ang...@googlegroups.com
Thanks for the reply, Luke. However I don't think your suggestion solves my issue as it describes a specialized directive (ie one supporting ng-pattern only) whereas I am looking for a general-purpose solution - i.e. either of the following:
  • element-level directive that supports any and all validators - present and future - that are supported by input[text]. it <editable ng-pattern ng-min> etc. So that if any 'x-foo' is a valid here <input type="text" ng-model="" x-foo>, it should also work on <editable x-foo> OR
    • can I do than copying attributes from <editable> to inner <input> during compile()?
OR 
  • attribute-level directive i.e. - <input type="text" editable> that 'decorates' <input> with click-to-edit UI I described above.
    • again, the problem here is that 'template' cannot be used with <input>.

I also thought of using transclude ie:

<editable><input type="text" ng-model="" ng-required="" ng-pattern ng-minlength=""></editable>

..to click-to-edit whatever's transcluded but (and I could be wrong here) I would not be able to access transcluded contents' ng-model from <editable>'s link().

-nikita


--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/cTkiixj_7ms/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.

Kamal

unread,
Apr 19, 2014, 2:00:44 PM4/19/14
to ang...@googlegroups.com
Nikita,

I do see two way to do it one with `transclude` and the other with `element.wrap`, where in both the case you can use restrict: A (attribute). 

http://plnkr.co/edit/n46dCxV8nHaJAaxdkZfG?p=info -- transclude

I would prefer the wrap, as in the case of transclude it creates and additional child scope which would cause some binding issues u can have a look at the demo by adding a ng-model and see how it works. In the case of wrap we just moving the input inside a different parent which would replace its current position therefore no new scope, no issues. I hope this helps.

Regards,
Kamal

Nikita Tovstoles

unread,
Apr 20, 2014, 10:54:07 AM4/20/14
to ang...@googlegroups.com
Thanks for your input, Kamal. I tried out both approaches and am having some difficulties:
  • wrap: does not display ng-model's initial value; also inlining HTML makes it difficult to develop a more elaborate UI (for example overlay ok/cancel buttons over input). demo (link should read 'bob' on initial render)
  • transclude: does not respect ng-model on input: so that external changes to value of expression of ng-model aren't reflected (i.e. two-way binding lost) (demo)
However, I do agree that transclude can be used. I think the only wrinkle is keeping scope.$parent in sync with transclude scope wrt <input> binding (for shadow scope property case wherein ng-model is bound either to a primitive or to a not-yet-existing object ie. ng-model="foo.bar" if foo is undefined initially). demo forthcoming

Thanks again,

-nikita

Kamalakar Gadireddy

unread,
Apr 20, 2014, 11:41:58 AM4/20/14
to ang...@googlegroups.com
Nikita,

Initial value is not the issue with the warp it with how the value is being set for the first time. I have updated the example to solve that and i remembered an other way to warp it which would be preferable way to make it easy for any template. Check out the updated demo for warp http://plnkr.co/edit/lNegC0?p=preview you can also use $templateCache service with a custom tag which would be replaced by the input (there by warping it inside at any given location) where the input to use the angular template engine. http://plnkr.co/edit/RioTmn?p=preview

If you want to deal with a little more complex code on how to transclude/wrap and create elements inside have a look at my git directive  https://github.com/gKodes/ngul/blob/master/js/nu.list.js / to play with http://jsfiddle.net/gKodes/xnTrZ/light/

Hope this give a much better picture.

Regards,
Kamalakar Gadireddy

Nikita Tovstoles

unread,
Apr 21, 2014, 1:49:09 PM4/21/14
to ang...@googlegroups.com
That version seems to suffer from the same issue - 2-way binding on ng-model is not respected (for example if I edit {{test}} inside the directive, the value in <pre> above it does not get updated)

I took a different approach, using transclude and then synchronizing ngModel's expression values between transcluded and parent scopes. Seems to work, though I would certainly appreciate any feedback. Here's the demo: http://plnkr.co/edit/YsnczYfKH7vyqFbZLi6y?p=preview

notice that {{foo}} external to the directive, directive input and label all are synchronized - irrespectively whether {{foo}} is updated by the directive or other controls.

Kamalakar Gadireddy

unread,
Apr 22, 2014, 6:38:21 AM4/22/14
to ang...@googlegroups.com
I did notice the issue was occurring because of using `angular.element.replaceWith` which when replacing the element (in this case the input) would daloc/unbind all the events, which in this case would cause ng-model not to work when any changes made to the input value. I found the use of this sort off directive interesting and created one of my own you can take a look at the demo here http://plnkr.co/edit/qvnLq1?p=preview and the source at https://github.com/gKodes/ngul/blob/master/js/nu.wrap.js. It still has some issue for rendering complex object, this is just a preview so only had concentrated on input type `text`.

Regards,
Kamalakar Gadireddy


Reply all
Reply to author
Forward
0 new messages