I am still rather new to angular JS, and wanted to learn about directives. I want to make a nested directives, kind like menu such as this:
<myDirective>Hello</myDirective>
Pretty simple, and I've put it like this with $compile:
.directive('myDirective', function($compile) {
return {
restrict: 'E',
transclude: true,
template: '<dialog2><div ng-transclude></div></dialog2>',
link: function (scope, element, attrs) {
$compile(element.contents())(scope);
}
};
})
.directive('dialog2', function() {
return {
restrict: 'E',
transclude: true,
template: '<b>Hi</b><div ng-transclude></div> there'
};
});
Now, it only shows as
Hello
And it should say
Hi Hello there
Where did I do wrong?
Thank you
Just simple thing to note when declare a directive in html use my-directive instead of myDirective
<my-directive>Hello</my-directive>
Also no need to complie in same contents again without modifying DOM.If you any directive/custom directive applied in template or templareUrl angularjs will complie for us before handover to us.
Demo link
Related
On my website, I want to standardize the way links to other persons look like, so I made a directive for it:
<my-person id="1"></my-person>
and
app.directive('myPerson', function() {
return {
restrict: 'E',
replace: 'true',
template: '{{person.name}}',
scope: {
person_id : '=id',
},
controller: function($scope, Repository) {
Repository.getPerson($scope.person_id).then(function(p) {
$scope.person = p;
});
},
};
});
This works well.
But in the next step, I want users to write news and in those news they want to refer to other persons. So basically, I want to display a text
'I met <my-person id="42"></my-person> yesterday.'
But when angularjs displays this context of my news entry, then the html tags are escaped of course and it is not compiled either.
Is there an easy way to achieve this? Here is a jsfiddle that shows my problem: jsfiddle link
You will need to compile your html inside the ng-repeat block.
For that make a directive like below
app.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
now inside your ng-repeat add your directive to the span like this:
<span class="row" bind-html-compile="newsEntry.text"></span>
working code here
Reference here
i have this wierd problem with binding data to a directive. this is how i declare my directive:
<my-directive data="myArray"></my-directive>
my directive code looks like:
angular.module('ngApp')
.directive('myDirective', function () {
return {
scope:{
data: '='
},
template: '<div steps="data.length"></div>',
restrict: 'E',
link: function postLink(scope, element, attrs) {
console.log(scope);
console.log(scope.data);
}
};
});
in the first log, the data property is correct:
screenshot of console.log output
but the second log is undefined.
any idea why?
here is your solution :
<my-directive data-example="myArray"></my-directive>
you should always name your variables data-<name>. This is far easier to maintain and this prevent errors with keywords like this one ;)
angular.module('ngApp')
.directive('myDirective', function () {
return {
scope:{
data: '=example'
},
template: '<div steps="data.length"></div>',
restrict: 'E',
link: function postLink(scope, element, attrs) {
console.log(scope);
console.log(scope.data);
}
};
});
i hope this help,
Live example : JsFiddle
thanks to #hadiJZ and #Unex i figured it out:
my directive was nested in another directive. but the parent directive used the link function for the logic, since on creation it wasn't having child directives.
so moving the logic out to a controller solved my problem.
i figured it out when i added a $timeout to the child directive, and the log (scope.data) was correct.
I'm using directive to display html snippets.
And templateUrl inside the directive,
to be able to include snippets as html file.
The directive does not work, if I try to call
inside a builtin ng-repeat directive
({{snip}} is passed as is, without substitute):
div ng-repeat="snip in ['snippet1.html','snippet2.html']">
<my-template snippet="{{snip}}"></my-template>
</div>
For reference, here is the directive:
app.directive("myTemplate", function() {
return {
restrict: 'EA',
replace: true,
scope: { snippet: '#'},
templateUrl: function(elem, attrs) {
console.log('We try to load the following snippet:' + attrs.snippet);
return attrs.snippet;
}
};
});
And also a plunker demo.
Any pointer is much appreciated.
(the directive is more complicated in my code,
I tried to get a minimal example, where the issue is reproducible.)
attrs param for templateUrl is not interpolated during directive execution. You may use the following way to achieve this
app.directive("myTemplate", function() {
return {
restrict: 'EA',
replace: false,
scope: { snippet: '#'},
template: '<div ng-include="snippet"></div>'
};
});
Demo: http://plnkr.co/edit/2ofO6m45Apmq7kbYWJBG?p=preview
Check out this link
http://plnkr.co/edit/TBmTXztOnYPYxV4qPyjD?p=preview
app.directive("myTemplate", function() {
return {
restrict: 'EA',
replace: true,
scope: { snippet: '=snippet'},
link: function(scope, elem, attrs) {
console.log('We try to load the following snippet:' + scope.snippet);
},
template: '<div ng-include="snippet"></div>'
};
})
You can use ng-include, watching the attrs. Like this:
app.directive("myTemplate", function() {
return {
restrict: 'E',
replace: true,
link: function(scope, elem, attrs) {
scope.content = attrs.snippet;
attrs.$observe("snippet",function(v){
scope.content = v;
});
},
template: "<div data-ng-include='content'></div>"
};
});
Just made changes in directive structure. Instead of rendering all templates using ng-repeat we will render it using directive itself, for that we will pass entire template array to directive.
HTML
<div ng-init="snippets = ['snippet1.html','snippet2.html']">
<my-template snippets="snippets"></my-template>
</div>
Directive
angular.module('myApp', [])
.controller('test',function(){})
.directive("myTemplate", function ($templateCache, $compile) {
return {
restrict: 'EA',
replace: true,
scope: {
snippets: '='
},
link: function(scope, element, attrs){
angular.forEach(scope.snippets, function(val, index){
//creating new element inside angularjs
element.append($compile($templateCache.get(val))(scope));
});
}
};
});
Working Fiddle
Hope this could help you. Thanks.
it seems you are trying to have different views based on some logic
and you used templateUrl function but Angular interpolation was not working, to fix this issue
don't use templateUrl
so how to do it without using templateUrl
simply like this
app.directive("myTemplate", function() {
return {
link: function(scope, elem, attrs) {
$scope.templateUrl = '/ActivityStream/activity-' + $scope.ativity.type + '.html'
},
template: "<div data-ng-include='templateUrl'></div>"
};
});
hope this is simple and esay to understand
I have previously created a directive called click-to-edit
you would use it like
<click-to-edit="someValue">
where $scope.someValue=7;
I have since realized that i need to use the ngModel controller, and it would be EASIER if i could recompile this directive and add ng-model="someValue" to the template. Im having trouble doing this as it is giving me the error
"Error: [$compile:ctreq] Controller 'ngModel', required by directive 'clickToEdit', can't be found!"
This is obviously because i have
require:'ngModel'
this is a snippet of the code so far
return {
restrict: 'A',
replace: true,
require: 'ngModel',
priority: 100,
compile: function(tElement, tAttrs, transclude) {
// Correct ngModel for isolate scope
tAttrs.$set('ngModel', tAttrs.clickToEdit, false);
}
return {
post: linkFn
};
},
scope: {
dp: '=?',
type: '#',
fn: '=?', //<--- formula
editFn:'&?' // if you want to execute a function on a valid save, add this
}
What i simply want to do, is take a directive that looks like
<click-to-edit="model"/>
and change it to
<click-to-edit="model" ng-model="model"/>
and then have it compile and work as expected.
Let me know if you need other code.
I'm not sure if you've considered this - but why not just do:
<click-to-edit ng-model="model"/>
To answer the original question, I suggest adding a body directive:
.directive('body', function() {
return {
restrict: 'E',
compile: function(element, attr) {
$('[click-to-edit]', element).attr('ng-model', 'model');
}
}
})
Plunker Here
I seem to have a small issue. I have created a directive and inserted the directive via an attribute on an existing DIV, see below.
<td my-directive>This Text I want to get hold of in my directive</td>
and the directive is displayed here. I did try playing around with element.parent() but this doesn't seem to work.
.directive('myDirective', function () {
return {
template: '<div></div>',
restrict: 'A',
link: function(scope, element, attrs) {
element.text(element.parent().text); // Doesn't work
}
};
});
I want the TD to continue with its normal operation i.e. Displaying the text with the TD element. But its blank, so I thought about re-injecting it in the directive, but why is it blank?
Actually what I am trying to do is any part of the directive is clicked I want to do some internal stuff and then raise an event on the $scope which is shared by controller i.e.
element.click(function(){
//alert("direc clicked");
scope.onClick()
});
Not sure if I am doing this correct.
Anyone done this before ?
I think you just need to add ng-transclude in your template:
.directive('myDirective', function () {
return {
template: '<div ng-transclude></div>',
transclude: true,
restrict: 'A',
link: function(scope, element, attrs) {
element.text(element.parent().text); // Doesn't work
}
};
});
source