How to get values resolved in template without double curly brackets - angularjs

I am using directive and template. My template is as follows
<div>
<a data-my-dir="item" data-attr1="true"
data-attr2="{{itemParentId}}"
data-attr3="{{item.id}}">
</a>
</div>
Here due to curly braces watches are added and it is affecting my performance. I don't want watches as I am not going to modify attr2 or attr3.
I want to directly resolved value here only.
We can use bindonce directive where we don't want watches where we can use bo-text="xyz" instead, but here I want to pass values as attr to my custom directive.
Inside my directive's link function I am binding click event with element as follows
link: function (scope, element, attrs) {
element.bind('click', function (event) {
var myAttr1 = attrs.attr1;
var myAttr2 = attrs.attr2;
}
}
So due to those watches in template on attr1 and attr2 I am getting these values resolved in click event.
What are the alternatives?

One time Binding
Seems like a good use case for one time binding (if you are using angular 1.3+)
<div>
<a data-my-dir="item"
data-attr1="true"
data-attr2="{{::itemParentId}}"
data-attr3="{{::item.id}}">
</a>
</div>
the directive would look like
angular.module('app', [])
.directive("myDir", function() {
return {
restrict: "A",
scope: {
"attr1": "#",
"attr2": "#",
"attr3": "#",
},
template: '<span> {{attr1}} {{attr2}} {{attr3}}</span>'
};
})
Demo
http://plnkr.co/edit/GJCZmb9CTknZZbcRHN7s?p=preview

You could use data-attr2="itemParentId" directly but for that you need to use = as currently you are using # option of directive.
app.directive('myDir', function(){
return {
scope: {
dataAttr1: '=', //or '=dataAttr1'
dataAttr2: '=' //or '=dataAttr2'
},
link: function(scope, element, attrs){
console.log(scope.dataAttr1);
console.log(scope.dataAttr2);
}
}
})

Related

pass data from controller to directive's link?

In my controller :
myApp.controller('homeCtrl', function($scope, $rootScope, $state, 'red';
$rootScope.$on('new_story', function(event, data) {
$scope.cardObj = {key:'value'};
});
});
In my HTML :
<div clickmeee ></div>
<div id="feedContainer" card='{{cardObj}}'> </div>
In my directive :
myApp.directive('clickmeee', function($compile, $rootScope) {
return {
restrict: 'A',
scope: {
card: '#'
},
link: function(scope, element, attrs) {
element.bind('click', function() {   
scope.$watch('card', function(newVal, oldVal) {
alert(scope.card);
});       
});
}
};
});
How do I pass data from controller to this directive. I compile some html and prepend it to the div. All of that is sorted out but I need some data from object I am trying to pass.
Any help??
There are several problems in your code:
you define a scope attribute named 'card', but you use cardObj instead
you use a watch that is completely unnecessary. And worse: you create a new watch every time the element is clicked
you don't define any card attribute on your clickmeee element. Instead, you're placing it on another element, on which the directive is not applied
you're passing the attribute with '#'. That works, but the directive will receive a string, containing the JSONified object, rather than the object itself
you're not showming us where you emit an event that will initialize cardObj in the controller scope
Here is a plunkr showing a working version of your code.
Also, note that using bind('click') is a bad idea. You'd better have a template in your directive and use ng-click in the template, or simply not use a directive at all and just use ng-click directly on the div element.
Bad news. You are doing it wrong all the ways.
Firstly
card='{{cardObj}}' >
this one should be put in the
<div clickmeee ></div>
So you can take it as binded scope variable in your directive registration
Secondly
If you managed to use '#' syntax
card: '#'
it will turn your input to string, not a binded scope. Use '=' instead.
In the end
You dont need to use watch here:
scope.$watch('card', function(newVal, oldVal) {
alert(newVal);
});
since scope.card is binded via '=' connector. Just simple use alert(scope.card). (Need to warn you that alert an object is not a good idea)
I have tried your code here: plunker. Changed a litte bit by using cardObj as string for easier presentation. Does it match your work?
You should watch the card object:
myApp.directive('clickmeee', function() {
return {
restrict: 'A',
scope: {
card: '#'
},
link: function(scope, element, attrs) {
scope.$watch('card', function(value) {
console.log(value);
});
}
};
});
And:
<div clickmeee id="feedContainer" card='{{cardObj}}'> </div>
Whenever the controller changes the cardObj, the directive's watch on card is triggered:
$scope.$apply(function() {
$scope.cardObj = "test";
}

Adding directives to an element using another directive

I am trying to create a directive that adds some html code but also adds additional attributes/directives.
Using the code below, an ng-class attribute is indeed added, but it seems angular does not recognize it as a directive anymore. It is there, but it has no effect.
How can I get it to work? Thanks.
The Angular code:
angular.module('myModule', [])
.directive('menuItem', function () {
return {
restrict: 'A',
template: '<div ng-if="!menuItem.isSimple" some-other-stuff>{{menuItem.name}}</div>'
+'<a ng-if="menuItem.isSimple" ng-href="{{menuItem.link}}">{{menuItem.name}}</a>',
scope: {
menuItem: '='
},
compile: function (element, attrs) {
element.attr('ng-class', '{active : menuItem.isActivated()}');
}
}
});
And the html:
<li menu-item="menuItem" ng-repeat="menuItem in getMenuItems()" />
EDIT:
The solution by #Abdul23 solves the problem, but another problem arises: when the template contains other directives (like ng-if) these are not executed. It seems the problem just moved.
Is it possible to also make the directives inside the template work?
Or perhaps insert the html using the compile function instead of the template parameter. Since I want a simple distinction based on some value menuItem.isSimple (and this value will not change), perhaps I can insert only the html specific to that case without using ng-if, but how?
You need to use $compile service to achieve this. See this answer.
For your case it should go like this.
angular.module('myModule', [])
.directive('menuItem', function ($compile) {
return {
restrict: 'A',
template: '<a ng-href="{{menuItem.link}}">{{menuItem.name}}</a>',
scope: {
menuItem: '='
},
compile: function (element, attrs) {
element.removeAttr('menu-item');
element.attr('ng-class', '{active : menuItem.isActivated()}');
var fn = $compile(element);
return function(scope){
fn(scope);
};
}
}
});

angularjs directive - get element bound text content

How do you get the value of the binding based on an angular js directive restrict: 'A'?
<span directiverestrict> {{binding}} </span>
I tried using elem[0].innerText but it returns the exact binding '{{binding}}' not the value of the binding
.directive('directiverestrict',function() {
return {
restrict:'A',
link: function(scope, elem, attr) {
// I want to get the value of the binding enclosed in the elements directive without ngModels
console.log(elem[0].textContent) //----> returns '{{binding}}'
}
};
});
You can use the $interpolate service, eg
.directive('logContent', function($log, $interpolate) {
return {
restrict: 'A',
link: function postLink(scope, element) {
$log.debug($interpolate(element.text())(scope));
}
};
});
Plunker
<span directiverestrict bind-value="binding"> {{binding}} </span>
SCRIPT
directive("directiverestrict", function () {
return {
restrict : "A",
scope : {
value : '=bindValue'
},
link : function (scope,ele,attr) {
alert(scope.value);
}
}
});
During the link phase the inner bindings are not evaluated, the easiest hack here would be to use $timeout service to delay evaluation of inner content to next digest cycle, such as
$timeout(function() {
console.log(elem[0].textContent);
},0);
Try ng-transclude. Be sure to set transclude: true on the directive as well. I was under the impression this was only needed to render the text on the page. I was wrong. This was needed for me to be able to get the value into my link function as well.

Angularjs - Pass argument to directive

Im wondering if there is a way to pass an argument to a directive?
What I want to do is append a directive from the controller like this:
$scope.title = "title";
$scope.title2 = "title2";
angular.element(document.getElementById('wrapper')).append('<directive_name></directive_name>');
Is it possible to pass an argument at the same time so the content of my directive template could be linked to one scope or another?
here is the directive:
app.directive("directive_name", function(){
return {
restrict:'E',
transclude:true,
template:'<div class="title"><h2>{{title}}</h3></div>',
replace:true
};
})
What if I want to use the same directive but with $scope.title2?
You can pass arguments to your custom directive as you do with the builtin Angular-directives - by specifying an attribute on the directive-element:
angular.element(document.getElementById('wrapper'))
.append('<directive-name title="title2"></directive-name>');
What you need to do is define the scope (including the argument(s)/parameter(s)) in the factory function of your directive. In below example the directive takes a title-parameter. You can then use it, for example in the template, using the regular Angular-way: {{title}}
app.directive('directiveName', function(){
return {
restrict:'E',
scope: {
title: '#'
},
template:'<div class="title"><h2>{{title}}</h2></div>'
};
});
Depending on how/what you want to bind, you have different options:
= is two-way binding
# simply reads the value (one-way binding)
& is used to bind functions
In some cases you may want use an "external" name which differs from the "internal" name. With external I mean the attribute name on the directive-element and with internal I mean the name of the variable which is used within the directive's scope.
For example if we look at above directive, you might not want to specify another, additional attribute for the title, even though you internally want to work with a title-property. Instead you want to use your directive as follows:
<directive-name="title2"></directive-name>
This can be achieved by specifying a name behind the above mentioned option in the scope definition:
scope: {
title: '#directiveName'
}
Please also note following things:
The HTML5-specification says that custom attributes (this is basically what is all over the place in Angular applications) should be prefixed with data-. Angular supports this by stripping the data--prefix from any attributes. So in above example you could specify the attribute on the element (data-title="title2") and internally everything would be the same.
Attributes on elements are always in the form of <div data-my-attribute="..." /> while in code (e.g. properties on scope object) they are in the form of myAttribute. I lost lots of time before I realized this.
For another approach to exchanging/sharing data between different Angular components (controllers, directives), you might want to have a look at services or directive controllers.
You can find more information on the Angular homepage (directives)
Here is how I solved my problem:
Directive
app.directive("directive_name", function(){
return {
restrict: 'E',
transclude: true,
template: function(elem, attr){
return '<div><h2>{{'+attr.scope+'}}</h2></div>';
},
replace: true
};
})
Controller
$scope.building = function(data){
var chart = angular.element(document.createElement('directive_name'));
chart.attr('scope', data);
$compile(chart)($scope);
angular.element(document.getElementById('wrapper')).append(chart);
}
I now can use different scopes through the same directive and append them dynamically.
You can try like below:
app.directive("directive_name", function(){
return {
restrict:'E',
transclude:true,
template:'<div class="title"><h2>{{title}}</h3></div>',
scope:{
accept:"="
},
replace:true
};
})
it sets up a two-way binding between the value of the 'accept' attribute and the parent scope.
And also you can set two way data binding with property: '='
For example, if you want both key and value bound to the local scope you would do:
scope:{
key:'=',
value:'='
},
For more info,
https://docs.angularjs.org/guide/directive
So, if you want to pass an argument from controller to directive, then refer this below fiddle
http://jsfiddle.net/jaimem/y85Ft/7/
Hope it helps..
Controller code
myApp.controller('mainController', ['$scope', '$log', function($scope, $log) {
$scope.person = {
name:"sangeetha PH",
address:"first Block"
}
}]);
Directive Code
myApp.directive('searchResult',function(){
return{
restrict:'AECM',
templateUrl:'directives/search.html',
replace: true,
scope:{
personName:"#",
personAddress:"#"
}
}
});
USAGE
File :directives/search.html
content:
<h1>{{personName}} </h1>
<h2>{{personAddress}}</h2>
the File where we use directive
<search-result person-name="{{person.name}}" person-address="{{person.address}}"></search-result>
<button my-directive="push">Push to Go</button>
app.directive("myDirective", function() {
return {
restrict : "A",
link: function(scope, elm, attrs) {
elm.bind('click', function(event) {
alert("You pressed button: " + event.target.getAttribute('my-directive'));
});
}
};
});
here is what I did
I'm using directive as html attribute and I passed parameter as following in my HTML file. my-directive="push" And from the directive I retrieved it from the Mouse-click event object. event.target.getAttribute('my-directive').
Insert the var msg in the click event with scope.$apply to make the changes to the confirm, based on your controller changes to the variables shown in ng-confirm-click therein.
<button type="button" class="btn" ng-confirm-click="You are about to send {{quantity}} of {{thing}} selected? Confirm with OK" confirmed-click="youraction(id)" aria-describedby="passwordHelpBlock">Send</button>
app.directive('ngConfirmClick', [
function() {
return {
link: function(scope, element, attr) {
var clickAction = attr.confirmedClick;
element.on('click', function(event) {
var msg = attr.ngConfirmClick || "Are you sure? Click OK to confirm.";
if (window.confirm(msg)) {
scope.$apply(clickAction)
}
});
}
};
}
])

scope change in a directive not a apply in view

I have a directive in a template.html, included by a ng-include, in this directive I change the scope , but it is not change in my view
Here is my html
<div ng-controller="myCtrl">
<div id="modal">
<div ng-show="showDIv">Somthing to controll</div>
</div>
<div ng-include src="template.html">
</div>
Here is my template
<a ng-support></a>
And here is my directive
app.directive('ngSupport', function(){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('click', function(e) {
$("#modal").dialog({height:518,width:900,modal:true });
scope.showDiv = true;
scope.$apply();
});
}
};
});
When i change the scope in the directive it is not apply in the view, anyone could help please ?
ng-include creates a new scope so scope.showDiv only affects the local scope.
Depending on how you want to structure your application, you could try accessing scope.$parent.showDiv instead, but it is not really future proof as it will depend on the HTML nesting.
A better solution would be to have the showDiv property stored inside an object in the parent scope. For example scope.ui = {}, this way, when you set scope.ui.showDiv = true in your directive, it will look up the parent scope automatically (using prototype inheritance), instead of adding the property to the local scope.
Finally, another solution would be to refactor your code to make it less complex: I think using a ng-include just for adding one element is an overkill, you could put directly <a ng-support></a> inside your html, which would avoid the problem you have with an intermediary scope being generated.
Another option is to broadcast an event, and watch for it in the controller. Something like this.
app.directive('ngSupport', function($rootScope){
return {
restrict: 'A',
link: function(scope, elem, attr, ctrl) {
elem.bind('click', function(e) {
$("#modal").dialog({height:518,width:900,modal:true });
$rootScope.$broadcast('event:modal-clicked');
});
}
};
});
With this in your controller.
$scope.$on('event:modal-clicked', function() {
$scope.showDiv = true;
});

Resources