Dynamically setting attributes on an AngularJS Directive element - javascript

I'm trying to build a directive in AngularJS that has a template which could contain other AngularJS directives. All of my directives require an "id" attribute, so I need to set the "id" on the directive inside the template. However, no matter how I do this, AngularJS keeps throwing this error:
Error: [$parse:syntax] Syntax Error: Token '{' invalid key at column 2 of the expression [{{field.id}}] starting at [{field.id}}].
http://errors.angularjs.org/1.4.6/$parse/syntax?p0=%7B&p1=invalid%20key&p2=2&p3=%7B%7Bfield.id%7D%7D&p4=%7Bfield.id%7D%7D
at angular.js:68
at Object.AST.throwError (angular.js:13010)
at Object.AST.object (angular.js:12997)
at Object.AST.primary (angular.js:12905)
at Object.AST.unary (angular.js:12893)
at Object.AST.multiplicative (angular.js:12880)
at Object.AST.additive (angular.js:12871)
at Object.AST.relational (angular.js:12862)
at Object.AST.equality (angular.js:12853)
at Object.AST.logicalAND (angular.js:12845)
I know that AngularJS is fine doing something like this: <div id="{{field.id}}">[...]</div>. It ends up being rendered correctly, with "{{field.id}}" replaced with the actual value of field.id. However once I try to apply my directive to that div, or use the directive as an element itself, AngularJS balks. I've tried all of the following and all result either in the error above or with the directive's ID set to "field.id" rather than the value of "field.id":
<!-- result in error shown above -->
<mydirective id="{{field.id}}"></mydirective>
<div mydirective id="{{field.id}}"></div>
<div class="mydirective" id="{{field.id}}"></div>
<mydirective ng-attr-id="{{field.id}}"></mydirective>
<div mydirective ng-attr-id="{{field.id}}"></div>
<div class="mydirective" ng-attr-id="{{field.id}}"></div>
<!-- result in id set to "field.id" -->
<mydirective id="field.id"></mydirective>
<div mydirective id="field.id"></div>
<div class="mydirective" id="field.id"></div>
<mydirective ng-attr-id="field.id"></mydirective>
<div mydirective ng-attr-id="field.id"></div>
<div class="mydirective" ng-attr-id="field.id"></div>
In case it helps, the general layout of the directive with the template looks like this:
<div ng-repeat="field in fields" ng-show="field.visible">
<!-- some other stuff -->
<mydirective ng-if="field.type == 'foobar'" id="{{field.id}}"></mydirective>
<!-- some other stuff -->
</div>
I'm seeing an identical issue with another attribute on the directive as well, so it's not limited to just the 'id' attribute.
Shortened version of the directive throwing the error:
var application = angular.module('application', []);
application = application.directive('mydirective', ['$http', function($http){
return {
restrict: 'AEC',
scope:{
mydirectiveId: '=id',
id : '#',
// a few other attributes
},
templateUrl: 'mydirective.html',
controller: function ($scope, $element){
if($scope.id === undefined){
console.error("The 'id' on mydirective is missing!");
}
// more logic... sorry can't post this
}
};
}]);

Can you show your directives code,if the name of your directive is myDirective,it should be my-Directive in the html.Also make sure you are using the restrict option on the correct element,attribute

I managed to figure this out. I'm not exactly sure why, but AngularJS was having a problem with the isolated scope on "mydirective". When I removed the mydirectiveId: '=id' attribute from the scope declaration, it started working again as expected.
Working HTML Template for parent directive:
<div ng-repeat="field in fields" ng-show="field.visible">
<!-- some other stuff -->
<mydirective ng-if="field.type == 'foobar'" id="{{field.id}}"></mydirective>
<!-- some other stuff -->
</div>
Working directive code for "mydirective":
application = application.directive('mydirective', ['$http', function($http){
return {
restrict: 'AEC',
scope:{
// mydirectiveId: '=id', << removed this line, it was the problem
id : '#',
// a few other attributes
},
templateUrl: 'mydirective.html',
controller: function ($scope, $element){
if($scope.id === undefined){
console.error("The 'id' on mydirective is missing!");
}
// more logic
}
};
}]);

Related

Placing custom directive in Angular 1.5.8 using ng-repeat is not working

