How can I reinitialize an AngularJS directive in ng-repeat? - angularjs

This is my directive
app.directive('masonry', function() {
return function(scope, element, attrs) {
scope.$watch(attrs.masonry,function(value){
jQuery(function($) {
var grid = $('.main_creation').masonry({
itemSelector: '.creation',
percentPosition: true,
horizontalOrder: true
});
grid.imagesLoaded().progress( function() {
grid.masonry('layout');
});
});
});
};
});
This is my HTML
<div class="creation" ng-repeat="img in images" masonry>
//some code
</div>
When first load the directive is functioning but if the ng-repeat is changing for example deleting or adding more data in ng-repeat the directive doesnt functioning.
EDIT:
Also I dont know why the watch doesnt trigger. When I console.log(attrs.masonry) their is changes.

Related

Angularjs update controller variable from directive and use in view

I've just started to use Angularjs and with the help of some stackoverflow answers I have created an image fallback directive with Angularjs.
The fallback functionality is working, but now I would like the use a boolean, a variable set in the controller I guess, in combination with ng-show in the view which indicates if the fallback image is used, or if the original image is loaded. I've changed my code several times, but it never worked....
(The teamCtrl is a seperated controller which does work and can be ignored in this issue, so I did not include the code.)
This is a piece of my html:
<div class="thumbnail margin-bot-20px">
<img ng-src="../img/team{{teamCtrl.selectedteam.id}}.jpg" myfallback-src="../img/onbekend.jpg" />
</div>
<div ng-controller="fallbackController as fbCtrl">
<p>
Wijzig foto
Verwijder foto
</p>
</div>
This is the directive and the directive's controller:
(function () {
'use strict';
angular.module('PD.fallback', [])
.directive('myfallbackSrc', myfallbackSrc);
angular.module('PD.fallback')
.controller('fallbackController', fallbackController);
function fallbackController()
{
this.directivedummy = false;
};
function myfallbackSrc()
{
var directive = {
link: link
//controller: fallbackController, // controllerfunctie
//controllerAs: 'vm' // controllerAs-alias
//bindToController: true
//scope: {}
};
return directive;
};
// 3. Link-function implementeren
function link(scope, element, attrs)
{
element.bind('error', function()
{
scope.directivedummy = false;
if (attrs.src != attrs.myfallbackSrc)
attrs.$set('src', attrs.myfallbackSrc);
});
element.bind('load', function()
{
if (attrs.src != attrs.myfallbackSrc)
scope.directivedummy = true;
});
}
})();
So I would like to show/hide a button in the view html. The button must be visible when the src image was loaded successfully and must be hidden when the fallback image is loaded.
Hopefully someone can help me?
Assuming your are not using ControllerAs syntax, you need to trigger the $digest since the bind callback is happening outside of Angular
element.bind('error', function(){
scope.$apply(function (){
scope.directivedummy = false;
if (attrs.src != attrs.myfallbackSrc)
attrs.$set('src', attrs.myfallbackSrc);
});
});

element.on('click') on a directive within ng-if doesn't work

