Angular directives in cached template not binding properly - angularjs

Given this directive:
myApp.directive('someDirective', ['$compile', '$templateCache', function($compile, $templateCache) {
return {
restrict: 'A',
link: function(scope, element, attr) {
// Load a template.
var template = $templateCache.get("partials/foo.html");
$compile(template)(scope);
$(element).append(template);
}
};
}]);
and given foo.html:
<div data-foo>Some Text</div>
and the foo directive:
myApp.directive('foo', [function() {
return {
restrict: 'A',
link: function(scope, element, attr) {
console.log('foo1');
$(element).on('click', function() {
console.log('foo2');
});
}
};
}]);
I will always see foo1 in my console but never foo2 when I click on the added element. In fact, though I can see the added elements just fine and can console.log() them just fine, I am unable to ever bind any handlers to them. What am I missing?

A div will have no height unless you put something in it. Change your div to this:
<div data-foo>Some content</div>
Also you need to make the template an element, do this as well right after you create it:
template = angular.element(template);
Here's a jsFiddle showing you: http://jsfiddle.net/c3Wuu/
As a side note, you should not be wiring up a click event, or building a template like this. Have a look at some videos and do angular tutorial unless you know what you're doing.
Edit
This is how you would wire it up using angular practices
in foo directive html:
<div data-foo>
<span data-ng-click="clickOne()">Click One</span>
<span data-ng-click="clickTwo()">Click Two</span>
</div>
In foo link function:
scope.clickOne = function(){
console.log("click one");
};
scope.clickTwo = function(){
console.log("click two");
};
By wiring up your own click function you are circumventing the point of angular.

Related

attribute directive that adds element to the dom from a template file

i have a template file myfile.html and i'm writing an attribute directive in angular, the purpose of this directive is to insert the elements that are inside myfile.html after the element that the directive attribute was declared on. for example: will insert all the content of myfile.html after that div.
i tried to do it like so:
app.directive('myAngularDirective', function () {
return {
restrict: "A",
link: function(scope, element){
var addMe = angular.element("myfile.html");
element.after($copile(addMe));
scope.$apply();
}
}
});
<h1>myfile.html</h1>
<div ng-controller="myController">
<div ng-click=clickMe()></div>
</div>
<h1>my home page</h1>
<div my-angular-directive></div>
but nothing happens, the content of myfile.html is not added to the home page.
if i write plain html in the after function like so:
element.after("")
it does get added to the dom but angular do not digest it, and ng-click is not working.
thanks for your help.
$compile("<html here>") returns a template function that expects a scope to be passed before you get your DOM back. Change your code to the following and it should work as expected:
app.directive('myAngularDirective', function($compile) {
return {
restrict: "A",
link: function(scope, element){
var addMe = angular.element("<div>Hello World</div>");
element.after($compile(addMe)(scope));
scope.$apply();
}
}
});
If you want to load the template from an external URL. You'll need to do a template request.
app.directive('myAngularDirective', function($compile,$templateRequest) {
return {
restrict: "A",
link: function(scope, element){
$templateRequest('index.html').then(function(tpl){
var addMe = angular.element(tpl);
element.after($compile(addMe)(scope));
scope.$apply();
});
}
}
});

Apply AngularJS directive (ui-scrollfix) conditionally

I'm using ui-scrollfix directive from UI.Utils to make so called sticky header. It works fine.
There are two headers in the app. Main one is constantly on the page and second one appears only in certain pages.
<div id="main-header" ui-scrollfix></div>
<div id="second-header" ui-scrollfix></div>
What I need to do is that ui-scrollfix directive was added or applied to main-header, if second-header is present.
Is that possible to achieve?
Thank you!
I think should do this by wrapping both headers in a directive say headers. And then in the said directive query for the second header, if it exists then apply the ui-scrollfix directive to it.
HTML
<div ng-app='app' ng-controller="aController">
<div headers>
<div id="main-header"> main header </div>
<div id="second-header" ui-scrollfix> second header </div>
</div>
</div>
JS
var app = angular.module('app', []);
app.controller('aController', ['$scope', function (scope) {
}]).directive('uiScrollfix', [function () { // this is just to check that the directive is applied to the element
return {
restrict: 'A',
link: function (scope, el, attrs) {
el.on('click', function () {
console.log(el[0].textContent);
});
}
}
}]).directive('headers', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, el) {
var directiveEl = el[0],
mainHeaderEl = directiveEl.querySelector('#main-header'),
secondHeaderEl = directiveEl.querySelector('#second-header'),
$mainHeaderEl = angular.element(mainHeaderEl);
if (secondHeaderEl) {
$mainHeaderEl.attr('ui-scrollfix', '');
$compile($mainHeaderEl)(scope);
}
}
}
}]);
JSFIDDLE

