I'm receiving the error: Multiple directives [gridsection, gridsection] asking for templateon : <div gridsection=""> with this code.
I don't see how i'm using nested directives or what is causing this.
html page
<div gridsection ></div>
directive
angular.module('web').directive('gridsection', function() {
return {
restrict: 'A',
replace: false,
scope: {
patient: "=patient"
},
templateUrl: 'directive/section.html',
link: function(scope, element, attrs, fn) {
}
};
});
directive/section.html
<div>
here?
</div>
It seems like you are declaring the gridsection multiple times in your angular code.
I have seen this before when I have a copy of a directive script file in a folder.
i.e. my file structure was
* myDirective.js
* myDirective - copy.js
So essentially I had two directives with the same name.
Doh!
Note originally posted this as a comment but created as an answer in response to comment from #jayjayjay
For posterity, I was getting this exception because I was trying to create a directive named pager and that was colliding with Bootstrap's pager.
Make sure you didn't include the script tag twice.
I had this issue but only declared the directive once in the markup, turns out it was because I included the script twice.
Note: I saw this in one of the comments for another answer and posted it as an answer for easier access/to prevent it getting lost.
I was getting this error for a reason not specified in other answers.
I was using declaration for xyz directive as <xyz xyz="xyz"></xyz>
my definition was:
angular.module('app')
.directive('xyz', function () {
return {
templateUrl: '..../xyz.html',
restrict: 'EA',
scope: {
xyz: '='
},
link: function (scope, element, attrs) {
}
};
});
The problem here is that I allowed directive to be used as element and attribute. so <xyz xyz="xyz"></xyz> contained both the declaration which was causing the issue.
Solution is to either restrict the directive to be used as Element only restrict: 'E' OR change the name of the directive to something like xyzView and use it like <xyz-view xyz="xyz"></xyz-view>.
Related
How can I parse/compile/resolve the contents of contained html in my directives.
The directive in question is:
angular.module('transclude', [])
.directive('heading', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
cls: '#'
},
template: '<h1 ng-transclude></h1>'
};
});
and the html to accompany it:
<div ng-app="transclude">
<div ng-controller="Ctrl">
<heading cls="beans">
<span class="{{cls}}">{{cls}}</span>
</heading>
</div>
</div>
I've created the following very simplified plunker to demonstrate my problem: http://jsfiddle.net/ys9fekss/
As you can see, I am expecting the html which is contained within my directive to have the {{cls}} tags on both the attribute and the contained html to be replaced with the literal string 'beans'.
I've been struggling with this all day - I've looked at scoping, compiling, link functions - you name it, and to no avail.
What I'm trying to do is create a validator directive which can wrap any type of field.
What do I have to do to get angular to parse that field's html?
UPDATE: Since I'm still struggling with this, I've posted my actual HTML (modified for the solution given below) showing the problem with the shared scope:
http://jsfiddle.net/hwsqsxf3/
In the above example, setting scope: true stops the repeated values, but then stops the name="" attribute being parsed!
What I need is both of these things... at once!
perhaps a different aproach is what you need, a directive with shared scope (not isolated like yours) but with the ability to create new values to the scope, that way you can use the directive many times in your app but with different values in each one of them:
<div ng-app="transclude">
<div ng-controller="Ctrl">
<heading cls="beans" target="tar">
<span class="{{tar}}">{{tar}}</span>
</heading>
</div>
</div>
// the target attribute will be your scope value (theres no need to be even declared in the controller)
function Ctrl($scope) {
$scope.foo = 'Bar';
}
angular.module('transclude', [])
.directive('heading', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<h1 ng-transclude></h1>',
link: function(scope, el, attr){
scope[attr.target] = attr.cls;
}
};
});
check this fiddle
I think I've solved my own problem - with much help from Strife86 and This blog
My solution is here. It's not very elegant - not sure if this is a fault of the way directives work or a fault of me. If there is a more elegant way I'd love to hear it.
JsFiddle
The directive's code is bascally:
.directive('validatingField', ['$compile', function($compile) {
return {
restrict: 'EA',
transclude: true,
scope: {
label: '#',
field: '#',
labelWidth: '#',
fieldWidth: '#',
error: '=',
ngModel: '='
},
templateUrl: "validating-field.html",
link: function (scope, element, attrs, ctrl, transcludeFn) {
transcludeFn(scope, function(clone, scp) {
element.find('ng-transclude').replaceWith(clone);
});
}
};
}]);
Binding ngModel was the most difficult thing and I'm not happy repeating elements constantly, but right now I've burned 1.5 days to get here so I'm not really in the mood for experimenting!
Ok, this seems to be the fix, although it is a bit inelegant:
http://jsfiddle.net/ywksgocm/
This includes two-way binding of the model, and inherited and augmented parent scope (for abritrary controller scope access).
The key to this is:
myScope.$new();
If there are any ways to improve this, please do let me know.
I want to create an angular directive inside of a link function, however; the directive created is not able to be compiled.
See this JSFiddle: http://jsfiddle.net/v47uvsj5/5/
Uncommenting this directive in the global space works as expected.
app.directive('test', function () {
return {
templateUrl: 'myform', // wraps script tag with id 'myform'
restrict: 'E',
require: "^mydir",
replace: true,
scope: {
},
link: function (scope, element, attrs, mydirCtrl) {
scope.remove = function () {
element.remove();
mydirCtrl.remove();
}
}
}
});
But the exact same code inside the link function fails.
The reason I want to do this is because I want the user (who is going to be myself) to be able to provide only a script tag's id via an id attribute to my main directive which will in turn create a 'wrapper' directive with a 'remove' method. This way, in the script tag, all one needs to do is implement the 'remove'.
Check out this fiddle:
http://jsfiddle.net/v47uvsj5/8/
What I did in this fiddle was daisy chain your directives, which is the correct thing to do. When your app runs, it does a binding of each of your directive and builds your html as it's being compiled, then it links events to it. Links and compilation happen after binding all directives to the DOM.
So <test></test> becomes <div></div> if you give it a template. If there is no template, nothing really builds your directive against the DOM, it just becomes empty, but you can still run a jquery script if you want.
Think of it like this, when your app loads up, it registers all the directives to be binded with the associated templates. Afterwards, the app then "compiles" those directives by binding any kind of events to the newly established DOM. At this point, if no directives are registered during app load, the compile function ignores it. In your case, you tried to bind the 'test' directive after the app load, and during the compilation.
This mechanism is analogous to how jquery's "on" works. When you do a "click" event on an already loaded DOM element, this fires up. But when you load html AFTER the DOM is finished, nothing works unless you use "on".
To be fair, the developers of angular did mention how there's a steep learning curve for handling directions, and will be revised to make it much easier in 2.0. You can read about it in this blog here: Angular-2.0
Anyways,
This is how your html should look like:
<mydir><test></test></mydir>
and this is how you daisy chain:
var app = angular.module('app', []);
app.directive('mydir', function ($compile, $templateCache) {
return {
template: '',
restrict: 'E',
controller: function () {
console.log("got it!");
}
}
}).directive('test', function () {
return {
templateUrl: 'myform',
restrict: 'E',
require: "^mydir",
replace: true,
scope: {
},
link: function (scope, element, attrs, mydirCtrl) {
scope.remove = function () {
element.remove();
mydirCtrl.remove();
}
}
}
});
I am developing a widget where I want to render some messages/text one after another. I want to change the template of the message based on the type of message.
my current directive setup is as follows
directive('cusMsgText', function(){
return {
restrict: 'E',
template:function(elements, attrs){
return '<div></div>';
},
link: function($scope, iElm, iAttrs, controller) {
//add children to iElm based on msg values in $scope
}
};
});
The directive is used as follows
<div ng-repeat="(key, value) in chatUser.msg">
<data-cus-msg-text msg="value.type"></data-cus-msg-text>
</div>
Now my question are -:
Is it possible to return one of multiple strings (templates) from
template function itself based on the actual value of attribute
msg. I tried accessing attrs.msg in template function and it
return value.type.
If not then, Is it good to manipulate template under linker or I
need to move it to compile function?
To render a different template based on value.type you can use the ng-switch statement:
<div ng-switch="value.type">
<div ng-switch-when="type1">
//...template for type 1 here...
</div>
<div ng-switch-when="type2">
//...template for type 2 here...
</div>
</div>
Also, if I understood your second question: manipulation of the uncompiled directive should be done in the compile function, all the manipulation which occurs after compilation should go in the link function.
Docs for ngSwitch
EDIT: +1 to Sebastian for understanding what you wanted. However, what he is proposing is essentially reinventing the wheel, since it is essentially compiling and inserting the template manually (which is what ngSwitch does for you). Also, you can access the attributes you put on your directive through the attrs argument of the link function.
In the template function you don't have access to the scope of your directive. If you want to control what gets rendered you can do this using conditional logic (e.g. ng-switch) in a global template as suggested by simoned or use a link function:
.directive('cusMsgText', function($compile) {
return {
restrict: 'E',
scope: {
msg: '=',
item: '='
},
link: function(scope, element, attrs) {
templates = {
x: '<div>template x {{item.name}}</div>',
y: '<div>template y {{item.name}}</div>'
};
var html = templates[scope.msg];
element.replaceWith($compile(html)(scope));
}
};
});
Using AngularJS 1.0.8, I'm trying to create some reusable directives to create a situation where a web developer can code a single "top-level" directive with a number of attributes, and this directive, in turn, has a template containing other directives, which themselves might contain other directives etc.
The problem I'm having is making the "inner" templates aware of the top level attributes. I thought this would be a universal problem, but it didn't look, from my research, that anyone else was asking this.
I created this Plunker to show the problem:
<!DOCTYPE html>
<html ng-app="outerInnerDirectivesApp">
<head>
<title>Outer/Inner Directives</title>
</head>
<body>
<div>Single level directive follows:</div>
<single-level-directive single-level-id="single123"></single-level-directive>
<div>Outer/inner directive follows (Expecting "outer123"):</div>
<outer-directive outer-id="outer123"></outer-directive>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<script src="app.js"></script>
<script src="directives.js"></script>
</body>
</html>
In the Plunker,
single-level-directive works and is, I think, a standard way to display data.
outer-directive and inner-directive aren't working.
What I expected to happen with these was
(i) outerDirective compiles/links to produce the html
<inner-directive inner-id="outer123"></inner-directive>
and then
(ii) innerDirective compiles/links to produce html
<div>outer123</div>
But at step (ii) I get
<inner-directive inner-id="" class="ng-isolate-scope ng-scope">
<div class="ng-binding"></div>
</inner-directive>
so an empty div is generated by innerDirective.
In fact, if I change outer-template.html to be
<div>{{outerId}}<div>
then the value displays correctly, so it looks like scope.outerId is available at the correct point, but Angular isn't happy about me trying to use it in the way I am.
Is this a reasonable thing to expect Angular to do? If so, what am I missing? If not, then what do you think would be a sensible alternative way to build up more complex screens from simple sets of directives?
If you are going to design directives with isolated scope, I would suggest using the isolated scope to define the type of attribute you want to use:
outerInnerApp.directive("outerDirective", function() {
return {
restrict: "E",
scope: {
outerId: '#'
},
link: function(scope, element, attrs) {
},
templateUrl: "outer-template.html"
};
});
outerInnerApp.directive("innerDirective", function() {
return {
restrict: "E",
scope: {
innerId: '='
},
link: function(scope, element, attrs) {
},
templateUrl: "inner-template.html"
};
});
Here is a working plunker.
Your outer directive is using the value that is defined in the attribute. So, to pass the value into the isolated scope, we can use #. The inner scope is then binding a variable through. So, we can use = to set up a bound attribute.
Had some more thoughts about this. Having used AngularJS a bit more, I'm not sure that I want to bind to the scope (using the "="). In fact, I can get the original Plunkr to work by making these changes:
outerInnerApp.directive("outerDirective", function() {
return {
restrict: "E",
scope: {
//add outerId here
outerId: "#"
},
link: function(scope, element, attrs) {
//remove scope assignment here
//scope.outerId = attrs.outerId;
},
templateUrl: "outer-template.html"
};
});
outerInnerApp.directive("innerDirective", function() {
return {
restrict: "E",
scope: {
//add innerId here
innerId: "#"
},
link: function(scope, element, attrs) {
//remove scope assignment here
//scope.innerId = attrs.innerId;
},
templateUrl: "inner-template.html"
};
});
What I don't understand at the moment is why there is a different between, say,
innerId:"#"
and setting the value of scope in the link function
link: function(scope, element, attrs) {
scope.innerId = attrs.innerId;
}
When I find out why it behaves differently I'll post back.
I've a question about a directive I want to write. Here is the jsfiddle
This is the HTML
<div ng-controller="TestCtrl">
<mydir base="{{absolute}}">
<div>{{path1}}</div>
<div>{{path2}}</div>
</mydir>
</div>
The directive, called 'mydir', will need the value of the base attribute and all the path values (there can be any number of paths defined). I don't want to inject values from the controller directly into the directive scope (they need to be independent). I have 3 questions about this (checkout the jsfiddle too!)
1) although the code works as it is now, there is an error: "TypeError: Object # has no method 'setAttribute'". Any suggestions what is wrong?
2) in the directive scope.base is 'undefined' instead of '/base/'. Can this be fixed.
3) How can I make a list of all the path values in the directive ?
You need to set an isolate scope that declares the base attribute for this directive:
scope: {
base: "#"
}
This solves (2).
Problem (1) is caused from the replace: true in cooperation with the scope above, because it creates a comment node to contain the repeated <div>s.
For (3), add paths: "=" to scope and, of ocurse, pass them from the parent controller as an array.
See forked fiddle: http://jsfiddle.net/swYAZ/1/
You're creating an new scope in your directive by setting scope: true what you want to do is:
app.directive('mydir', function ($compile, $rootScope) {
return {
replace: true,
restrict: 'E',
template: '<div ng-repeat="p in paths"> {{base}}{{p}}/{{$index}}</div>',
scope: {
base : '='
},
link: function (scope, iElement, iAttrs) {
scope.paths = ['p1', 'p2'] ;
}
}
});
This will setup a bi-directional binding between your directive's local scope and the parent's scope. If the value changes in the parent controller it will change in the directive as well.
http://jsfiddle.net/4LAjf/8/
If you want to make a robust directive to be able to replace the data to its content (3)
You can pass a reference as an attribute and display the data accordingly.
See this article, look for wrapper widget.