Call jquery masonry using angularjs - angularjs

i'm trying to get angularjs to work with jquery masonry. Without angular i have managed to get masonry to work fine including css. However, i have used angular to script the whole web app and i now load items dynamically via angular. Since masonry needs to be reloaded after dynamically adding data, i have written a directive to listen to last element of the items in angular repeat so that i can call the jquery masonry. When i call masonry, for some reason the items gets all weird up and does not load when it loaded initially without ng-repeat. Please check the code given below and tell me what im doing wrong here. I wish i had plunkr/js fiddler but since i pull data from a backend i cannot replicate the same.
The library i use is called "masonry.pkgd.js"
<div id="container" class="masonry js-masonry" data-masonry-options='{ "columnWidth": ".grid-sizer", "itemSelector": ".item", "isFitWidth": true }'>
<div class="grid-sizer"></div>-
<div ng-repeat="item in items" class="{{item.class}}" newsitemupdated>
<?php $this->load->view('item') ?>
</div>
</div>
app.directive('newsitemupdated', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
masonryUpdate();
});
}
}
}
});
var masonryUpdate = function() {
var $container = $('#container').masonry({
columnWidth: '.grid-sizer',
itemSelector: '.item',
isFitWidth: true
});
setTimeout(function() {
// $('#container').masonry();
$container.masonry('reloadItems');
console.log('called');
}, 500);
}
This is how it looks below i reload items.
This is how it looks after i reloaditems
Please tell me what im doing wrong and how i can call reload items after ng-repeat to load jquery masonry how its supposed to load.

I have fixed by doing the following
First i write a function to initialize my masonry with options
function BuildMasonry() {
var $container = $('#container').masonry();
$container.masonry({
columnWidth: '.grid-sizer',
itemSelector: '.item',
isFitWidth: true
});
}
Afterwards i will call it when ng-repeat finishes loading.
var masonryUpdate = function() {
$('#container').masonry('reloadItems');
BuildMasonry();
}
Hope this helps to someone out there :)

Related

Create an ajax loader generic directive

I need some help regarding my very first AngularJS project (after the official tutorial).
It started well, since I was able to control my form, submit my ajax request and display the results as I want with a pagination... Isn't that great? But now I try to make an ajax loader. I think it would have been possible using ng-show directive, but I'd like to make it as generic as possible so I can re-use it on other projects.
So I started to create a directive (following this: Angularjs loading screen on ajax request). Now everything works as I want, except that the ajax loader is never displayed.
It seems that my directive doesn't check the loading parameter. I have the feeling I missed something about the scope.
Here's my code (simplified):
(function() {
var app = angular.module('querygen', []);
app.controller('QuerygenController',['$http', function($http){
this.loading=false;
this.getQueries = function(page){
this.loading = true;
querygen = this;
$http.post('/action.php', data).success(function(data){
querygen.loading = false;
}).error(function(data){
querygen.loading=false;
alert('An error occured.');
});
};
}]);
app.directive('loading', function () {
return {
restrict: 'E',
replace:true,
template: '<div class="loading"><img src="images/loader.gif" alt="Loading..." /></div>',
link: function (scope, element, attr) {
scope.$watch('loading', function (val) {
if (val)
element.show();
else
element.hide();
});
}
};
});
})();
My HTML:
<div ng-controller="QuerygenController as querygenCtrl">
<loading></loading>
<div ng-show="!querygenCtrl.loading">
<div ng-repeat="query in querygenCtrl.queries">
{{query}}
</div>
</div>
</div>
Also, once my issue solved, if I put the directive into another module, and add the new module as a dependency of "querygen", will it work as it is?
I post the answer. MapOfVeins was right. Updating this.loading to $scope.loading solved my issue.
There was definitively something I missed regarding this/$scope.
Thank you!

How to force angular to render outside elements from the custom directive?

