angular and isotope - Adding new item within isotope context - javascript

UPDATE: After some very insightful code from #Marc Kline, I went back and cleaned up my page. It turned out that I had my controllers listed in reversed (My angular controller was inside the Isotope controller, instead of the other way round). Once I changed it back and cleaned off some additional scripting, it started working again. I have updated the code snippet to reflect the change. Thanks to Marc and S.O!
I am having trouble figuring out how can I add new items using Angular and still let Isotope manage their UI display.
I am using Isotope and Angular to render server results in a masonry style layout. When I add new items to the layout on a button click, angular adds it just fine. However, they do not appear in the context of the isotope UI and appear separately (and cannot be sorted, laid out or filtered using Isotope).
Here is my JS Code
<!-- Define controller -->
var contrl = app.controller("MainController", function($scope){
$scope.items ={!lstXYZ}; //JSON data from server
//Function called by button click
$scope.addItem = function(item)
{
$scope.items.push(item);
$scope.item = {};
}
});
contrl.$inject = ['$scope'];
Here is the HTML to display the server results...(Updated to show working code..refer comments)
<div ng-controller="MainController">
<div class="isotope" id="isotopeContainer">
<div ng-repeat="item in items">
<div class='element-item {{item.status}}' data-category='{{item.status}}'>
<p class="number">{{item.type}}</p>
</div>
</div>
</div>
</div>
And here is my HTML button to add the new items
<table>
<tr>
<td><input type="text" ng-model="item.status" /></td>
</tr>
<tr>
<td><input type="text" ng-model="item.type" /></td>
</tr>
<tr>
<td colspan="2"><input type="Button" value="Add" ng-click="addItem(item)" /> </td>
</tr>
</table>
I am not sure how do I ensure that Isotope can recognize the newly added element and re-animate the layout.
Any help or pointers will be very appreciated. Thanks!

ng-repeat takes care of adding the new element to the DOM for you. However, Isotope isn't doing any "watching" for you - you have to manually invoke a redraw of the container.
You could just add something like $("#isotopeContainer").isotope(...) directly to your controller, but in the spirit of keeping your controllers lean and free of DOM-related code, you should instead create a service. Something like:
myApp.service('Isotope', function(){
this.init = function(container) {
$(container).isotope({itemSelector: '.element-item'});
};
this.append = function(container, elem) {
$(container).isotope('appended', elem);
}
});
... where the first method initializes a new Isotope container and the next redraws the container after an item is appended.
You could then inject this service into any controller or directive, but directives probably are best for this scenario. In this Plunker, I created two new directives: one for Isotope containers and another for Isotype elements, and used the service to do the initialization and redrawing.

In this particular case, my code was not written correctly. I have updated the question's code but wanted to mention it more clearly here...
Apparently, the beauty of Angular is that you do not need to bother with the underlying UI framework (Isotope in this case). As long as you update the Angular data array, the UI binding is updated automatically.
The only gotcha is to ensure that the UI framework div is within the context of your Angular div.
Here is the non-working code...Note that the isotope div is outside the Angular controller.
<div class="isotope" id="isotopeContainer">
<div ng-controller="MainController">
<div ng-repeat="item in items">
<div class='element-item {{item.status}}' data-category='{{item.status}}'>
<p class="number">{{item.type}}</p>
</div>
</div>
</div>
</div>
Here is the updated code with isotope running within the Angular controller context...
<div ng-controller="MainController">
<div class="isotope" id="isotopeContainer">
<div ng-repeat="item in items">
<div class='element-item {{item.status}}' data-category='{{item.status}}'>
<p class="number">{{item.type}}</p>
</div>
</div>
</div>
</div>
Hope this helps. I am thankful for all the responses and help I got from SO. Appreciate the learning opportunity.

Related

ng-click not updating value in ng-repeat

