I want to pass object array to directive and have it print out the fields which I determine at the place where I use that directive.
Here's the example:
//directive
app.directive('MyDirective', function() {
return {
restrict: 'A',
templateUrl: 'my-directive.html',
scope: {
items: '#',
field: '#'
}
};
});
// my-directive.html template
<div ng-repeat="item in items">{{ item.field }}</div>
The idea is that I could use it with any object like this:
// object arrays
var phones = [{id:1,number:'555-5555'}, {id:2,number:'555-6666'}];
var persons = [{id:1,name:'John'}, {id:2,name:'Jane'}];
// directive usage
<div my-directive items="phones" data-field="???number???"></div>
<div my-directive items="persons" data-field="???name???"></div>
The result should print out numbers and names. Is that even doable in Javascript?
You can, just bind the items with '=':
.directive('myDirective', function() {
return {
restrict: 'A',
template: '<div ng-repeat="item in items">{{ item[field] }}</div>',
scope: {
items: '=',
field: '#'
}
};
})
Then use it like this:
<div my-directive items="phones" field="number"></div>
See this plunker.
Yes, it's possible, you can do it like this:
Directive:
myApp.directive('myDirective', function() {
return {
restrict: 'A',
template: '<div ng-repeat="item in items">{{ getItemField(item) }}</div>',
scope: {
items: '=',
field: '#'
},
link: function(scope, element, attr) {
scope.getItemField = function (item) {
return item[scope.field];
};
}
};
HTML:
<div my-directive items="phones" data-field="number"></div>
<div my-directive items="persons" data-field="name"></div>
Fiddle
This doesn't take a directive, probably the directive you are looking for is ng-repeat:
var phones = [{id:1,number:'555-5555'}, {id:2,number:'555-6666'}];
var persons = [{id:1,name:'John'}, {id:2,name:'Jane'}];
<li ng-repeat="phone in phones">{{phone.number}}</li>
<li ng-repeat="person in persons">{{person.name}}</li>
Related
I am trying to figure out how to pass a transclusion down through nested directives and bind to data in the inner-most directive. Think of it like a list type control where you bind it to a list of data and the transclusion is the template you want to use to display the data. Here's a basic example bound to just a single value (here's a plunk for it).
html
<body ng-app="myApp" ng-controller="AppCtrl as app">
<outer model="app.data"><div>{{ source.name }}</div></outer>
</body>
javascript
angular.module('myApp', [])
.controller('AppCtrl', [function() {
var ctrl = this;
ctrl.data = { name: "Han Solo" };
ctrl.welcomeMessage = 'Welcome to Angular';
}])
.directive('outer', function(){
return {
restrict: 'E',
transclude: true,
scope: {
model: '='
},
template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'
};
})
.directive('inner', function(){
return {
restrict: 'E',
transclude: true,
scope: {
source: '=myData'
},
template :'<div class="inner" my-transclude></div>'
};
})
.directive('myTransclude', function() {
return {
restrict: 'A',
transclude: 'element',
link: function(scope, element, attrs, controller, transclude) {
transclude(scope, function(clone) {
element.after(clone);
})
}
}
});
As you can see, the transcluded bit doesn't appear. Any thoughts?
In this case you don't have to use a custom transclude directive or any trick. The problem I found with your code is that transclude is being compiled to the parent scope by default. So, you can fix that by implementing the compile phase of your directive (this happens before the link phase). The implementation would look like the code below:
app.directive('inner', function () {
return {
restrict: 'E',
transclude: true,
scope: {
source: '=myData'
},
template: '<div class="inner" ng-transclude></div>',
compile: function (tElem, tAttrs, transclude) {
return function (scope, elem, attrs) { // link
transclude(scope, function (clone) {
elem.children('.inner').append(clone);
});
};
}
};
});
By doing this, you are forcing your directive to transclude for its isolated scope.
Thanks to Zach's answer, I found a different way to solve my issue. I've now put the template in a separate file and passed it's url down through the scopes and then inserting it with ng-include. Here's a Plunk of the solution.
html:
<body ng-app="myApp" ng-controller="AppCtrl as app">
<outer model="app.data" row-template-url="template.html"></outer>
</body>
template:
<div>{{ source.name }}</div>
javascript:
angular.module('myApp', [])
.controller('AppCtrl', [function() {
var ctrl = this;
ctrl.data = { name: "Han Solo" };
}])
.directive('outer', function(){
return {
restrict: 'E',
scope: {
model: '=',
rowTemplateUrl: '#'
},
template: '<div class="outer"><inner my-data="model" row-template-url="{{ rowTemplateUrl }}"></inner></div>'
};
})
.directive('inner', function(){
return {
restrict: 'E',
scope: {
source: '=myData',
rowTemplateUrl: '#'
},
template :'<div class="inner" ng-include="rowTemplateUrl"></div>'
};
});
You can pass your transclude all the way down to the third directive, but the problem I see is with the scope override. You want the {{ source.name }} to come from the inner directive, but by the time it compiles this in the first directive:
template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'
the {{ source.name }} has already been compiled using the outer's scope. The only way I can see this working the way you want is to manually do it with $compile... but maybe someone smarter than me can think of another way.
Demo Plunker
I trying to make a directive which accepting an attribute and hook it to the isolated scope, but the attribute value is not showing.
angular.module('app', [])
.controller('torrentController', [function() {
this.recommended = ['...'],
this.otherArray = ['...']
}])
.directive('torrentsTable', [function() {
return {
restrict: 'E',
templateUrl: 'templates/directives/torrentsTable.html',
scope: {
index: '='
},
controller: 'torrentController as torrentCtrl'
};
}]);
The idea is to use this directive to show different list of torrents with this syntax:
<torrents-table index="recommended"></torrents-table>
<torrents-table index="someOtherIndex"></torrents-table>
I wish this 2 almost same lines to show different "list" with results.
templates/directives/torrentsTable.html
<!-- I also tried with ng-repeat="torrent in torrentCtrl.recommended" -->
<!-- And is working as I excepted (It's shows the recommended array) -->
<div layout="row" ng-repeat="torrent in torrentCtrl[index]">
<div flex>Name: {{torrent.name}}</div>
<div flex>{{index}}</div>
</div>
{{index}} is not showing, and it's value is not showing.
While I actually make hardcoded ng-repeat arguments - it repeating but {{index}} is empty.
What I am doing wrong?
Your problem: how you pass key.
You use in directive:
scope: {
index: '='
},
so you should pass to directive expression, that evaluated to $scope property. So if you not inject scope - you pass undefined.
You can fix this two ways:
1) pass string instead something else
<torrents-table index="'recommended'"></torrents-table>
<torrents-table index="'someOtherIndex'"></torrents-table>
2) change directive definition to
scope: {
index: '#'
},
sample you can see in snippet below.
angular.module('app', [])
.controller('torrentController', [function() {
this.recommended = [1,2,3,4,5];
this.someOtherIndex = ['a','b','c','d','e'];
}])
.directive('torrentsTable', [function() {
return {
restrict: 'E',
template: '<div flex>{{index}}</div>'+
'<div layout="row" ng-repeat="torrent in torrentCtrl[index]">'+
' <div flex>Name: {{torrent}}</div>'+
'</div>',
scope: {
index: '='
},
controller: 'torrentController as torrentCtrl'
};
}])
.directive('torrentsTable2', [function() {
return {
restrict: 'E',
template: '<div flex>{{index}}</div>'+
'<div layout="row" ng-repeat="torrent in torrentCtrl[index]">'+
' <div flex>Name: {{torrent}}</div>'+
'</div>',
scope: {
index: '#'
},
controller: 'torrentController as torrentCtrl'
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<div ng-app='app'>
<torrents-table index="'recommended'"></torrents-table>
<torrents-table index="'someOtherIndex'"></torrents-table>
<hr/>
<torrents-table2 index="recommended"></torrents-table2>
<torrents-table2 index="someOtherIndex"></torrents-table2>
</div>
I have a list and want it to filter by my custom filter. But the value to it I want to put from custom directive with it's own scope. How to do it?
Body:
<body ng-controller="test">
<tr ng-repeat="item in list | myfilter: HowToPuttHereValue? >
Here is my custom filter:
.filter('myfilter', function(){
return function(array, num){
return array.slice(num, num+1);
}
})
And here is my custom directive:
.directive('mydirective', function() {
return {
restrict: "E",
template:"<input ng-model='counter'><button ng-click='getIt(counter)'>PRESS</button>",
scope:{
item: '='
},
link: function(scope, element, attr){
scope.getIt = function(counter){
console.log(counter);
}
}
}
})
Please see the Example:
JsFiddle Example
P.S. I guess I've already found a solution using "scope.$parent" . But is there a possibility to pass the value straight to a "myfilter: here? "
Here it is.
<div ng-app="hello">
<div ng-controller="forExampleController">
<ul>
<li ng-repeat="num in list | myfilter: howToPutHereFromDirective ">{{num}} </li>
</ul>
<mydirective item="list.length" filter-value="howToPutHereFromDirective"></mydirective>
</div>
</div>
function forExampleController($scope){
$scope.list = [1,2,3,4,5,6,7,8,9];
$scope.howToPutHereFromDirective = 3;
}
angular.module('hello', [])
.filter('myfilter', function(){
return function(array, num){
return array.slice(num, num+1);
}
})
.directive('mydirective', function() {
return {
restrict: "E",
template:"<input ng-model='counter'><button ng-click='getIt()'>PRESS</button>",
scope:{
item: '=',
filterValue: '='
},
link: function(scope, element, attr){
scope.getIt = function(){
scope.filterValue = parseInt(scope.counter);
}
}
}
});
Doing scope.$parent within isolated scope isn't too good indeed, because the objective of isolated scope is exactly the opposite.
I'm not sure what purpose item="list.length" two-way binding has to serve, but it is a bad idea.
I have a TimelineController that has a publish function on the scope, that will send some data to the server.
My timeline is composed by 2 directives
timeline (Element)
share-post (Element)
I would like to be able to call from the share-post directive the publish function on my TimelineController is that possible?.
Controller
function TimelineController($scope,TimelineService)
{
$scope.posts = [];
$scope.publish = function(wall_type,wall_id,text,video,image) {
console.log('click controller');
TimelineService.publishPost(wall_type,wall_id,text,video,image).$promise.then(function(result){
$scope.posts.push(result.response);
});
};
}
Timeline Directive:
function timelineDirective() {
return {
restrict: 'E',
replace: true,
scope: {
type: '#',
ids: '#',
postime: '&',
posts: '='
},
templateUrl:"/js/templates/timeline/post-tmpl.html",
controller: function($scope,$element) {
this.type = $element.attr('type');
this.ids = $element.attr('ids');
}
}
};
Timeline Directive Template
<ol class="timeline" ng-init="postime({wall_type:type,wall_id:ids})">
<li>
<share-post></share-post>
</li>
<li ng-repeat="post in posts">
{{post.text}}
</li>
</ol>
SharePost Directive: From this directive I would like call the publish on the TimelineController
function sharePost() {
return {
restrict: 'E',
replace: true,
require: "^timeline",
templateUrl:"/js/templates/timeline/share-tmpl.html",
link: function($scope,$element,$attr,ctrl) {
$scope.pub = function() {
// This does not work because it call the parent directive
// Instead of controller
$scope.publish(ctrl.type, ctrl.ids, $scope.text);
}
}
}
};
Sharepost Directive Template
<div class="module comment">
<div class="content">
<textarea class="form-control" ng-model="text" placeholder="What is going on..." rows="2"></textarea>
</div>
<button type="button" class="btn" ng-click="pub()"> Share</button>
</div>
well you use your directive just to bind the event click from the controller, something like:
angular.module('module').directive('sharePost', [
function(){
return {
link: function (scope, element, attr) {
var clickAction = attr.clickAction;
element.bind('click',function (event) {
scope.$eval(clickAction);
});
}
};
}]);
html
<a sharePost click-action="publish(wall_type,wall_id,text,video,image)"> publish</a>
Change your directive to have an extra item in the scope like this onPublish: "#" then in your html you can pass a pointer to the controller function you want to invoke like this:
<share-post on-publish="publish"></share-post>
to call this from the directive you have to do:
$scope.onPublish()(ctrl.type, ctrl.ids, $scope.text)
I have a directive that transcludes the original content, parses it, and uses the information in the original content to help build the new content. The gist of it looks like this:
.directive('list', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: '...',
scope: true,
controller: function($scope, $element, $attrs, $transclude) {
var items;
$transclude(function(clone) {
clone = Array.prototype.slice.call(clone);
items = clone
.filter(function(node) {
return node.nodeType === 1;
})
.map(function(node) {
return {
value: node.getAttribute('value')
text: node.innerHTML
};
});
});
// Do some stuff down here with the item information
}
}
});
Then, I use it like this:
<list>
<item value="foo">bar</item>
<item value="baz">qux</item>
</list>
This all works fine like this. The problem occurs when I try to use an ng-repeat inside the directive content, like this:
<list>
<item ng-repeat="item in items" value="{{ item.value }}">{{ item.text }}</item>
</list>
When I try to do this, there are no items. Anyone know why this wouldn't work, or if there is a better way of accomplishing the same kind of thing?
You may try:
transcludeFn(scope, function (clone) {
iElem.append(clone);
})
For bit more details:
HTML:
<foo data-lists='[lists data here]'>
<li ng-repeat="list in lists">{{list.name}}</li>
</foo>
Directive:
var Foo = function() {
return {
restrict: 'E',
template: '...'
transclude: true,
scope: { lists: '=?' }
link: function(scope, iElem, iAttrs, Ctrl, transcludeFn) {
transcludeFn(scope, function (clone) {
iElem.append(clone);
}
}
};
};
.directive('foo', Foo);
You should let transcludFn know which scope you are going to use within transcludeFn. And if you don't want to use isolate scope, you may also try transcludeFn(scope.$parent....)