Dynamically initialize a ng-model directive - angularjs

I'm dynamically building a form with angular depending of the kind of input:
<div ng-controller="formController" ng-repeat="field in formFields" ng-switch="field.type">
<div ng-switch-when="text">
<!-- Something -->
</div>
<div ng-switch-when="dropdown">
<myDirective my-data="field.param" ng-model="field.model"></myDirective>
</div>
</div>
I've got two problems with my directive which builds a custom dropdown input:
Ng-model directive interprated the name field.model as plain text, whereas I would like the attributs ng-model="field.model" would be replaced by the value contained into field.model. Curly brackets don't seems to work here. Any idea?
How to let the ng-model value accessible both in my form controller, and into my custom directive's controller?
Exemple of a field object:
{
label : "Name",
model : "employeeName",
type : "dropdown",
param : {
dropdownArray : result,
dropdownName : 'Nom',
dropdownFieldValue : 'nameUUID',
dropdownVisibleValue : [ 'employeeSS', 'employeeName' ]
}
}
Then in my controller I should be able to access this dropdown value with: $scope.employeeName.

It sounds like you're accessing field.model via the attributes input parameter supplied to the link function in your directive. Instead, you should be accessing it via the scope variable.
link: function(scope, element, attributes) {
// attributes.ngModel will yield 'field.model'
// scope.ngModel will contain the actual value of field.model
}
If you don't explicitly define an isolate scope for your directive, then ng-model assigned to field.model should be available in your directive via the scope variable as I mentioned above.
scope: false // This is the default
// Define an isolate scope with field.model available through scope.ngModel
scope: {
ngModel: '='
}
Hope that helps.

Related

angular directive isolated scope property updated in link function but not reflecting on directive view

I have a case where, I have a directive which get a value form Controller $scope.colorName. Directive bind it one-way and keep this value in isolated scope "colorVar". Template of directive render "colorVar" as {{ colorVar }} .
I need to change the value of "colorVar" in link function of directive. but its not reflecting on UI.
HTML:
<div ng-app="myApp" ng-controller="appCtrl">
{{colorName}}
<my-directive color='{{colorName}}'>
</my-directive>
</div>
JavaScript:
angular.module('myApp', []).controller('appCtrl',function($scope){
$scope.colorName='red';
})
.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
colorVar: '#color'
},
template: '<span> {{ colorVar }} </span><span>{{extra}}</span>',
link:function(scope,element,attrs){
scope.colorVar='orange';
scope.extra='kk';
}
};
});
In Link function I have updated scope.colorVar with 'orange' but is don't reflect on UI, but scope.exta do reflect on UI.
http://jsfiddle.net/ut7628d7/1/
Any idea what am I doing wrong. and why this is happening and how to achieve it ?
Thanks.
It depends on whether you want the directive's updated value to also change the original value in the controller or not:
Modify the original variable
If you want the directive to modify the original colorName in the controller, pass colorName by reference and use a two-way binding in the template:
<my-directive color='colorName'> <!-- instead of color='{{colorName}}' -->
and in the directive:
scope: {
colorVar: '=color' // instead of '#color'
},
http://jsfiddle.net/9szjpx9d/
The output from this will be "orange orange kk" because it's the same object throughout, so when the directive changes it to 'orange' it will affect both places.
Copy the variable
If you want the directive to only modify its own value, and output "red orange kk", then keep an attribute binding as you have now, but delay the directive link function by one tick using $timeout, so that the value it sets on scope will overwrite the value received via the directive attribute:
$timeout(function() {
scope.colorVar = 'orange';
scope.extra = 'kk';
});
Meanwhile the separate original color value will remain untouched in the controller, because it was passed to the directive as a string rather than as an object reference.
http://jsfiddle.net/mpb2cuaj/
If you want to be able to change the color form inside your directive then use a two way binding. e.g.
scope: {
colorVar: '=color'
}
HTML
<my-directive color='colorName'></my-directive>
http://jsfiddle.net/0he428hw/

Two way binding from the link function

Can someone tell me why I am not able to two way bind from the link function?
Please refer to this plunk: http://plnkr.co/edit/RI1ztP?p=preview
The below watch successfully adds the collection to attrs.ngModel but I dont see it reflecting in the parent controller
scope.$watchCollection("selectedItems",function(collection){
attrs.ngModel = [];
for(var i=0;i<collection.length;i++){
attrs.ngModel.push(collection[i]);
}
console.log("ngModel",attrs.ngModel);
});
Cant see the collection over here (selectedUsers):
<body ng-controller="mainCtrl">
<div multi-select-search-box ng-model="selectedUsers" label="name" my-options="state in states"></div>
{{selectedUsers}}
If you look at the above html, I am binding the selectedUsers array to ng-model. In my link function, i add the selected users to attrs.ngModel array. When I look at the console, the selectedUsers are added to attrs.ngModel but the array isn't reflected back on the html {{selectedUsers}}
The data bound to the ng-model of your multi-select-search-box is $scope.selectedUsers.
Therefore to register a change in the DOM you have to update that variable rather than ng-model.
scope.$watchCollection("selectedItems",function(collection){
for(var i=0;i<collection.length;i++){
scope.myNgModelVar.push(collection[i]);
}
});
Since ng-model is a string that gets $parse()/$eval() called on it to evaluate it as an expression, updating that ng-model value won't do you any good.
EDIT:
After some clarification it appears that this is a custom directive designed to be reusable. So therefore we do not want to stick variables from your controller inside the directive. Instead, you should bind a directive attribute to your directives scope.
// Directive Def. Object:
return {
restrict: "AE",
scope: {
myNgModelVar: "=",
bindModel: "=ngModel" //This is the alternate method aliasing ngModel var with a scope var.
},
template: "<input ng-model='myNgModelVar' />"
};
Although you could use ngModel by using an alias scope: {bindModel:'=ngModel'}, this gives you an isolated scope variable that you bind to ngModel instead. Therefore keeping your directive reusable.
The solution was to require the ng-model controller and sync changes using the viewValue array:
scope.$watchCollection("selectedItems",function(collection){
ctrl.$viewValue.splice(0,ctrl.$viewValue.length);
for(var i=0;i<collection.length;i++){
ctrl.$viewValue.push(collection[i]);
}
});
and
require: 'ngModel'