This is the code:
<div ng-repeat="data in products">
<div class=edit ng-click="dataUI.showEdit = true; content = data;">
</div>
<div ng-repeat="renew in data.renewed">
<div class=edit ng-click="dataUI.showEdit = true; content = renew;">
</div>
</div>
</div>
<div class="modal" ng-show="dataUI.showEdit">
<div class="product_price">{{content.product_price}}</div>
</div>
When I click this, the popup opens but, the content is not filled with items. In the popup, I am using content to show the data.
What am I doing wrong here?
JSFiddle: http://jsfiddle.net/HB7LU/22082/
Here's your fiddle fixed: http://jsfiddle.net/masa671/xtaa9gev/
You were using an old version of Angular, changed to version 1.4.8 (see the JavaScript Gear).
Then, a couple of missing injections:
MyCtrl.$inject = ['$scope'];
myApp.controller('MyCtrl', MyCtrl);
Finally, assignment to content in ng-click did not work, because ng-repeat creates a new scope ("Always use a dot"). I fixed this with dataUI.content. Here is one good explanation: Ng-click doesn't work inside ng-repeat.

Can I use a JQuery plugin from within an angularjs directive?

I'm quite new to javascript in general. I've spent the past couple of weeks building a front end with Angular.js.
I have a number of directives I've defined that sit on my page, Angular has been great for this.
Here's what my main page looks like:
<body class="body" ng-controller="OverviewController as overview" font-size:1em>
<sidebar-menu ng-controller="PanelController as panel"></sidebar-menu>
<div id="content" >
<div>
<div class="list-group">
<div class="list-group-item" ng-repeat="site in overview.sites" ng-click="">
<div class="item-heading">
<h3>{{site.name}}</h3>
<p>Address: {{site.address}}</p>
Click Here
</div>
<installationsite-panels ng-controller="PanelController as panel"></installationsite-panels>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
$('.paulund_modal').paulund_modal_box();
});
</script>
</body>
Note the javascript function to call a modal box at the bottom, using this tutorial.
I've spent the past few days trying different tutorials to get modals to work in my webapp, but with no success. I think it's down to my lack of understanding of Angular and Javascript in general.
In any case, I've managed to get this tutorial to work using JQuery, and when I click on the link, the modal opens as expected.
However, I don't want to call this modal from here. I want to call it from a directive that's embedded within the <installationsite-panels> directive in the above code, which looks like this (just a single section shown here):
Device Statuses
<div>
<div class="device-icon-group">
<div class="device-type1-icons" ng-click="panel.showDevices(3)" ng-show="showtype1Red"><img src="img/type1red.png" style="width:50%; height:50%;"/></div>
<div class="device-type2-icons" ng-click="panel.showDevices(3)" ng-show="showType2Red"><img src="img/type2red.png" style="width:50%; height:50%;" /></div>
</div>
<div class="service" ng-click="panel.showDevices(3)" ng-show="showService">
<b>{{panel.getServiceDeviceCount()}} device needs servicing</b>
</div>
<div ng-show="showServiceList">
<device-list-service></device-list-service>
</div>
</div>
The directive <device-list-service> shows a list of items like so:
<div ng-controller="DevicesController as deviceList" font-size:1em >
<div id="device-list-group">
<div id="device-list-group-item" ng-click="" ng-repeat="device in deviceList.devicesService">
<div ng-class="device.status"><img src="{{(device.type == 'type1') ? 'img/type1white.png' : 'img/type2white.png'}}"> </div>
<div class="device-params">
<b>ID: </b> {{device.id}}<br />
<b>Type: </b> {{device.type}}
</div>
<div class="device-params">
<b>Location: </b> {{device.location}}<br />
<b>Action: </b> {{device.action}} <br />
</div>
</div>
</div>
</div>
I want to show the modal when the user clicks on one of the list-group-item 's, and display some data relating to that item.
The modal works fine from the top level in the main app, but I cannot call it from within any of the directives. How can I do this?
Is it possible, or do I need to scrap my JQuery modal and do it the Angular way, which hasn't worked for me for the past few attempts.
Don't use jquery modals. You can, but you shouldn't.
Instead, I recommend using Angular UI, which has a pretty usable modal implementation: https://angular-ui.github.io/
Second alternative: if you don't like Angular UI, then use AngularJS + Bootstrap, and create your own custom directives
Third alternative: Use jQuery.
If you still want to go with the 3rd alternative, despite my advice against it, then here is how you do it:
var app = angular.module('app', []);
app.directive('modal', function($http, $timeout) {
return {
restrict: 'A',
link: function(scope, element, attr) {
$timeout(function() {
element.paulund_modal_box();
}, 0, false);
}
};
});
Usage:
<div modal></div>
Some explanation is needed here.
Why is the $timeout service necessary? jQuery plugins often require the DOM to be fully loaded in order to work properly. That is why most jQuery plugins are wrapped inside of a $(document).ready block. In AngularJS there is no concept of DOM ready, and there is no easy way in AngularJS to hook into the event. However, there is a well-known hack, which is to use the $timeout service. In Angular there are three phases:
1. compile - Angular walks the DOM tree looking for directives
2. Link - Angular calls the link function for each directive to setup watch handlers.
3. Render - Angular updates the views
Using $timeout within the Link function queues the $timeout function to be executed until after the current closure is done executing. It just so happens that the Render phase is within the current closure's scope of execution. Hence, the $timeout function will execute after the render phase, when the DOM has been loaded.
Mixing JQuery and Angular in that way is maybe a little messy, but sometimes you do want to use a well-built component. You could try to find a similar modal in Angular - angular-modal - or you could try and build the component into your Angular directive itself - jQuery Plugins in AngularJS