I need a small help in angularjs,
please have a look on this
code (chrome browser):
http://jsfiddle.net/Aravind00kumar/CrJn3/
<div ng-controller="mainCtrl">
<ul id="names">
<li ng-repeat="item in Items track by $index">{{item.name}} </li>
</ul>
<ak-test items="Items">
</ak-test>
</br>
<div id="result">
</div>
</div>
var app = angular.module("app",[]);
app.controller("mainCtrl",["$scope",function($scope){
$scope.Items = [
{name:"Aravind",company:"foo"},
{name:"Andy",company:"ts"},
{name:"Lori",company:"ts"},
{name:"Royce",company:"ts"},
];
$scope.Title = "Main";
}]);
app.directive("akTest",["$compile",function($compile){
return {
restrict: 'E',
replace: true,
scope: {
items: "="
},
link: function (scope, element, attrs) {
// var e =$compile('<li ng-repeat="item in Items track by $index">{{item.name}} </li>')(scope);
// $("#names").append(e);
var lilength = $("#names li").length;
var html ='<div> from angular ak-test directive: '+lilength+'</div>';
element.replaceWith(html);
}
};
}]);
$(function(){
$("#result").html('from jquery: '+$("#names li").length);
});
I have created a custom directive and trying to access an element from the view which in the ng-repeat above my custom directive
The problem is, in the directive it was saying ng-repeat not rendered yet.
Here is the problem
I have two elements
<svg>
<g>
List of elements
</g>
<g>
Based on the above rendered elements I have to draw a line between elements like a connection. I have to wait till the above elements to get render then only I can read the x,y positions and can draw a line.
</g>
</svg>
Both elements and the connections are scope variables. As per my understanding both are in the same scope and execution flow starts from parent to child and finishes from child to parent. How can I force above ng-repeat rendering part to complete before starting the custom directive?
is there any alternative available in angular to solve this dependency?
It's been a while, so my Angular is getting a bit rusty. But if I understand your problem correctly, it's one that I have run into a few times. It seems that you want to delay processing some elements of your markup until others have fully rendered. You have a few options for doing this:
You can use timeouts to wait for the page to render:
$timeout(function() {
// do some work here after page loads
}, 0);
This generally works ok, but can cause your page to flash unpleasantly.
You can have some of your code render in a later digest cycle using $evalAsync:
There is a good post on that topic here: AngularJS : $evalAsync vs $timeout. Typically, I prefer this option as it does not suffer from the same page flashing issue.
Alternatively, you can look for ways to refactor your directives so that the dependent parts are not so isolated. Whether that option would help depends a lot on the larger context of your application and how reusable you want these parts to be.
Hope that helps!
I would create a directive for the whole list, and maybe a nested directive for each list item. That would give you more control I would think.
Thanks a lot for your quick response #Royce and #Lori
I found this problem causing because of ng-repeat I have solved it in the following way..
Created a custom directive for list elements and rendered all elements in a for loop before the other directive start. This fix solved the problem temporarily but i'll try the $evalAsync and $timeout too :)
var app = angular.module("app",[]);
app.controller("mainCtrl",["$scope",function($scope){
$scope.Items = [
{name:"Aravind",company:"foo"},
{name:"Andy",company:"ts"},
{name:"Lori",company:"ts"},
{name:"Royce",company:"ts"},
];
$scope.Title = "Main";
}]);
app.directive("akList",["$compile",function($compile){
return {
restrict: 'A',
replace : false,
link: function (scope, element, attrs) {
var _renderListItems = function(){
$(element).empty();
for(var i=0;i<scope.Items.length; i++)
{
var li ='<li> '+ scope.Items[i].name +' </li>';
element.append(li);
}
};
_renderListItems(scope);
scope.$watch('Items.length', function (o, n) {
_renderListItems(scope);
}, true);
}};}]);
app.directive("akTest",["$compile",function($compile){
return {
restrict: 'E',
replace: true,
scope: {
items: "="
},
link: function (scope, element, attrs) {
var lilength = $("#names li").length;
var html ='<div> from angular ak-test directive: '+lilength+'</div>';
element.replaceWith(html);
}
};
}]);
$(function(){
$("#result").html('from jquery: '+$("#names li").length);
});

Perform task after model's DOM is displayed in view

I have a code snippet in my content which is a model fetched from http. I am using syntax highlighter to prettify the code. So I need to call a javascript function as soon as the DOM is updated for that particular model.
Here is a sample code to make it clear. I am using alert to demonstrate it. In my project I would use a third party plugin which will find matching dom elements and remodel them.
Here,
I want the alert to occur after the list is displayed
jsfiddle :
http://jsfiddle.net/7xZde/2/
My controller has something like this.
$scope.items = Model.notes();
alert('test');
alert comes even before the items list is shown, I want it after the list is displayed.
Any hint to help me achieve this.
We need to use $timeout ,
$scope.items = Model.notes();
$timeout(function () {
alert('test');
})
Yeah it was silly , $timeout seemed to be a misnomer to me. I am 2 days old to angularjs . Sorry for wasting your time.
Lucky for you, I wanted to do the exact same thing. Mutation observers are the path forward, but if you need backwards compatibility with older browsers, you'll need a bit more code than this.
Working plunker for Firefox, Chrome, and Safari.
Javascript:
var app = angular.module('plunker', [])
.controller('MainCtrl', function($scope) {
$scope.name = 'World';
})
.directive('watchChanges', function ($parse, $timeout) {
return function (scope, element, attrs) {
var setter = $parse(attrs.watchChanges).assign;
// create an observer instance
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
$timeout(function () {
var text = angular.element('<div></div>').text(element.html()).text();
setter(scope, text);
});
});
});
// configuration of the observer:
var config = {
attributes: true,
childList: true,
characterData: true,
subtree: true
};
// pass in the target node, as well as the observer options
observer.observe(element[0], config);
};
});
HTML:
<body ng-controller="MainCtrl">
<div watch-changes="text">
<p>Hello {{ name }}</p>
<label>Name</label>
<input type="text" ng-model="name" />
</div>
<pre>{{text}}</pre>
</body>