How to get the attribute value in template html with angularjs

I wrote a directive but I don't know how to get the attribute of the directive in template html, those attributes are used in ngIf to determine which div elements will be displayed, pls take a look at below code snippet.
directive use:
<geo-country-selector type="WGC"></geo-country-selector>
template html:
<div ng-if="type.indexOf('W')>0">
...
</div>
<div ng-if="type.indexOf('G')>0">
...
</div>
I need to get the attribute type's value in the template html. I did lots of research on that, but no luck. Any ideas? Thanks
The directive can be like Anik said :
link : function(scope,element,attr){
...
},
scope: {
type: '#'
}
In your directive add a link function then add attrs value to scope then you can check condition in your template like that
try like this
link : function(scope,element,attr){
scope.type=attr.type
}
There is another way by creating isolate scope like this
scope: {
type: '#'
}

How to access the attribute of angular directive in templateUrl html content

How do I access the attribute of angular directive in templateUrl html content?
angular.module('myApp')
.directive('questionImage', function () {
return {
restrict : 'E',
scope : false,
templateUrl : 'templates/questionImage.html',
link : function(scope, element, attrs) {
scope.type = attrs['type'];
}
}
});
template HTML:
<div>
<label>Image Links</label><br>
<button type="button" ng-click="newImageLink('{{type}}')">+</button>
<span ng-repeat="imageLink in questionFormData.imageUrls['{{type}}'] track by $index">
<input type="text" ng-model="questionFormData.imageUrls['{{type}}'][$index]">
<button type="button" ng-click="removeElement(questionFormData.imageUrls['{{type}}'], $index)">-</button>
</span>
</div>
Inside this HTML, I need to access the "type" attribute value specified when the directive is used in DOM such as below.
<question-image type="optionAImages"></question-image>
<question-image type="optionBImages"></question-image>
How do I get "type" attribute value inside the template html?
Update: Added the 'link' function in the directive definition. I was able to get the type but its value is always the last directive usage.
i.e I always see optionBImages value for type attribute. optionAImages value is not available.
the problem comes from the scope:false statement as as the second call of the question-image directive override the parameter changed by the first call of the same parameter
you need to change the scope:false to be scope:true
this will not create an isolated scope but it will give the directive a nested scope of the parent scope of the controller.
return {
restrict : 'E',
scope : {
type : '#'
},
templateUrl : 'templates/questionImage.html'
This is equivalent to
scope.type = attrs.type;

Function arguments in directive with templateUrl

I created a custom directive with an isolated scope that binds to a function from the enclosing controller and with references to a templateUrl. Here's what my code looks like:
the html
<div ng-controller='MyCtrl as my'>
<custom-directive data='my.data' on-search="my.find(param1, param2)"></custom-directive>
</div>
the directive
app.directive('customDirective', function() {
return {
restrict : 'E',
scope : {
data : '=data'
search : '&onSearch',
},
templateUrl : 'customDirective.html'
};
});
the template
<div>
<input ng-model='data.id'>
<a ng-click='find(param1, param2)'></a>
</div>
The arguments received by function find is also stored in data. The controller data binds to the directive but not the function. My log inside the function won't even show.
It seems there are different ways to do it as I have seen in many examples (see below) but none seems to work in my case.
Example 1: pass a mapping of parameter and values in the template
<div>
<input ng-model='data.id'>
<a ng-click='find.({param1: data.value1, param2: data.value2})'></a>
</div>
Example 2: put a link in the directive
app.directive('customDirective', function() {
return {
restrict : 'E',
scope : {
data : '=data'
search : '&onSearch',
},
templateUrl : 'customDirective.html',
link : function(scope, elem, attr) {
scope.retrieve({param1: scope.data.value1,
param2: scope.data.value2});
}
};
});
Example 3 : use scope.$apply(), $parse in link but haven't tried this
Could someone show me how to do it and also explain to me the link part (I don't understand that part) and if you're feeling generous, show the working alternatives as shown by the examples. Thanks
You don't have to passe params for your function just the reference so in your html
<custom-directive data='my.data' on-search="my.find"></custom-directive>
and your template directive directly call
<div>
<input ng-model='data.id'>
<a ng-click='find(data.value1, data.value2)'></a>
</div>
I also suggest you to use $scope and not the controller. So in your controller define
$scope.data = {
id: 1,
value1: "value1",
value2: "value2"
}
$scope.find = function (param1, param2) {
//Your logic
}
And in your template put directly
<custom-directive data='data' on-search="find"></custom-directive>
I hope this answer to your question
About link this text from angular js doc is pretty clear I think
Directives that want to modify the DOM typically use the link option.
link takes a function with the following signature, function
link(scope, element, attrs) { ... } where:
scope is an Angular scope object. element is the jqLite-wrapped
element that this directive matches.
attrs is a hash object with key-value pairs of normalized attribute names and their
corresponding attribute values.
In our link function, we want to update the
displayed time once a second, or whenever a user changes the time
formatting string that our directive binds to. We will use the
$interval service to call a handler on a regular basis. This is easier
than using $timeout but also works better with end-to-end testing,
where we want to ensure that all $timeouts have completed before
completing the test. We also want to remove the $interval if the
directive is deleted so we don't introduce a memory leak.

Resources