In AngularJS how can I create a new ngController dynamically?

I'm trying to create new ngControllers on the fly, is this possible? I'm not writing it into the template with a ngRepeat or anything, since the user might never create an instance of a certain ngController.
Here's an example of my current template:
<div ng-controller="ViewUsers">
<div class="viewUsersList">
<table>
<tr class="userRow" ng-click="viewUser(user.user_id)" ng-repeat="user in users">
<td>{{user.first_name}} {{user.last_name}}
<br />{{user.phone}}
</td>
</tr>
</table>
</div>
</div>
So far it works great. When the user clicks a .userRow it calls viewUser(ID). The problem is here:
I want to create a new block of code like this:
<div ng-controller="UserDetail">
{{first_name}}
</div>
And append this to the DOM.
So if they click on Bobby and Sally, two UserDetail controller objects would be added to the DOM and work accordingly. (Ideally, I'd like to pass in the data-bound user model, but that's for later).
I tried a ghetto version in JSBin, but I'd rather not use JQuery if possible:
http://jsbin.com/EdOteTav/2/edit
Can anyone shine some light on this? Thanks in advance
I would change the code little bit and use directive instead of controller. See this simple and quick example in jsfiddle.
The directive would handle the click events and display whatever you want to display on the DOM. You can get the full user object from scope.user inside the directive.

Sort or Rearrange Rows of a table in angularjs (drag and drop)

