Problem:
I'm attempting to pass a value from an ng-repeat into a child-directive but when I try to access my passed variable in directive 2 I get "undefined".
Here's an illustration of what I am attempting. Basically directive 1 represents an array of widgets while directive 2 represents a single widget. I am attempting to pass an item from the ng-repeat loop into my child directive.
My Attempt:
Here's a simplified version of my directive 1 template:
<li ng-repeat="item in widgets">
<directive2 item="item"></directive2>
</li>
Here's a simplified version of directive 2:
angular.module('directive2').directive(
['$compile', '$rootScope',
function ($compile, $rootScope) {
return {
restrict: 'E',
scope: { item: '=' },
templateUrl: 'ext-modules/tile/widgetTemplate.html',
link: function (scope, element, attrs) {
console.log(scope.item); // undefined
}
};
}]);
The ng-repeat on widgets creates two items and I have verified that the data exists. The application works fine and doesn't throw an error but my console.log returns : undefined.
My Question:
How can I pass a value from a directive template's ng-repeat into a child-directive?
here's a fiddle: http://jsfiddle.net/3znEu/112/
Yet another solution proposal:
HTML:
<div ng-controller="MyCtrl">
<directive1></directive1>
</div>
JavaScript:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.widgets = [
'a', 'b', 'c', 'd'
];
}])
.directive('directive1', function () {
return {
restrict: 'E',
scope: false,
template:
'<li ng-repeat="item in widgets">' +
'<directive2 item="item"></directive2>' +
'</li>'
}
})
.directive('directive2', ['$compile', '$rootScope',
function ($compile, $rootScope) {
return {
restrict: 'E',
scope: { item: '=' },
template:
'<div>elem = {{item}}</div>',
link: function (scope, element, attrs) {
console.log(scope.item);
}
}
}]);
Fiddle: http://jsfiddle.net/masa671/dfn75sp3/
It works fine when you put directive2 as directive name, not module:
http://jsfiddle.net/3znEu/113/
'use strict';
var app = angular.module('myApp', [])
.controller('myController', ['$scope', function($scope) {
$scope.greeting = 'Hello World!';
$scope.widgets = ["111","222","333"]
}]);
app.directive('directive1',
['$compile', '$rootScope',
function ($compile, $rootScope) {
return {
restrict: 'E',
scope: { item: '=' },
template: '<div>{{item}}</div>',
link: function (scope, element, attrs) {
console.log(scope.item); // undefined
}
};
}]);
I have modified your fiddler a bit http://jsfiddle.net/3znEu/115/. Few changes
1. Added a restrict to your directive.
2. Added a template to render the Items (only for testing and demo)
3. Changed items in scope from '#' to '='
angular.module("myApp").directive("directive1", function(){
return {
restrict: 'E',
scope: {
item: "="
},
template: "{{item}}"
}
});
Related
I'm trying to update values in a ng-repeat on a ng-model;
I have the current directive:
app.directive('myDirective', function () {
return {
require: 'ngModel',
restrict: 'E',
template: '<div ng-repeat="e in model"><input ng-model="e"/></div>',
scope: {
ngModel: '='
},
link: function($scope, elem, attrs, ngModelCtrl) {
$scope.$watch(function (){
return ngModelCtrl.$modelValue;
}, function (v) {
$scope.model = ngModelCtrl.$viewValue;
});
}
};
});
but it isn't updating the value as illustrated here:
http://plnkr.co/edit/E89sbXY0gUw53EmJobz0?p=preview
anybody knows what might be wrong?
http://plnkr.co/edit/2JwxNzBRQa1dzACoJIpF?p=preview
Had to replace $scope.model = ngModelCtrl.$viewValue; with scope.model = ngModelCtrl.$viewValue; and it works fine.
app.directive('myDirective', function () {
return {
require: 'ngModel',
restrict: 'E',
template: '<div ng-repeat="e in model"><input ng-model="e"/></div>',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs, ngModelCtrl) {
console.debug()
scope.$watch(function (){
return ngModelCtrl.$modelValue;
}, function (v) {
scope.model = ngModelCtrl.$viewValue;
})
}
};
});
UPDATE: I converted 'stuff' to an array of objects and now it works:
http://plnkr.co/edit/2JwxNzBRQa1dzACoJIpF?p=preview
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.stuff = [{number: 1},{number: 2},{number: 3}];
});
app.directive('myDirective', function () {
return {
require: 'ngModel',
restrict: 'E',
template: '<div ng-repeat="e in model"><input ng-model="e.number"/></div>',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs, ngModelCtrl) {
console.debug()
scope.$watch(function (){
return ngModelCtrl.$modelValue;
}, function (v) {
scope.model = ngModelCtrl.$viewValue;
console.log(scope.model)
})
}
};
});
#Kiwi ng-repeat creates a child scope and ng-model will use the property on the child scope, because ng-model binding will evaluate on the current scope. This is the reason why the json presented in the view doesn't change in your example as it was bound to a property on the child scope created by the ng-repeat directive.
Check this simple jsfiddle example I hope it will be of help to you.
<div ng-app="demo">
<div ng-controller="DefaultController as ctrl">
{{ctrl.numbers | json}}
<numbers numbers="ctrl.numbers"></numbers>
</div>
</div>
angular
.module('demo', [])
.controller('DefaultController', DefaultController)
.controller('NumbersController', NumbersController)
.directive('numbers', numbers);
function DefaultController() {
var vm = this;
vm.numbers = [1, 2, 3];
}
function numbers()
{
var directive = {
restrict: 'E',
scope: {
numbers: '='
},
template: '<div ng-repeat="number in vm.numbers"><input type="number" ng-model="vm.numbers[$index]"/></div>',
bindToController: true,
controller: NumbersController,
controllerAs: 'vm'
};
return directive;
}
function NumbersController() {
var vm = this;
}
I'm trying to display the elements of an array using ng-repeat and a directive. The directive part is important to the solution. However the element of the array is not getting bound and displays an empty value.
The fiddle can be found at http://jsfiddle.net/qrdk9sp5/
HTML
<div ng-app="app" ng-controller="testCtrl">
{{chat.words}}
<test ng-repeat="word in chat.words"></test>
</div>
JS
var app = angular.module('app', []);
app.controller("testCtrl", function($scope) {
$scope.chat = {
words: [
'Anencephalous', 'Borborygm', 'Collywobbles'
]
};
});
app.directive('test', function() {
return {
restrict: 'EA',
scope: {
word: '='
},
template: "<li>{{word}}</li>",
replace: true,
link: function(scope, elm, attrs) {}
}
});
OUTPUT
["Anencephalous","Borborygm","Collywobbles"]
•
•
•
Expected output
["Anencephalous","Borborygm","Collywobbles"]
•Anencephalous
•Borborygm
•Collywobbles
Appreciate your help
You didn't bind word.
You have used isolate scope. If you don't bind with it's scope property,it won't work.
scope: {
word: '='
},
Try like this
<test word="word" ng-repeat="word in chat.words"></test>
DEMO
var app = angular.module('dr', []);
app.controller("testCtrl", function($scope) {
$scope.chat= {words: [
'Anencephalous', 'Borborygm', 'Collywobbles'
]};
});
app.directive('test', function() {
return {
restrict: 'EA',
scope: {
word: '='
},
priority: 1001,
template: "<li>{{word}}</li>",
replace: true,
link: function(scope, elm, attrs) {
}
}
});
Your directive needs to run before ng-repeat by using a higher priority, so when ng-repeat clones the element it is able to pick your modifications.
The section "Reasons behind the compile/link separation" from the Directives user guide have an explanation on how ng-repeat works.
The current ng-repeat priority is 1000, so anything higher than this should do it.
myPage.html
<div ng-controller="MyPageCtrl">
<my-custom-directive arg1="{{currentObj.name}}"></my-custom-directive>
<div>
in myPageCtrl.js (Controller)
app.controller("MyPageCtrl", ["$scope", function ($scope) {
$scope.currentObj = {"name":"Collin"};
}]);
And this is how my directive code looks like
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl"
};
}]);
Finally here's my directive's controller,
app.controller("MyCustomDirCtrl", ["$attrs", function ($attrs) {
var arg = $attrs.arg1;
alert('Arg '+arg);
}]);
The alert just displays "{{currentObj.name}}" and not the value of the name property of currentObj.
Please can you suggest me ways to figure this out.
Thanks.
Not sure why did you use $attrs for a controller. Just use a normal $scope.
myPage.html
<div ng-controller="MyPageCtrl">
<my-custom-directive arg1="{{currentObj.name}}"></my-custom-directive>
<div>
myPageCtrl.js (Controller)
app.controller("MyPageCtrl", ["$scope", function ($scope) {
$scope.currentObj = {"name":"Collin"};
}]);
myCustomDirective
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl"
};
}]);
Directive's controller (change $attrs to $scope here),
app.controller("MyCustomDirCtrl", ["$scope", function ($scope) {
var arg = $scope.arg1;
alert('Arg '+arg);
}]);
Instead of accessing the attr from your controller, you could access it from your directive using the link function.
app.directive("myCustomDirective", [function () {
return {
restrict: "E",
controller: "MyCustomDirCtrl",
link: function(scope, element, attr) {
alert(attr.arg1);
}
};
}]);
I try to pass an attribute directive to an element directive, is it possible? I tried do it as in example but it doesn't work.
for example I have element directive:
<np-form-input
np-form-input-attrs="np-my-attr-directive"
>
</np-form-input>
JS:
.directive('npFormInput', [function () {
return{
restrict: 'E',
templateUrl: '/resources/view/common/form_input',
link: function(scope, element, attr){
scope.attributes= attr.npFormInputAttrs;
}
};
}])
And then in directive HTML
<input
{{attributes}}
>
Thanks in advance.
EDIT: My solution based on Micah Williamson answer:
.run(['$templateCache', '$http', function($templateCache, $http){
$http.get('/resources/view/common/form_input').success(function(data){
$templateCache.put('/resources/view/common/form_input', data);
});
}])
.directive('npFormInput', ['$templateCache', '$compile', function ($templateCache, $compile) {
return{
restrict: 'E',
compile: function (ele, attrs) {
var tpl = $templateCache.get('/resources/view/common/form_input');
tpl = tpl.replace('{{attributes}}', attrs.npFormInputAttrs);
var tplEle = angular.element(tpl);
ele.replaceWith(tplEle);
return function (scope, element, attr) {
$compile(tplEle)(scope);
};
},
};
}])
I've done something similar to what you're trying to do but I had to inject the attributes in the compile. You would need to add the template to $templateCache first though.
.directive('npFormInput', [function ($templateCache, $compile) {
return{
restrict: 'E',
compile: function(ele, attrs) {
var tpl = $templateCache.$get('/resources/view/common/form_input');
tpl = tpl.replace('{{attributes}}', attrs.npFormInputAttrs);
var tplEle = angular.element(tpl);
ele.replaceWith(tplEle);
return function(scope, element, attr){
$compile(tplEle)($scope);
};
}
};
}])
I have this code, written with Angular 1.2: http://jsfiddle.net/VmkQy/1/
<div ng-app="app">
Title is: <span my-directive data-title="Test title">{{ title }}</span>
</div>
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: {title:'#'},
link: function($scope) {
alert($scope.title);
}
}
}])
;
Scope has a title property, but it does not rendered. Why?
If I change directive config to scope:true, it will works fine: http://jsfiddle.net/VmkQy/2/
angular.module('app', [])
.directive('myDirective', [function() {
return {
restrict: 'A',
scope: true,
link: function($scope, $element, attrs) {
$scope.title = attrs.title;
alert($scope.title);
}
}
}])
;
This is a bug or feature in Angular 1.2? Older version works fine in all this cases: http://jsfiddle.net/VmkQy/3/
The {{title}} inside of your <span /> gets replaced. Add template: "{{title}}" to your directive and it works:
http://jsfiddle.net/VmkQy/5/