I'm writing an angularjs directive, and receive properties from html attributes.
Then I created an isolate scope for my directive, and modified the value of the scope properties, but the directive template does not show the modified value.
I created a demo in plunker.
Can anyone help me changing to template value 'test' to 'changeit'?
Let me describe my questions more detail:
I may have a directive and in the html like: , the 'top' is just a simple string, not any ngModel, so I use '#' to receive it in directive, but when I modify it in link, the template model value not change, then I try '=', as it is not an ngModel, so the received value is undefined, but can be dynamic changed in template {{}}.
You are using attribute based linking that is one way. For two way binding use = in your directive definition. Something like:
scope: {
title: '=title'
}
Update: I got your question now. The issue here is that, the # based binding keeps the parent scope and the isolated scope in sync. So even if you change the bound variable in the directive, it will be reverted back to the original value. You can verify this by adding a $timeout to the second console log
$timeout(function(){
console.log(scope.title);
})
the value of title is reverted back.
The option you have is to copy the title value into a new variable in the link function and bind that function to your template.
Related
I have created one custom directive using isolate scope for way data binding.
I am broadcasting event from parent to change one of the variable value and then binding this changed value to variable which is defined for two way binding in directive defination object. as given below :-
$scope.$on('updateData', function(event, data) {
//some code
$scope.datatosend = **NEW_VALUE**;
});
return {
restrict : 'E',
replace : 'true',
scope : {
datatoload : "=",
datatosend : "=", // assigning updated value to this variable
setFn : '&'
},
<locationgrid datatoload="location" datatosend="finaldata"></locationgrid>
used directive 'locationgrid' in parent template as above.
this variable is reflecting chnages on parent html template as i have tried setting that variable as ng-model for one of the textbox.
But this updated value is not reflecting in parent controller. it gives 'undefined' in parent controller , but updated value can be seen in parent html template.
Please help have spent lot of time but all in vain , tried $scope.$watch,$scope.$apply,$timeout.any help is appreciated.
May be this will be helpful for someone so writing , I was using angularJS 1.5 which doesn't support $watch the prior versions used to.
Got answer at below link
AngularJs 1.5 - Component does not support Watchers, what is the work around?
Thanks
I'm attempting to watch a custom directive attribute value inside the directive. This value is a variable binding from a controller. The variable is a boolean and is updated via an action in the controller.
I can see that I'm updating this value in the controller action correctly through console.logs but I cannot seem to get the directive to watch for changes to this value. As I said, this value is the value of a custom directive: auto-focus="{{isFocused}}"
I've created a simple plunker to show my problem, any help would be great.
Angular - listening to binding changes in directive executed by controller
http://plnkr.co/edit/QwwFCQPN7L7nwuthH0CJ?p=preview
In order to watch for changes in an attribute that has an interpolation you'll need to use $attrib.$observe instead of $scope.$watch. This is described in the angularjs documentation: http://docs.angularjs.org/api/ng/service/$compile#Attributes
Also, you're comparing focusVal to true, but you're passing it as a string through the attribute. Here's an updated plunker: http://plnkr.co/edit/e8ZaBM?p=preview
In the focus-on directive, you have:
focus-on="{{isFocused}}"
This makes your directive actually watch what isFocus contains - which is "false"
Change it to:
focus-on="ifFocused"
Then your code works fine.
I have a directive and I want to change the ng-model value given with this directive...
I'm setting scope: {ngModel: '='} and I'm changing the ngModel value (on click event) inside my directive but I can't see changes on my external/original object.
This plunker shows the problem...
There are a few things wrong here, all of them common mistakes.
Event handlers registered through jQuery using $(...).on(...) will be executed outside of angular context, so angular will not know when things have updated. To address this, you must wrap the contents in a scope.$apply call like so
$('#aaa').on('click', function() {
_scope.$apply(function(){
_scope.ngModel = 'Other Value';
updateTemplate();
});
});
This will update the binding to the input with ng-model. In fact you can avoid having to do this by using the ng-click directive.
With angular, you do not need to update templates like this yourself using .html(...). Binding is one of the major features of the framework. Instead of having the update function, you can use interpolation by putting an expression inside of {{ ... }} and your DOM will be updated when your model is. For example when defining the directive you can use
template: '<div id="aaa">{{ngModel}}</div>'
to set your template and {{ngModel}} will show the current value of ngModel.
ngModel is not just any attribute, it is a powerful directive. If you need your own directive to be able to declare the current model valid or invalid, or to interact with forms then you should use this through the require property on your controller (see here).
If you don't need those features then you should be calling your attribute something different to avoid conflict.
I have updated the plunker to include these points.
Before posting this fiddle, i checked SO for similar question. Got few answer but all those were not form elements. http://jsfiddle.net/dgQAd/
I have the following questions:
1) The textbox is bound to a model uname, but onload the textbox is not displaying the value. why this is happening?
2)while searching for answers for this, i saw something like require:ngModel, and injecting a controller inside the linking function, how can i use this injected controller inside the linking function of the directive.
3)How to look for the changes in the parent scope ng-model from inside a linking function of an isolate scope directive.
The only way I've been able to get ng-model to work with an isolate scope is to use the same name for the isolate scope property: scope:{ "uname":"=ngModel" }. Your $watch will now work.
For more on this see also https://stackoverflow.com/a/14792601/215945
When a directive requires another directive's controller, that controller is available as the 4th option to the linking function. In your fiddle, that is what you called ngModel:
link:function(scope,el,attrs,ngModel){
Normally, I prefer to name this ngModelCtrl to remind me that it is a controller.
$observe is only used with isolate scope properties that use the '#' syntax.
I am attempting to create a custom tag similar to the following:
<mytag type="Big" />
where type is an attribute that gets bound to the component. in such a way that it sets the text in a label, as shown below:
<label>{{type}}</label>
... (other components)...
As the documentation says, I have a controller that sets a default type:
$scope.type = "Small";
so that if I use my tag without the attribute type still gets set.
I am attempting to do binding using a directive:
angular.module('TestPage',[])
.directive('mytag',function() {
return {
restrict: 'E',
templateUrl: 'component.html',
scope: {
type: '='
}
}
});
Note that I do have the appropriate ng-app settings in my component template (ng-app="TestPage").
My problem is that the binding to type does not appear to be actually binding anything.
I have read the documentation about how to bind a variable to components using directive. According to the documentation, you can do such bindings inside a scope. Scopes apparently can contain an "object-hash" (whatever that is!) which creates something called an "isolate scope" (???). Such scopes can represent "local properties" in the following ways:
# or #attr - bind a local scope property to the DOM attribute. The result is always a string
since DOM attributes are strings. If no attr name is specified then the local name and
attribute name are same. Given and widget definition of scope: { localName:'#myAttr' }, then widget scope property localName will reflect the interpolated value of hello {{name}}. As the name attribute changes so will the localName property on the widget scope. The name is read from the parent scope (not component scope).
Huh??? What has all this to do with the proper syntax for binding?
= or =expression - set up bi-directional binding between a local scope property and the parent
scope property. If no attr name is specified then the local name and attribute name are same.
Given and widget definition of scope: { localModel:'=myAttr' }, then widget scope property localName will reflect the value of parentModel on the parent scope. Any changes to parentModel will be reflected in localModel and any changes in localModel will reflect in parentModel.
Excuse me? What is being said here???
& or &attr - provides a way to execute an expression in the context of the parent scope. If no
attr name is specified then the local name and attribute name are same. Given
and widget definition of scope: { localFn:'increment()' },
then isolate scope property localFn will point to a function wrapper for the increment() expression. Often it's desirable to pass data from the isolate scope via an expression and to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).
Now I'm totally confused! You have widget tags and some kind of related function that I have to write iin order to do the bind??? All I want is to bind a value to a label tag!
I have copied the above text from the documentation (http://docs.angularjs.org/guide/directive) to make a point: that this doco reads like the old UNIX documentation: really useful to those who already know the system, but not so helpful to beginners who are trying to develop real expertise. With all the tutorials that show how to do simple tasks in AngularJS (great for toy apps but not so good for the kinds of client- side applications I want to build), why aren't there any for the more advanced stuff???
Okay, time for me to be more constructive.
Can someone please provide some nice, simple examples of how to do the various bindings that this documentation is trying so hard to describe??? Examples that show the proper syntax for these scope statements and descriptions (in plain English) of exactly how they go back to the attribute being added to the custom tag???
Thank you for your patience and thanks in advance for any assistance.
I struggled a bit with this documentation too when first getting into angular, but I will make an attempt try to clarify things for you. First, when using this scope property, it creates an "isolated scope." All this means is that it won't inherit any properties from parent scopes, and so you don't have to worry about any collisions within the scope.
Now, the '#' notation means that the evaluated value in the attribute will automatically get bound into your scope for the directive. So, <my-directive foo="bar" /> would end up with the scope having a property called foo that holds the string "bar". You could also do something like <my-directive foo="{{bar}}" And then the evaluated value of {{bar}} will be bound to the scope. Since attributes are always strings, you will always end up with a string for this property in the scope when using this notation.
The '=' notation basically provides a mechanism for passing an object into your directive. It always pulls this from the parent scope of the directive, so this attribute will never have the {{}}. So, if you have <my-directive foo="bar" /> it will bind whatever is in $scope.bar into your directive in the foo property of your directive's scope. Any change's you make to foo within your scope will be refelected in bar in the parent scope, and vice versa.
I haven't used the '&' notation nearly as much as the other too, so I don't know it as well as those two. From what I understand, it allows you to evaluate expressions from the context of the parent scope. So if you have something like <my-directive foo="doStuff()" />, whenever you call scope.foo() within your directive, it will call the doStuff function in the directive's parent scope. I'm sure there's a lot more you can do with this, but I'm not as familiar with it all. Maybe someone else can explain this one in more detail.
If just the symbol is set in the scope, it will use the same name as the attribute to bind to the directives scope. For example:
scope: {
foo1: '#',
foo2: '=',
foo3: '&'
}
When including the directive, there would need to be the attributes foo1, foo2, and foo3. If you want a property in your scope different than the attribute name, you can specify that after the symbol. So, the example above would be
scope: {
foo1: '#bar1',
foo2: '=bar2',
foo3: '&bar3'
}
When including the directive, there would need to be the attributes bar1, bar2, and bar3, and these would get bound in the scope under properties foo1, foo2, and foo3 respectively.
I hope this helps. Feel free to ask questions with which I can clarify my answer.
Youre pretty close..
app.directive('mytag',function() {
return {
restrict: 'E',
template: '<div>' +
'<input ng-model="controltype"/>' +
'<button ng-click="controlfunc()">Parent Func</button>' +
'<p>{{controlval}}</p>' +
'</div>',
scope: {
/* make typeattribute="whatever" bind two-ways (=)
$scope.whatever from the parent to $scope.controltype
on this directive's scope */
controltype: '=typeattribute',
/* reference a function from the parent through
funcattribute="somefunc()" and stick it our
directive's scope in $scope.controlfunc */
controlfunc: '&funcattribute',
/* pass a string value into the directive */
controlval: '#valattribute'
},
controller: function($scope) {
}
};
});
<div ng-controller="ParentCtrl">
<!-- your directive -->
<mytag typeattribute="parenttype" funcattribute="parentFn()" valattribute="Wee, I'm a value"></mytag>
<!-- write out your scope value -->
{{parenttype}}
</div>
app.controller('ParentCtrl', function($scope){
$scope.parenttype = 'FOO';
$scope.parentFn = function() {
$scope.parenttype += '!!!!';
}
});
The magic is mostly in the scope: declaration in your directive definition. having any scope: {} in there will "isolate" the scope from the parent, meaning it gets it's own scope... without that, it would use the parent's scope. The rest of the magic is in the scope's properties: scope: { 'internalScopeProperty' : '=externalAttributeName' }... where the = represents a two way binding scenario. If you change that = to a # you'll see it just allows you to pass a string as an attribute to the directive. The & is for executing functions from the parent scope's context.
I hope that helps.
EDIT: Here is a working PLNKR