I wanted to have the functionality of rearranging rows in a table (sorting rows using drag and drop).
And the index of the row arrangement should also change in the model.
How can I do something similar to this : http://jsfiddle.net/tzYbU/1162/
using Angular Directive?
I am generating table as :
<table id="sort" class="table table-striped table-bordered">
<thead>
<tr>
<th class="header-color-green"></th>
<th ng-repeat="titles in Rules.Titles">{{titles.title}}</th>
</tr>
</thead>
<tbody ng-repeat="rule in Rules.data">
<tr>
<td class="center"><span>{{rule.ruleSeq}}</span></td>
<td ng-repeat="data in rule.ruleData">{{statusArr[data.value]}}</td>
</tr>
</tbody>
</table>
I did it. See my code below.
HTML
<div ng:controller="controller">
<table style="width:auto;" class="table table-bordered">
<thead>
<tr>
<th>Index</th>
<th>Count</th>
</tr>
</thead>
<tbody ui:sortable ng:model="list">
<tr ng:repeat="item in list" class="item" style="cursor: move;">
<td>{{$index}}</td>
<td>{{item}}</td>
</tr>
</tbody>{{list}}
<hr>
</div>
Directive (JS)
var myapp = angular.module('myapp', ['ui']);
myapp.controller('controller', function ($scope) {
$scope.list = ["one", "two", "thre", "four", "five", "six"];
});
angular.bootstrap(document, ['myapp']);
There is another library: RubaXa/Sortable: https://github.com/RubaXa/Sortable
It is for modern browsers and without jQuery dependency. Included is a angular directive. I'm going to check it out now.
You get good touch support additionally.
AngularJS was not really built for the manipulation of DOM elements, rather to extend the HTML of a page.
See this question and this Wikipedia entry.
For DOM manipulation, jQuery/mootools/etc will suite you just fine (hint: the example in your jsFiddle link).
You could probably use AngularJS to keep track of the ordering of your elements to update your model. I'm not sure how to do this using directives, but the following code may be useful
var MyController = function($scope, $http) {
$scope.rules = [...];
...
}
var updateRules = function(rule, position) {
//We need the scope
var scope = angular.element($(/*controller_element*/)).scope(); //controller_element would be the element with ng-controller='MyController'
//Update scope.rules
}
Then when you reorder the list, simply call updateRules() with the changed rule and its new position in the model.
Anyone else who wants something like this but not understanding the accepted answer. Here is a directive UI.Sortable for AngularJS that allows you to sort an array/ table rows with drag & drop.
Requirements
JQuery v3.1+ (for jQuery v1.x & v2.x use v0.14.x versions)
JQueryUI v1.12+
AngularJS v1.2+
Usage
Load the script file: sortable.js in your application: (you can find this sortable.js from here
<script type="text/javascript" src="modules/directives/sortable/src/sortable.js"></script>
make sure you have included JQuery, AngularJs and JQueryUI js files in
order before this sortable file
Add the sortable module as a dependency to your application module:
var myAppModule = angular.module('MyApp', ['ui.sortable'])
Apply the directive to your form elements:
<ul ui-sortable ng-model="items">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
Developing Notes:
ng-model is required, so that the directive knows which model to update.
ui-sortable element should contain only one ng-repeat
Filters that manipulate the model (like filter, orderBy, limitTo,...) should be applied in the controller instead of the ng-repeat
3rd point is very Important as it took almost an hour to understand
why my sorting was not working?? It was because of orderBy in html
and that was resetting the sorting again.
For more understanding you can check the detail here.
If I understand you correctly, you want to be able to sort the rows? If so, use UI-Sortable: GitHub
Demo: codepen.io/thgreasi/pen/jlkhr
Along with RubaXa/Sortable there is one more angularjs library avilable that is angular-ui-tree. Along with drag and drop we can arrange elements in a tree structure and we can add and delete elements (nodes)
Please see the this link for examples
http://angular-ui-tree.github.io/angular-ui-tree/#/basic-example .
Please see this for github
https://github.com/angular-ui-tree/angular-ui-tree.

viewContentLoaded - Without duplicating code per each controller - AngularJS

Hey all I'm trying to get a feel for angular and have ran into a little snag.
I have a container structure like the following:
<div class="main-container" ng-view>
<!-- The below divs are constantly being
reloaded based on the current URL and it's associated view -->
<div class="left-col">
</div>
<div class="right-col">
</div>
</div>
Before I implemented Angular I just had a simple script that would check the height of the window and set the height of the left column and right column divs accordingly.
With angular there is probably a better way to do this then attaching an event function to the window object. Basically I want to fire a function everytime a new view is rendered but not duplicate the below code in all of my angular controllers like so:
$scope.$on('$viewContentLoaded', setColumnHeight);
Any help would be appreciated. Thanks!
I've solved this problem in my app by having a root "Application Controller" at the top of the DOM tree. For example:
<body ng-controller='applicationController'>
...
<div class="ng-view"></div>
...
</body>
applicationController is always there, and can set up bindings on its scope when it is created.
How about putting that event listener on the root scope:
$rootScope.$on('$viewContentLoaded', setColumnHeight);
You could use something like this:
<div class="main-container" ng-view>
{{ setColumnHeight() }}
<!-- The below divs are constantly being
reloaded based on the current URL and it's associated view -->
<div class="left-col">
</div>
<div class="right-col">
</div>
</div>
and it will check the height every time a render is made, however, this seems to be a bit over processing.
The best approach would be to only update the height from both columns when the height of one of them changes. For that you could use DOM Mutation Observers, however they are not yet available on all browsers. If that's not a problem for you, check the mutation-summary lib.
If you are using jQuery you can also try this: https://github.com/jqui-dot-net/jQuery-mutate (examples here).

Categories

Resources