I use a directive on links to provide a modal login, if the user isn't logged in. And it works fine for all usecases until now. In a new case the directive is part of a ng-if section.
On the snippet the first link works fine, the second doesn't work. The element.on('click', function(evt) {…}) will never called. And the permissions are not checked and no modal login will prompt to the user.
Ok, if I use ng-show instead of ng-if, both links works. Because ng-show doesn't remove the element from the DOM and it doesn't create a child scope like ng-if. But in my usecase I must use ng-if. What can I do? How can I provide a click event, which works within a ng-if and other ng-x directives too?
var app = angular.module('myapp', []);
app.controller('MyCtrl', function($timeout) {
// something stupid here
var vm = this;
vm.bar = false;
$timeout(function() {
vm.bar = true;
});
});
/**
* I need an isolate scope, because some user state checks
* will take place in the directive. And there are different
* login forms based on type and other attributes.
*/
app.directive('modalLogin', function() {
return {
restrict: 'A',
scope: {
type: '#mlType'
},
compile: function(element, attrs) {
if (element[0].nodeName !== ('A')) {
// use directive only at links
return function() {};
}
return function(scope) {
function checkPermissions() {
// do the magic here
alert('foo');
}
element.on('click', function(evt) {
evt.preventDefault();
checkPermissions();
})
}
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="myapp" ng-controller="MyCtrl as FOO">
<div>
<!-- it works -->
Klick me, I'm working well
</div>
<!-- it doesn't work -->
<div ng-if="FOO.bar">
Klick me, I'm nested within a ng-if
</div>
</section>
Use link instead of compile.
link: function(scope, element) {
function checkPermissions() {
alert('foo');
}
element.on('click', function(evt) {
evt.preventDefault();
checkPermissions();
});
}

How to display a button in dependency of the state of a directive

Given the following data which consists of an array of elements, that each have a title and a text:
[
{
title: 'title1',
text: 'text1'
},
{
title: 'title2',
text: 'text2'
}
]
Each entry of the array should be displayed as a list item using ng-repeat. Whereas each item makes use of contenteditable in order to make these fields editable.
I am using a directive to make the elements editable
app.directive("contenteditable", function() {
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attrs, ngModel) {
function read() {
ngModel.$setViewValue(element.html());
}
ngModel.$render = function() {
element.html(ngModel.$viewValue || "");
};
element.bind("blur keyup change", function() {
scope.$apply(read);
});
}
};
});
This is the view which makes use of the contenteditable directive
<div ng-repeat = "entry in contentEditables.entries">
<span contenteditable ng-model="entry.title"></span>
<span contenteditable ng-model="entry.text"></span>
<button ng-if="howToQueryIfOneOfTheContentEditablesHasBeenEdited?">save</button>
</div>
Each row should have a "save" button, which should be only displayed if one or both contenteditable elements of that row have been changed. I am able to track whether the contenteditable directive has changed by giving it its own scope, however I have not been able to find a way to display the save button only when the elements of that row have been changed.
How would one query the directive from within the view? Is there a better way to do something like this?
Plunker can be found here: http://plnkr.co/edit/E8ZC8zwGlO0AjsOoWTss?p=preview
I ended up writing a simple controller to handle that:
app.controller('EditableController', ['$scope', function(scope) {
var initial = {};
angular.copy(scope.entry, initial);
scope.edited = function(val) {
return !angular.equals(val, initial);
};
}]);
And to initialize that controller:
<body>
<div ng-controller="ContentEditableController as contentEditables">
<div ng-repeat = "entry in contentEditables.entries" ng-controller="EditableController">
<span contenteditable ng-model="entry.title"></span>
<span contenteditable ng-model="entry.text"></span>
<button ng-if="edited(entry)">save</button>
</div>
</div>
</body>
Plunker can be found here: http://plnkr.co/edit/E8ZC8zwGlO0AjsOoWTss?p=preview

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/

After $compile controller of new element works but data binding not

I add DOM element (externally to Angular), $compile it and link it with scope in directive's event handler - controller defined in ng-controller of new element fires up, but databinding is not working - the result is
{{data.name}}
like it's not compiled at all ... Why?
(I use $compile for the first time so, maybe I'm missing something)
Below is just directive's code:
app.directive('pages', function ($compile) {
return {
restrict: 'E',
link: function (scope, element) {
element.on('pageLoaded', function(event){
var page = angular.element(event.detail.element);
var linkFn = $compile(page);
scope.data = {
name: 'DATA SET IN DIRECTIVE'
};
linkFn(scope);
});
}
}
});
Page I add (dom element in event.detail.element) is
<div page="AddedPage" ng-controller="PageController">
{{data.name}}
</div>
Here is jsfiddle: http://jsfiddle.net/yoorek/EYCwY/
You didn't add the element to the DOM. See this Fiddle:
scope.$apply(function () {
element.replaceWith($compile(page)(scope));
});
If you want to add multiple pages, append it instead of replacing it.
Furthermore, I recommend reading through this post on the recommendations and concepts of AngularJS.
Add addPage into controller and use ng-click
http://jsfiddle.net/EYCwY/1/
<div ng-app="app" ng-controller="AppController">
<button ng-click="addPage()">Add Page</button>
<pages>
<page page="StaticPage">
</page>
</pages>
app.controller('AppController', function ($scope) {
log('App Controller');
$scope.data = {
name: 'Data set in AppController'
}
$scope.addPage = function () {
var page = document.createElement('div');
var parent = document.getElementsByTagName('pages')[0];
page.setAttribute('page', 'AddedPage');
page.setAttribute('ng-controller', 'PageController');
page.innerHTML = '{{data.name}}';
parent.appendChild(page);
var event = new CustomEvent(
"pageLoaded",
{
detail: {
page: "AddedPage",
element: page
},
bubbles: true,
cancelable: true
}
);
parent.dispatchEvent(event);
};
});

Resources