Binding Ngmodel To A Custom Directive
Solution 1:
Directives are not the easiest concepts out there and documentation is really not that good and it's scattered around the interwebs.
I struggled with compile
, pre-compile
and such when I tried to write my first directives but to date I have never needed those functions. It might be due to my lack of understanding but still...
Looking at your examples I see there's some basic things that needs clarification. First of all, I'd restrict your directive to E
lement since it's replacing the control in HTML. I'd use A
ttribute e.g. to add functionality to existing control.
There is a (mandatory) naming convention where you use dashed naming in HTML and camel casing inside your JavaScript. So something-cool
becomes somethingCool
. When you "bind" variables to directive's scope, there's a major difference on how you do it. Using =
you bind to variable, using @
to variables evaluated (string) value. So first allows the "two-way binding" but latter of course, not. You can also use &
to bind to parent scope's expression/function.
If you use e.g. plain =
then directive's scope expects same name in your HTML. If you wish to use different name, then you add variable name after the =
. An example
ngModel : '=' // <divng-model="data"></div>
otherVar: '@someVar' // <divsome-var="data></div> or <some-var="data"></some-var>
I took liberty to take your first Fiddle of metro-input-transform
as starting point and rewrite it in Plunker. I'm trying to explain it here (and hope I understood you right).
Metro input directive
directives.directive('metroInput', function () {
return {
restrict: 'E',
scope: {
ngModel: '=',
placeholder: '@watermark'
},
link: function (scope) {
scope.clear = function () {
scope.ngModel = null;
};
},
templateUrl: 'metro-template.html'
};
});
Directive expects ngModel
to bind to and watermark
to show when ngModel has no value (text input is empty). Inside link
I've introduced clear()
function that is used within directive to reset ngModel
. When value is reset, watermark
is show. I have separated the HTML parts into a separate file, metro-template.html.
Metro input HTML template
<input type="text" ng-model="ngModel" placeholder="{{ placeholder }}">
<button type="button" class="btn-clear" ng-click="clear()">x</button>
Here we bind ngModel
to input and assign placeholder
. Button showing [X] is bound to clear()
method.
Now when we have our directive set up, here's the HTML page using it.
HTML page
<body><divng-controller="Ctrl"><section>
The 'Product name' textbox in the 'Directive'
fieldset and the textbox in the 'Controls'<br>
fieldset should all be in sync.
</section><br><fieldset><legend>Directive</legend><labelfor="productName">Product name</label><br><metro-inputname="productName"ng-model="data.productName"watermark="product name"></metro-input></fieldset><br><fieldset><legend>Control</legend><inputdetect-mouse-overtype="text"ng-model="data.productName"></fieldset></div></body>
So in above example usage of metro directive is as follows. This will be replaced with directive's HTML template.
<metro-input name="productName"
ng-model="data.productName"
watermark="product name">
</metro-input>
The other input has detect-mouse-over
directive applied to it, restricted to A
ttribute just to show usages/differences between A
and E
. Mouse detection directive makes input change background-color when mouse is moved over/out of it.
<input detect-mouse-over
type="text"
ng-model="data.productName">
.
directives.directive('detectMouseOver', function () {
return {
link: function (scope, element, attrs) {
element.bind('mouseenter', function () {
element.css('background-color', '#eeeeee');
});
element.bind('mouseleave', function () {
element.css('background-color', 'white');
});
}
};
});
It also has same ng-model
to mirror changes between controls.
In your example you also had a productService
that provided the value to above input controls. I rewrote it as
Product service
app.service('productService', function () {
return {
get: function () {
return { productName: 'initial value from service' };
}
};
});
So get()
function just gets the hard coded value but it still demonstrates use of services. Controller, named Ctrl
is really simplistic. Important part here is that you remember to inject all services and such into your controller. In this case angular's $scope
and our own productService
.
Controller
app.controller('Ctrl', function ($scope, productService) {
$scope.data = productService.get();
});
Here a screen capture of above solution.
Changing value in any of the inputs changes value of both. Input below has "mouseover" so it's greyish, mouseout would turn it white again. Pressing [X] clears the value and makes placeholder visible.
Here's the link to plunker once more http://plnkr.co/edit/GGGxp0
Solution 2:
Ok I'm not exactly sure what other advantages from the Metro UI you're getting, but here's a simple fiddle that doesn't need your directive at all to capture what you had in your first fiddle that works for me. http://jsfiddle.net/f0sph1vp/7/
<inputplaceholder="{{page.placeholder}}"ng-model="page.data.productName"ng-focus="page.data.productName=''"><buttonng-click="page.data.productName=''">x</button>
The second fiddle you posted, http://jsfiddle.net/gary_stenstrom/xcx2y8uk/64/, is pretty weird to me, because it doesn't seem like you want the second input box to be the same model as your first one. It kind of seems like you want the clicking of the x button to assign the value of the first input to the second. Which makes a lot more sense.
<inputng-model="data.first"><buttonng-click="data.second = data.first; data.first=''">X</button
<inputng-model="data.second">
Post a Comment for "Binding Ngmodel To A Custom Directive"