Angular, ng-repeat to build other directives

I'm building a complex layout, that takes a JSON document and then formats it into multiple rows, with each row then having more rows and/or combinations of rows/columns inside them.
I'm new to Angular and am just trying to get to grips with Directives. They are easy to use for very simple things, but quickly become very difficult once you need to anything more complicated.
I guess I'm doing this the wrong way around, but is there a way to simply add the name of a directive (in the example below, I've used ) and get that directive to be rendered on an ng-repeat?
Maybe the same way that you can use {{{html}}} instead of {{html}} inside of mustache to get a partial to render as HTML and not text.
As expected, the example below simply writes the name of the directive into the dom. I need Angluar to take the name of the directive, understand it, and then render before before it is written. Due to the complex layout of the page I need to design, I could be rendering many different directives, all inside each other, all from 1 JSON document (which has been structured into different rows and then row / column combinations).
Example code that renders the name of the directive to the page, but gives you an idea of how I'd like to write a solution the problem...
<div app-pages></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
<script>
var app = angular.module("app", ['main']);
angular.module('main', [])
.controller("appPageController", ['$scope', function( $scope ){
$scope.pages = [];
var page1 = {
title: 'Page 1',
directive: '<app-page-type-1>'
};
var page2 = {
title: 'Page 2',
directive: '<app-page-type-2>'
};
$scope.pages.push(page1);
$scope.pages.push(page2);
}])
.directive("appPageType2", function factory() {
console.log('into page type 2');
return {
replace: true,
template: 'This is the second page type'
};
})
.directive("appPageType1", function factory() {
console.log('into page type 1');
return {
replace: true,
template: 'This is the first page type'
};
})
.directive("appPages", function factory() {
console.log('into pages');
return {
replace: true,
template: '<ul><li ng-repeat="page in pages">{{page.directive}}</li></ul>'
};
});
</script>
This is one possible alternative to your idea. The idea is to append the directive you defined in page object for each html element inside the ng-repeat. Please take a look at the demo. Hope it helps.
<div ng-app="myApp" ng-controller="appPageController">
<ul>
<li ng-repeat="page in pages" app-pages></li>
</ul>
</div>
.directive("appPages", function ($compile) {
console.log('into pages');
return {
replace: true,
link: function (scope, elements, attrs) {
var html = '<div ' + scope.page.directive + '></div>';
var e = angular.element(html);
elements.append(e);
$compile(e)(scope);
}
};
});
Demo

unable to make custom html with angular tags work with select2

I am using angular-ui's ui-select2. I want to add custom html formatting to the selections. Select2 allows this by specifying the formatSelection in its config.
I have html with angular tags as below that I want to use for formatting the selection-
var format_code = $compile('<div ng-click="showHide=!showHide" class="help-inline"><div style="cursor: pointer;" ng-show="!!showHide" ng-model="workflow.select" class="label">ANY</div><div style="cursor: pointer;" ng-hide="!!showHide" ng-model="workflow.select" class="label">ALL</div></div>')( $scope );
var format_html = "<span>" + data.n + ' : ' + data.v +' ng-bind-html-unsafe=format_code'+ "</span>"
$scope.select_config = {
formatSelection: format_html
}
If I compile the html as in above and assign it, I just see an [object,object] rendered in the browser. If I dont compile it, I see the html rendered properly, but the angular bindings dont happen, ie the clicks dont work.
Any ideas what is wrong?
I had the same problem, select2 loading in a jquery dialog and not using the options object I would give it.
What I ended up doing is isolating the element in a directive as following:
define(['./module'], function (module) {
return module.directive('dialogDirective', [function () {
return {
restrict: 'A',
controller: function ($scope) {
console.log('controller gets executed first');
$scope.select2Options = {
allowClear: true,
formatResult: function () { return 'blah' },
formatSelection: function () { return 'my selection' },
};
},
link: function (scope, element, attrs) {
console.log('link');
scope.someStuff = Session.someStuff();
element.bind('dialogopen', function (event) {
scope.select2content = MyResource.query();
});
},
}
}]);
and the markup
<div dialog-directive>
{{select2Options}}
<select ui-select2="select2Options" style="width: 350px;">
<option></option>
<option ng-repeat="item in select2content">{{item.name}}</option>
</select>
{{select2content | json}}
</div>
What is important here:
'controller' function gets executed before html is rendered. That means when the select2 directive gets executed, it will already have the select2Options object initialized.
'link' function populates the select2content variable asynchronously using the MyResource $resource.
Go on and try it, you should see all elements in the dropdown as "blah" and selected element as "my selection".
hope this helps, that was my first post to SO ever.

Resources