I am placing my custom-directive on the fly in my HTML using ng-repat directive.
But the custom-directives are not evaluated and if the placed the same directive on HTML directly then it is working.
...
<body ng-app="docsSimpleDirective">
<div ng-controller="Controller">
<div>This is a Problem</div>
<!--Here in for loop i want to us the value to call a directive-->
<div ng-repeat="var in arr">
<!--Here i am using directive with restrict: 'C' and this is not expending-->
<span class="{{var}}"></span>
</div>
<!-- here my directive named "direct" is working fine -->
<span class="direct"></span>
</div>
</body>
...
and My Js code is
(function(angular) {
'use strict';
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.arr = ['direct','indirect'];
}]).directive('direct', function() {
return {
template: 'direct',
restrict: 'C'
};
}).directive('indirect', function() {
return {
template: 'indirect',
restrict: 'C'
};
});
})(window.angular);
I believe there is some compilation issue and i searched web and found $compile can solve my purpose but unable to implement.
Please help me in solving my issue.
Plunker implemention for same : https://plnkr.co/edit/lYGg0UkQpNhN5NJx13Zj?p=preview
Using a directive as a class is bad practice (because unreadable)
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#restrict-to-elements-and-attributes
Then you pass a directive as a class but dynamically by interpolation which in itself is bad practice (https://docs.angularjs.org/guide/interpolation#known-issues). The class name is interpolated and the element rendered but the directive is not compiled during interpolation.
Remove the restrict lines from your directive definitions and use them as attributes:
<span indirect></span>
Then to use them in the ng-repeat loop, you can check if var = "direct" or "indirect"
https://plnkr.co/edit/JUNFMCZASMntCnC6FsIO?p=preview
Use the directive like this
<div ng-repeat="var in arr">
<span direct ng-if="var == 'direct'"><span>
<span indirect ng-if="var == 'indirect'"><span>
<div>
and in controller define restrict as attribute.
.directive('direct', function() {
return {
template: 'direct',
restrict: 'A'
};

Unable to set the value of bound property in angular component

I'm trying to implement a bound property for an angular component as explained in the component documentation and this example.
Unfortunately the values I'm assigning at the tag level or in the $onInit methods are never used. Nor is the value printed when I use it as a model value.
You can find the full code on plunker.
My binding definition:
(function(angular) {
'use strict';
function SearchResultController($scope, $element, $attrs) {
var ctrl = this;
ctrl.searchFor = 'nohting-ctor';
ctrl.$onInit = function() {
console.log('SearchResultController.$onInit: searchFor='+ctrl.searchFor);
ctrl.searchFor = 'nothing-int';
};
}
angular.module('myApp').component('searchResult', {
templateUrl: 'searchResult.html',
controller: SearchResultController,
bindings: {
searchFor: '<'
}
});
})(window.angular);
Template:
<p>SearchResult for <span ng-model="$ctrl.searchFor"</span></span></p>
How it's used:
<h1>Main Window</h1>
<search-input on-start-search="$ctrl.startSearch(value)"></search-input>
<search-result search-for="nothing-ext"></search-result>
None of the nothing-* values is evers shown.
Any ideas what's wrong?
The usage of you component is not correct. If you want to pass a string it should be quoted:
<search-result search-for="'nothing-ext'"></search-result>
Then next problem is that this line
<p>SearchResult for <span ng-model="$ctrl.searchFor"</span></span></p>
doesn't make sense, as ngModel directive is only valid for input controls. You want ngBind or simple {{ $ctrl.searchFor }}:
<p>SearchResult for <span ng-bind="$ctrl.searchFor"</span></span></p>

How to get passed attributes from custom directive and collect them in an array to show into template

I have created a custom directive which should display a slider with the data entered to the custom directive. I need to be able to display the image Url and redirect Link via directive attributes for example:
<div class="sliderBanner" imgUrl="http://example.com/img1.jpg" imgLink="example.com"></div>
<div class="sliderBanner" imgUrl="http://example.com/img2.jpg" imgLink="example.com"></div>
<div class="sliderBanner" imgUrl="http://example.com/img3.jpg" imgLink="example.com"></div>
Now I want to collect those data and place them inside an array within directive scope and use ng-repeat inside directive template to show them.
e
PS: I'm using Swiper angular directive for slider purpose.
var app = angular.module('APP',['ksSwiper']);
app.directive('sliderBanner',function($http){
return{
scope:true,
restrict:'C',
link: function(scope,element,attr){
scope.data = [];
scope.data.push({
"id": attr.id,
"imgUrl": attr.imgUrl,
"imgRef": attr.imgRef
});
},
templateUrl: 'http://www.lajmislam.com/wp-content/themes/Newspaper/ng-templates/sliderBanner.html'
}
});
This is my directive template:
<ks-swiper-container autoplay="3000" show-nav-buttons="true" pagination-is-active="true" swiper="swiper">
<ks-swiper-slide ng-repeat="item in data">
{{item.id}},{{item.imgUrl}}
</ks-swiper-slide>
</ks-swiper-container>
<div ng-repeat="item in data">
{{item.id}},{{item.imgUrl}}
</div>
What errors are you seeing?
What you have here appears to be working. I tested it out here: https://github.com/styonsk/StackOverflowSolutions/tree/master/35997232

How to apply directive conditionally in AngularJS?

I want to apply a simple directive conditionally using ngAttr. I don't understand why my directive is always displayed. So if I have an undefined / false variable I want to apply my directive: dirr.
When using ngAttr, the allOrNothing flag of $interpolate is used, so if any expression in the interpolated string results in undefined, the attribute is removed and not added to the element.
My code pen
<div ng-app="myApp" ng-controller="MainController" class="container-fluid">
<h2 ng-bind="currentVersion"></h2>
<hr>
<div ng-attr-dirr="hidden || undefined">Default div</div>
</div>
angular.module('myApp',[])
.directive('dirr', function(){
return {
restrict:'AE',
template:'<div>Div from directive</div>'
}
})
.controller('MainController',['$scope', function($scope){
$scope.currentVersion = 'Angular 1.3.6';
$scope.hidden = undefined;
}])
;
You can make use of AngularJS's inbuilt directive ng-if to check for the condition and execute it conditionally.
Example:
<div ng-if="{some_condition}">
<dirr></dirr> <!--Execute the directive on the basis of outcome of the if condition-->
</div>
Form documentation
All of the Angular-provided directives match attribute name, tag name, comments, or class name
so whenever angular matches a diretive with attribute name,it compiles the template and renders the html irrespective of attribute value.
anyway you can use scope in directive template.so use ng-hide with scope's hidden property
angular.module('myApp',[])
.directive('dirr',function(){
return{
restrict:'AE',
template:'<div ng-hide="hidden">Div from directive</div>',
}
})
.controller('MainController',['$scope', function($scope){
$scope.hidden=false;
}]);
The answers are true and the sample code you provided works for small issues, but when it comes resolving this problem on large applications you may want to take this approach:
Updated Pen
<div ng-app="myApp" ng-controller="MainController" class="container-fluid">
<h2 ng-bind="currentVersion"></h2>
<hr>
<div dirr value="{{hidden}}">Default div</div>
</div>
.directive('dirr', function($log){
var directiveInstance = {
restrict:'AE',
scope:{
value:'#'
},
link: function (scope, element, attrs, ctrl) {
if(attrs.value === 'true'){
console.log('directive on !!');
$log.debug('element',element);
element[0].innerHTML = '<div>hello ' + attrs.value + '</div>';
}
else {
console.log('directive off !!');
}
}
}
return directiveInstance;
})
This is more tidy and you may not want to duplicate your code using ngIf or ngSwitch directives in seperate divs when you have something like:
<table>
<thead dirr value="{{statement}}">
<tr>
<th>
CrazyStuffHere...
</th>
<th>
CrazyStuffHere...
</th>
....
</tr>
</thead>
</table>

Use Directive two times in one controller AngularJS

I want to use same directives multiple times in one controller in AngularJS. I want to create a list widget that can be used multiple times. I can display two widgets at the same time under the same controller. But, I am unable to bind teamA and teamB data to ng-repeat in my directive. In addition to that, the code fails during addTeamMember() because datasource is undefined. I was hoping that datasource will be updated with teamA and teamB respectively.
Here is the HTML code.
<div ng-controller="myCtrl"><div class="container">
<my-directive datasource="model.teamA"></my-directive>
<my-directive datasource="model.teamB"></my-directive>
</div></div>
Controller.js:
angular.module('app',[])
.controller('myCtrl', [ '$scope', function($scope){
$scope.teamA = {};
$scope.teamB = {};
} ] );
Directive.js:
angular.module('app', [] )
.directive('myDirective', function(){
return{
restrict: 'AE',
templateUrl: 'directive_html.html',
scope: {
datasource: "=TeamMembers"
},
transclude: true,
link: function(scope, elem, attrs){
scope.addTeamMember = function(){
scope.datasource.push({});
};
scope.removeTeamMember = function(item){
scope.datasource.splice(item, 1);
};
}
};
}) ;
directive_html.html:
<div><div class="container">
<div class="form-group" ng-repeat="member in TeamMembers">
<textarea ng-model="member.text" rows="2"></textarea>
Remove
<div>
<button type="button" ng-click="addTeamMember()">Add</button>
</div></div>
Could anyone please help me out here? I want to create custom Widgets that can be used multiple places either in same controllers or in different controllers.
Thanks
As #Neozaru pointed out in comments. You are expecting the directive attribute to be called team-members here:
<div ng-controller="myCtrl"><div class="container">
<my-directive team-members="model.teamA"></my-directive>
<my-directive team-members="model.teamB"></my-directive>
</div></div>
You do this when you define the isolated scope as:
scope: {
datasource: "=TeamMembers"
}
The above line is saying, "team-members is what the outside attribute will be named, but internally I'll refer to the referenced object as scope.datasource".

Categories

Resources