Let's say I have something like this:
<div ng-repeat="item in list">
<div change-color>Some content</div>
</div>
Where the directive "changeColor" can change the background color of the div by clicking it.
If I now change the content of my list, say by a HTTP-request which overwrites the whole list:
$scope.list = newList;
My "changeColor" directive "reloads", since the divs will be repopulated, is it possbile, by some angular-way to preserve the state of the div (by which the directive has changed it) or do I need to do it manually?
P.S.
This would be really useful since in my situation I have a huge list of items where the user wants to interact with each item in a way like above, but the list is overwritten each 30 seconds and that clears all the "changes" to it's pristine state.
Related
I have a ng-repeat that lists users and uses a custom directive.
My custom directive has some event handlers like click handlers etc.
This vm.UserList will get modified every time a drop down list value is changed in the UI. My question is, will the event handlers get updated whenever the UserList is modified by a drop down list?
The dropdownlist on change will make a remote API request and then set the JSON result of the users to the value of the UserList.
<div ng-repeat="u in vm.UserList">
<div><div my-custom-directive user-id="{u.id}" >{u.email}</div></div>
</div>
</div>
Yes, it does. According to angularjs documentation:
ngRepeat uses $watchCollection to detect changes in the collection.
When a change happens, ngRepeat then makes the corresponding changes
to the DOM:
1- When an item is added, a new instance of the template is added to the
DOM.
2-When an item is removed, its template instance is removed from
the DOM.
3-When items are reordered, their respective templates are
reordered in the DOM.
So your directive gets added to the page including all of the event handlers.
I have my code on Plnkr:
http://plnkr.co/edit/6wz4zub2sEOfhrqps0c8
The html code is quite simple:
<div ng-app="myApp" ng-controller="MainCtrl">
<div ng-dropdown-multiselect="" options="myData" selected-model="myModel" extra-settings="mySettings" events="myEvents"></div>
<div ng-repeat="item in Items">
<item-input></item-input>
</div>
</div>
</div>
Basically, the problem is my function in ng-if gets called every time I click anywhere on the page. I think it has something to do with the multi-select control but I'm not sure where I should fix it. Any help is appreciated.
Thanks.
UPDATED
What I don't really understand is why this behavior stops when I comment out the multi-select dropdown (<div ng-dropdown-multiselect="" options="myData" selected-model="myModel" extra-settings="mySettings" events="myEvents"></div>)?
As shown in the picture below, taken from Chrome DevTools, the multi-select dropdown is generating two click event handlers :
One event is on the button and one on the whole document. Therefore, a click on the document is triggering at least that last event handler, which potentially changes the model. Angular then do a digest cycle, and that implies that the displayMe function bound to the ngIf directive is evaluated in case the element should be removed of the DOM or not.
If you remove the dropdown component, and thus these two click handlers, you are left with 3 text inputs. Indeed, now there are no digest after a click on the document because no handler are executed, but you can still trigger digests essentialy by typing inside the input elements.
This answer by Miško Hevery has some valuable info about the dirty-checking process in Angular 1.x.
Think about this simple scenario:
<div ng-controller="ctrl1">
... drop down list...
</div>
<div ng-controller="ctrl2">
...grid list...
</div>
The first <div> contains a dropdown and the second <div> contains grid.
According to the selected item in dropdown, ctrl2 should update the grid with new data. This is working great after loading, I fire event and catch it using $broadcast and $on.
BUT, I am having trouble doing the first load of the grid. The dropdown list loads with a list of items and the first item is selected by default, so the grid should show data according to that default selection. I can't use events here because if I fire event on $init of ctrl1, the $on of ctrl2 didn't initialized yet so it will not work. What is the right way to do this first load?
Please advice.
Using AngularJS, I would like to animate an element being added to one list from another, as in a clone of the element should appear to move in the page from the menu of items, and be added to the target list.
<ul class="list">
<li ng-click="choose(item)" ng-repeat="item in originItems">{{item.name}}</li>
</ul>
<ul class="list">
<li ng-repeat="item in targetItems track by $index">{{item.name}}</li>
</ul>
Where the function that adds the items does a simple push
$scope.choose = function(item) {
$scope.targetItems.push(item);
}
You can see this in action (with no animation) at this Plunker
I've considered custom directives and events, but not really got anywhere. What's a good structure of directives and/or events to allow this movement animation be achieved, keeping this a separate from the business of adding to a list as possible?
Note: my exact situation in terms of business logic and animation is a bit different, but I'm hoping the solution to this is flexible enough to allow some changes/additions to what happens in the menu, during the "move" animation, and what happens in the target list.
Demo http://plnkr.co/edit/XYnf7U?p=preview
I removed list style to ensure proper position of list item while moving. If you have to enable list style, you have to consider that.
The demo works by: when user add an item, it get the position of original and final items, and apply a jquery animation to the cloned list item between those two positions.
Use $event.target to identify original item.
I want to filter an array based on even and odd $index property and I want to add odd elements in a separate div and even elements in another div, I could easily do this in JavaScript or jQuery, but I wanted to know the correct way to implement the same in AngularJS.
This is what I've done so far:
<div ng-app>
<div ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
<div class="row" ng-repeat="name in names">
<div class="col-xs-6" ng-show="(($index + 1)%2) == 0">{{name}}</div>
<div class="col-xs-6" >{{name}}</div>
</div>
</div>
</div>
It shows empty spaces in place of odd elements in first case, and when I add the odd expression for second div, the elements in the second div disappear and appear in first div. Here is the fiddle for the same issue.
Please advise.
EDIT:
I have got what I wanted, I split the main array into two different arrays odd and even array; this fiddle may be useful for people who stumble upon the same issue in the future.
Since Angular 1.2.0-rc1, ngRepeat exposes the $odd and $even properties on the local scope. You can simply use an ngSwitch based on one of this properties:
<div ng-repeat="item in itemsList" ng-switch="$even">
<div ng-switch-when="true">{{item}} is even</div>
<div ng-switch-default>{{item}} is odd</div>
</div>
Notice that if you just want to change the class based on the parity, ngClassOdd and ngClassEven are available since 1.0.0:
<div ng-repeat="item in itemsList" ng-class-even="'even'" ng-class-odd="'odd'">
{{item}}
</div>
All-in-one Fiddle
I think you're going to have some trouble with what you're looking to do, due to the way ng-repeat works. It generates a new scope for each iteration of the repeat. So you'll end up with two divs each time through the loop, one visible and one hidden.
To explain the weird behavior you've been seeing:
In your first case, the loop is alternating between showing two divs and showing one div. In a row that displays only one div, it understandably looks like a blank was inserted in the second column.
In the second case, the ng-repeat loop alternates between showing the first div and the second one. Without some CSS styling to show the difference, it looks like one div. You can make it pop by adding a style such as text-info to one of the divs.
I hope that explains what's going on behind the scenes, but the better question is... what are you trying to accomplish with the two divs?
Update: Since it looks like your goal is to take that array and pump it out into a 2-column layout, you can probably do away with your row div and your 2 column divs. Instead, try repeating a single 6-width column div. It'll handle the row wrapping for you every other column. You can use odd and even styling as #Blackhole pointed out if you want to differentiate the divs. I've got an example fiddle set up here that may be of use.