I am attempting to pass down attributes returned from a MongoDB query to a directive in Angular, but for some reason it will not pass down an "_id" attribute. When I view the records returned in the parent's $scope, I can see that each object does in fact have an "_id". However when I go to inspect the $scope of the directive I am attempting to render, I can see that it has every attribute besides "_id".
My parent template code:
<div id=cards>
<div ng-repeat='card in cards' class='card'>
<card-directive
_id={{card._id}} attack={{card.attack}} cost={{card.cost}}
health={{card.health}} img={{card.img}} name={{card.name}}
hero={{hero}} rarity={{card.rarity}} type={{card.type}}
class={{hero}} add={{add}} faction={{card.faction}} >
</card-directive>
</div>
</div>
My directive code:
function cardDirective() {
return {
restrict: 'E',
scope: {
'_id': '#',
'img': '#',
'attack': '#',
'cost': '#',
'health': '#',
'name': '#',
'rarity': '#',
'type': '#',
'hero': '#',
'add': '#',
'faction': '#'
},
Is there something special about including an attribute that begins with an underscore?
It should have worked, only wrap your attribute value with "(quotes)
Rather than passing each value inside attribute, you could consider passing whole card object to directive.
<div id=cards>
<div ng-repeat='card in cards' class='card'>
<card-directive my-card="card"></card-directive>
</div>
</div>
Directive
function cardDirective() {
return {
restrict: 'E',
scope: {
'myCard': '<'
},
So inside directive you could easily access value inside card object and < ensure one way binding. You could use {{myCard._id}} for using _id.
AngularJS is normalizing the attribute _id to Id with a capital I.
For more information, see AngularJS Developer Guide - Directive Normalization.
angular.module("app",[])
.directive("customDirective", function() {
return {
template: `Id={{Id}}`,
link: function(scope, elem, attrs) {
scope.Id = attrs.Id;
console.log("Id=",attrs.Id);
}
}
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<div custom-directive _id=99></div>
</body>
Related
I want to make a directive which take a class name conditionally. However, I found that my code can work only if I hardcode the class name into the class attribute. If I try to use it with any expression, its failed to work.
For e.g.
// HTML
// Doesn't work (cannot find class="editable" in the final output template)
<tit-txt ng-class="true ? 'editable' : ''" ng-model="mdEnt.phone"></tit-txt>
// Works! (I can find class="editable" in the final output template)
<tit-txt class="editable" ng-model="mdEnt.phone"></tit-txt>
//JS
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
ngModel: '=',
},
link: function (scope, element, attrs) {
scope.editable = element.hasClass('editable') ? 'editable' : '';
},
template: '<input ng-class="editable" ng-model="ngModel" />',
};
})
Anyone can explain to me that why is it happening? How can I use it with expression?
UPDATE 1
// HTML
// Doesn't work
<tit-txt ng-class="{'editable': true}" ng-model="mdEnt.phone"></tit-txt>
//JS
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
title: '#',
fieldName: '#',
ngModel: '=',
},
link: function (scope, element, attrs) {
console.log(element.hasClass('editable'));
scope.editable = element.hasClass('editable') ? 'editable' : '';
},
template: '<div><span>{{title}}: </span><input id="{{fieldName}}" ng-class="{editable: true}" name="{{fieldName}}" ng-model="ngModel" /></div>',
};
})
Anyone can explain to me that why I get false in the console.log(element.hasClass('editable'));?
With the class attribute, the element's class is set by JavaScript. With the ng-class directive, the class is set by the AngularJS framework. When there are more that one directive on an element, there is no guarantee of the order of execution of the code of the respective directives.
Avoid having AngularJS manipulate the DOM and having subsequent AngularJS manipulate the model based on the state of the DOM. With MVC frameworks the Model should be the single source of truth and the DOM should be directly determined by the Model.
<tit-txt inner-class="true ? 'editable' : ''" my-model="mdEnt.phone">
</tit-txt>
app.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
title: '#',
fieldName: '#',
innerClass: '<',
myModel: '=',
},
link: function (scope, element, attrs) {
scope.$watch(attrs.innerClass, function(newValue) {
console.log("inner-class=", newValue);
});
},
template: `<div><span>{{title}}: </span>
<input id="{{fieldName}}" ng-class="innerClass"
name="{{fieldName}}" ng-model="myModel" />
</div>`,
};
})
Notice how the directive uses one-way, '<', binding to compute the value of the inner-class attribute from an AngularJS Expression.
Also notice that I changed ng-model to my-model. The ng- prefix is reserved for core AngularJS directives. Use of ng-model should be specifically be avoided unless the custom directive properly integrates with the ngModelController.
I'm creating a directive to show differences in text. For this directive, I need a couple of buttons which I've split up in directives. A simpliefied example would be:
.directive('differenceViewer', function($templateCache, $compile) {
return {
restrict: 'E',
scope: {
oldtext: '#',
newtext: '#',
template: '#',
heading: '#',
options: '=',
itemdata: '&',
method: '&'
},
replace: false,
link: (scope, element, attr) => {
element.append(angular.element($compile($templateCache.get(scope.template))(scope)));
}
};
}).directive('diffbutton', function() {
return {
restrict: 'E',
scope: {
method: '&'
},
template: '<button class="btn btn-sm btn-success" ng-click="method()">Rollback</button>',
replace: true,
terminal: true,
link: (scope, element, attr) => {
scope.directiveClick = function(){
console.log("directive method"); // is never called
}
}
}
})
I compile the html via a template script:
<script type="text/ng-template" id="differenceViewer.html">
<div class="ibox-footer">
<div class="row">
<div class="col-md-12">
<diffbutton method="clickedBtn()">Foo</diffbutton>
</div>
</div>
</div>
</script>
Where the diffbutton is created inside the html compiled by differenceViewer.
I need to call a method in the controller that is responsible for creating all the difference-views.
app.controller('MainCtrl', function($scope) {
$scope.clickedBtn = function() {
console.log('foo'); // is never called
}
})
Here's a plunker demonstrating the issue.
What do I need to change in order to be able to forward the button click on my directive in a directive to the controller method?
I've been working with the answers of this question but still can't make it work.
Note that, if I add
scope.clickedBtn = function() {console.log("bar");}
to the differenceViewer directive, it gets called - however I need to call the method in the controller instead.
Pass on a method from the parent to the child element and then trigger it on click. For example (pseudo code coming in )
<parent-directive>
<child-directive totrigger="parentClickCallback()"></child-directive>
</parent-directive>
then in your parent directive you set
$scope.parentClickCallback = function(){//do whatever you neeed}
and on your child in its scope binding you set
scope:{
totrigger:'&'
}
and on that child button you simply add
<button ng-click="totrigger()">ClickMe</button>
every time you click that button it will trigger parentClickCallback by reference attached to totrigger.
Why would you need to complicate your code so much I'm not really sure. If you not happy with scope binding just require controller in your directive and pass the controller bound function.
I'm trying to build a feed of posts, where the post object specifies a type value, which then specifies a template for the post. I got a custom directive built but I'm having issues.
JS
myApp.controller('FeedCtrl', function ($scope, $http) {
$scope.posts = [
{'title': 'Post 1',
'type': 'post',
'snippet': 'Vim id noster platonem definitionem...',
}
];
});
myApp.directive('myPost', function () {
return {
restrict: 'E',
scope: {
title: '=',
type: '=',
snippet: '=',
},
templateUrl: function(elem, attr){
return './templates/'+attr.type+'.html'
}
};
});
HTML
<div ng-controller="FeedCtrl">
<div ng-repeat="post in posts">
<my-post title='post.title' type='post.type' snippet='post.snippet'></my-post>
</div>
</div>
doesn't work... but when I change the type attr to
<my-post type="post"></my-post>
it works and passes all of the scope data into the post.html template. What's the difference? Why doesn't it pass 'post.type' over to the directive, but it passes everything else just find?
Try this way:
<my-post title='post.title' type='{{post.type}}' snippet='post.snippet'></my-post>
And change this:
scope: {
title: '=',
type: '#',
snippet: '=',
}
The template is requested before the scope is initialized. You could remove the type from the isolate scope and change the HTML to:
<my-post title='post.title' type='{{post.type}}' snippet='post.snippet'></my-post>
as #karaxuna suggested.
I was thinking to pass data from my controller to a directive template. How can I do this?
Here is my controller:
$scope.$on('itemSelectedEvent', function (event, args) {
$scope.displayOrgs.push(data.orgName);
})
And the directive:
directive('search', function ($timeout) {
return {
restrict: 'AEC',
scope: {
selecteditemslist: "=",
searchid: "#",
selecteditemsdisplay: "=",
searchobj: '#',
displayOrgs: '=displayOrgs'
},
link: function (scope, elem, attrs, index, searchobj,searchid) {
scope.$emit('itemSelectedEvent', {
selectedId:scope.selectedId,
sortOrder:index,
searchId : searchid
}
);
}
});
This displayOrgs should pass to directive. How is it I am getting undefined?
<li class="tag" ng-repeat="list in displayOrgs track by $index" ng-class="{selected: $index==selectedIndex}" >
<span class="tag-label">{{list}}</span>
<span class="tag-cross pointer" ng-click="Delete($index,selecteditemslist[$index],list,searchobj)">x</span>
</li>
Please help me. Thanks in advance.
There is no need to generate an event scope.$emit to notify controller that variable has got changed. While you have control over the variables with two binding inside its isolate scope: { .. }
displayOrgs: '=displayOrgs' has = means two way binding with variable associated with directive
element attribute div.
As you are using displayOrgs: '=displayOrgs' (in isolated scope) in your directive, the changes in any of the variable of the which is mentioned in isolated scope, that will change automatically in applies those changes to scope variable which is associated with display-orgs attribute.
link: function (scope, elem, attrs) {
scope.displayOrgs.push(data.orgName); // this will update binding with parent scope.
});
Given the following directive:
angular.module('news.directives', [])
.directive('newsArticle', function($location, $timeout) {
return {
restrict: 'AE',
replace: 'true',
templateUrl: 'partials/pages/news/directives/article.html',
scope: true
};
});
And the following template:
<div id="story-{{item.id}}" ng-class="{'red': item.active, 'story-container': true}">
<div class="story-banner-image"></div>
<div class="story stationary">{{ item.title | words: 10 }}</div>
<div class="story-banner-content"></div>
</div>
And the following call to the directive:
<news-article ng-repeat="item in news">
</news-article>
This works. But if I want to use an isolated scope and expose a single item:
scope: {
item: '#'
}
// or
scope: {
news: '#'
}
// or
scope: {}
Then it doesn't. All of the {{item.property}} tags specified in the template return a null value (empty string). Why doesn't item exist in the isolated scope?
It's quite clearly inheriting it's parent properties when scope is set to true, but it's not inheriting when I tell it what it should inherit.
You problem is that you are confused about the way scope configuration is set. In order to setup two-way data binding with isolated scope you should provide corresponding attribute in HTML:
<news-article ng-repeat="item in news" item="item"></news-article>
and then setup directive accordingly:
scope: {
item: '='
}
Demo: http://plnkr.co/edit/b1I8PIc27MvjVeQaCDON?p=preview