How to call custom directive template url on button click using AngularJS

I am trying to call a html page which is given in the templateUrl of my directive when I click a button, below is my code "hi" should be displayed when I click the "click me" button. Please suggest me how to do this.
sample.html:
<div ng-controller="MyController">
<button custom-click="">Click Me</button>
</div>
sample.js:
appRoot.directive('customClick', function() {
return {
link: function(scope, element, attrs) {
element.click(function(){
templateUrl:'/page.html';
});
}
}
});
Page.html:
<div><h4>HI</h4></div>
Update: The Snippet has been updated with getting the code from a URL
Adding onto the above answers:
appRoot.directive('customClick', function($http, $compile) {
return {
link: function(scope, element, attrs) {
element.click(function(){
$http.get("/page.html").then(function(resp){
$(element).html(resp.data);
var fnLink = $compile(element);
fnLink($scope);
});
});
}
}
});
P.S: Needs jQuery to run as using some functions like html() which can be bypassed if you dont want to include jQuery
I don't think that structure is possible, at all.
The easiest way would be to handle a show/hide type of functionality on the directive and have the template be there at all times.
For this you could use either ng-show, ng-hide or ng-if (and some others that I won't dig into).
base directive
appRoot.directive('customClick', function () {
return {
template: '<div><h5>HI</h5></div>',
link: function (scope, el, attrs) {
scope.active = false;
el.on('click', function () {
scope.$apply(function () {
scope.active = !scope.active;
});
});
}
}
});
ng-show
template: '<div ng-show="active"><h5>HI</h5></div>'
ng-hide
template: '<div ng-hide="!active"><h5>HI</h5></div>'
ng-if
template: '<div ng-if="active"><h5>HI</h5></div>'
Edit: If you are using templateUrl, simply put the ng-show/hide/if directive on the root element of the template being referenced, and this should work the same.
Oh, and here's a fiddle.
http://jsfiddle.net/ADukg/5426/

Directive does not see elements in AngularJS

I am having troubles to understand why my directive does not seem to work properly. Here's the quick overview of the problem:
I load the HTML using Ajax that contains Angular elements. The Ajax response is $compiled, so those elements works, but the problem is that I have to timeout it with 0 seconds.
<div my-directive button="bar">
<button id="foo">Foo</button>
</div>
<button id="bar">Hi!</button>
and the js:
app.directive('myDirective', function() {
return {
link: function(scope, element, attrs) {
var foo = element.find('button');
var bar = $(attrs.button);
}
};
});
The above foo-bar elemenets will not be found until I surround them with setTimeout(..., 0);
Can someone tell me if there's a better approach to get access to them without having setTimeout?
What you are doing is the right way to achieve this but you should use $timeout.
app.directive('myDirective', function($timeout) {
return {
link: function(scope, element, attrs) {
$timeout(function() {
var foo = element.find('button');
var bar = $(attrs.button);
},0);
}
};
});
It's just changing priority of execution code in javascript.

Load angular directive in view, based on $scope value

I have a directive defined as
Application.Directives.directive('listview', function() {
return {
restrict: 'EAC',
templateUrl: 'directives/listview/view.html'
};
});
And then want to include it from the main view like this
<div class="{{directiveName}}">
</div>
where directiveName equals "listview". However, it does not work. It generates the below code, but the listview directive does not get loaded
<div class="listview">
</div>
Yet, when I type the above generated code directly into the main template, it does load the directive. How come? How can I make it work?
So I found a way. What you'd want is something like this
<div {{directiveNameInScope}}></div>
But again, that doesn't work. So I created a directive to do it for you. It works like
<div loaddirective="directiveNameInScope"></div>
where the loaddirective directive looks like
Application.Directives.directive('loaddirective', function($compile) {
return {
restrict: 'A',
scope: { loaddirective : "=loaddirective" },
link: function($scope, $element, $attr) {
var value = $scope.loaddirective;
if (value) {
// Load the directive and make it reactive
$element.html("<div "+value+"></div>");
$compile($element.contents())($scope);
}
},
replace: true
};
});
I put it up on github here: https://github.com/willemmulder/loaddirective

Resources