AngularJS get property dynamically - angularjs

This should be a really simple question.
Is it possible to get a property value dynamically like this:
<div tabset>
<div tab ng-repeat="item in teamController.range track by $index">
<div tab-heading>
<div class="selected-colour" ng-class="{ 'no-colour-selected': !controller.kit['colour' + $index + 1] }" ng-style="{ 'background-color' : '#' + controller.kit['colour' + $index + 1] }"></div> {{ controller.kit['colour' + $index + 1] }}
</div>
<div class="picker colour-picker">
<ul class="picker-dropdown list-inline">
<li ng-repeat="colour in teamController.colours" ng-class="{ 'active': controller.kit['colour' + $index + 1] === colour.hex }">
<a href style="background-color: #{{ colour.hex }};" ng-click="teamController.setColour(controller.kit['colour' + $index + 1], colour)"></a>
</li>
</ul>
</div>
</div>
</div>
hopefully you can see that my model has 3 properties:
Colour1
Colour2
Colour3
And in my repeat I am trying to get each of them by doing
controller.kit['colour' + $index + 1]
Update
So I have changed my view to this:
<div tabset>
<div tab ng-repeat="item in teamController.range track by $index">
<div tab-heading>
<div class="selected-colour" ng-class="{ 'no-colour-selected': !controller.kit['colour' + ($index + 1)] }" ng-style="{ 'background-color' : '#' + controller.kit['colour' + ($index + 1)] }"></div> {{ controller.kit['colour' + ($index + 1)] }}
</div>
<div class="picker colour-picker">
<ul class="picker-dropdown list-inline">
<li ng-repeat="colour in teamController.colours" ng-class="{ 'active': controller.kit['colour' + ($index + 1)] === colour.hex }">
<a href style="background-color: #{{ colour.hex }};" ng-click="teamController.setColour(controller.kit['colour' + ($parent.$index + 1)], colour)"></a>
</li>
</ul>
</div>
</div>
</div>
In my controller I have this:
// Set our colours
self.setColour = function (item, colour) {
// Set the colour
item = colour.hex;
console.log(item);
console.log(kit);
// Store our model in the session
configuratorService.saveToSession(kit);
};
It doesn't update the kit.
But if I change the setColour invocation to
teamController.setColour(controller.kit['colour' + ($parent.$index + 1)], colour)
and then change my controller function to this:
// Set our colours
self.setColour = function (item, colour) {
// Set the colour
item.colour1 = colour.hex;
console.log(item);
console.log(kit);
// Store our model in the session
configuratorService.saveToSession(kit);
};
everything works fine.
I have also tried using teamController.setColour(controller.kit['colour' + ($index + 1)], colour) and this didn't work.
Does anyone know why?

You need to wrap $index + 1 inside round brackets to evaluate it first before concatenating the string.
Additionally You need to use $parent notation while you wanted to access the $index of parent ng-repeat
ng-click="teamController.setColour(controller.kit['colour' + ($parent.$index + 1)], colour)"

This was a weird one to solve.
I had to change the way my function worked. I changed my function to this:
// Set our colours
self.setColour = function (propertyName, colour) {
// Set the colour
kit[propertyName] = colour.hex;
// Store our model in the session
configuratorService.saveToSession(kit);
};
and my HTML to this:
<div class="picker colour-picker">
<ul class="picker-dropdown list-inline">
<li ng-repeat="colour in teamController.colours" ng-class="{ 'active': controller.kit['colour' + ($index + 1)] === colour.hex }">
<a href style="background-color: #{{ colour.hex }};" ng-click="teamController.setColour('colour' + ($parent.$index + 1), colour)"></a>
</li>
</ul>
</div>
For some reason I found that if I tried to pass the actual property, although it updated the property it did not update all references (as if dynamic properties in views are always treated as copies). Doing it this new way fixed the problem.

Related

Divider into ng-repeat for random number of iterations

I need to create an ng-repeat iteration with divider in it. The problem is that I have to add divider after every two, three or four elements randomly. I started with this code:
<li repeat-start="person in persons track by $index">
<p>{{ person.name }}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % 2 === 0">
<p>--divider--</p>
</li>
It works great when I specify the exact value of 2 inside ng-if. It shows me result like that.
Person 1
Person 2
--divider--
Person 3
Person 4
--divider--
Person 5
etc...
But how to specify this coefficient dynamically? I added a function to genereate a random number among 2, 3 and 4.
$scope.getNumber = function() {
return Math.floor(Math.random() * (3) + 2);
}
But when I try to change hardcoded value I cannot see the desirable result. None of these solutions works. Neigher with simple function call.
ng-if="($index + 1) % getNumber() === 0"
nor with ng-init variations.
<li repeat-start="person in persons track by $index" ng-init="coeff = getNumber()">
<p>{{ person.name }}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % coeff === 0">
<p>--divider--</p>
</li>
How to achieve this functionality?
The problem was with Infinite $digest Loop, which was caused by fact, that getNumber function returns random results, so AngularJS can't stabilize itself. To calculate getNumber only once for each ng-repeat iteration, you can use ng-init directive, as you already did (may be your code doesn't work due to typo: repeat-start instead of ng-repeat-start):
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.persons = [];
for(var i = 0; i < 20; i++)
$scope.persons.push({name:'Person ' + i});
$scope.getNumber = function() {
return Math.floor(Math.random() * 3 + 2);
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app='app' ng-controller='ctrl'>
<li ng-init='temp = getNumber()' ng-repeat-start="person in persons">
<p>{{person.name}}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % temp === 0">
<p>--divider--</p>
</li>
</ul>
Another solution is to "cache" results of getNumber function, for example at person entity:
angular.module('app', []).controller('ctrl', ['$scope', function($scope) {
$scope.persons = [];
for(var i = 0; i < 20; i++)
$scope.persons.push({name:'Person ' + i});
$scope.getNumber = function(person) {
return person.temp || (person.temp = Math.floor(Math.random() * 3 + 2));
}
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<ul ng-app='app' ng-controller='ctrl'>
<li ng-repeat-start="person in persons">
<p>{{person.name}}</p>
</li>
<li ng-repeat-end ng-if="($index + 1) % getNumber(person) === 0">
<p>--divider--</p>
</li>
</ul>
Try this (some refactorings):
<li repeat-start="person in persons track by $index">
<p>{{ person.name }}</p>
<p ng-if="putDividerHere($index)">--divider--</p>
</li>
$scope.putDividerHere(index) {
var randomNumber = Math.floor(Math.random() * (3) + 2);
return ((index + 1) % randomNumber) === 0;
}
A small change needed. Call a function in your ng-if that cals the getNumber function and returns the condition true/false
HTML:
<li ng-repeat-end ng-if="isShowDivider($index+1)">
<p>--divider--</p>
</li>
JS:
$scope.isShowDivider = function (index) {
if (index % $scope.getNumber() !== 0)
return false;
return true;
}
$scope.getNumber = function() {
return Math.floor(Math.random() * (3) + 2);
}
Edit for the iterations reached. Aborting error.
<li ng-repeat="person in persons track by $index">
<p>{{person.name}}</p>
<p ng-if="isShowDivider($index+1)">--divider--</p>
</li>

Depth of nested ng-repeat — AngularJS

Is there a way to get the depth of a nesting ng-repeats? Say if you are iterating over an nested object of unknown depth like nested comments?
Since ng-repeat creates a new scope, you could do something like this.
{{$depth = $parent.$depth && $parent.$depth + 1 || 1}}
See https://jsfiddle.net/crgt25uk/ for a working example.
var app = angular.module('app',[]);
app.controller('ExampleController',function($scope){
$scope.items = [
{name:'foo',items:[{name:'foofoo'},{name:'foobar'}]},
{name:'bar',items:[{name:'barfoo'},{name:'barbar'}]}
]
})
And your template:
<div ng-app="app">
<div ng-controller="ExampleController">
<li ng-repeat="item in items">{{$depth = $parent.$depth && $parent.$depth + 1 || 1}}: {{item.name}}
<ul>
<li ng-repeat="item in item.items">{{$depth = $parent.$depth && $parent.$depth + 1 || 1}}: {{item.name}}</li>
</ul>
</li>
</div>
</div>

nested ngrepeat, get the index in the nested array

can you help me with the index in the nested ng-repeat ? I can't find the right way to take the first 8 element of prodata, then the 8 following elements, then the 8 following elements...etc.
<ion-slide-box show-pager="true" does-continue="true" on-slide-changed="slideHasChanged($index)">
<ion-slide ng-repeat="s in [0,1,2,3,4,5,6,7,8,9]">
<ul>
<li ng-repeat="item in prodata | limitTo:8*s+8:8*s+0">
{{s}}
<a class="suggestPro" href="#">
<span><img ng-src="img/boards/{{item.imageName}}" /></span>
<p class="flex-caption"> {{item.model}} - {{item.name}}</p>
</a>
</li>
</ul>
</ion-slide>
</ion-slide-box>
Check your angular version. The ability to specify the begin argument has been added to 1.4 version of angular :
Extract from angular's change log :
limitTo: extend the filter to take a beginning index argument (aaae3cc4, #5355, #10899)
Your code wil be :
<li ng-repeat="item in prodata | limitTo:8:8*s">
The best way IMO would be to reformat the initial array to make it suitable for usage with ng-repeat. Example:
var getSplittedArray = function (array, numberOfElements) {
var newArray = [];
for(var i = 0; i < array.length; i += 1) {
if (i % numberOfElements === 0) {
newArray.push([]);
}
newArray[newArray.length - 1].push(array[i]);
}
return newArray;
}
// Example: var realArray = someService.get();
$scope.splittedArray = getSplittedArray(realArray, 8);
Example usage in the view:
<ion-slide-box show-pager="true" does-continue="true" on-slide-changed="slideHasChanged($index)">
<ion-slide ng-repeat="set in splittedArray">
<ul>
<li ng-repeat="item in set">
{{s}}
<a class="suggestPro" href="#">
<span><img ng-src="img/boards/{{item.imageName}}" /></span>
<p class="flex-caption"> {{item.model}} - {{item.name}}</p>
</a>
</li>
</ul>
</ion-slide>
</ion-slide-box>

In ng-repeat how to get the $last item in a filter?

I define a collection of language objects like this:
$scope.languages = [
{'name':'English', value:'english', 'checked':true, 'available': true},
{'name':'German', value:'german', 'checked':false, 'available': true},
{'name':'Spanish', value:'spanish', 'checked':true, 'available': true}
];
Then I display them in checkboxes like this:
<div class="form-group">
<label for="description">Languages</label>
<div class="checkbox" data-ng-repeat="language in languages">
<label><input type="checkbox" ng-model="language.checked">{{language.name}}</label>
</div>
</div>
And as they are checked on and off, I display a list of them like this:
<div class="dataRow">
<div class="dataLabel">Languages:</div>
<div class="dataValue text-success" data-ng-repeat="language in languages">
<span ng-if="language.checked">{{language.name + ($last ? '' : ',')}}</span>
</div>
<div class="clear"></div>
</div>
And as long as the last language is selected, then the final comma will not be shown. But if the last language (Spanish) is not selected, then the final comma is erroneously shown.
Instead of $last I need something like $last(isChecked). How do I do that?
You could iterate only over the checked items by adding a filter in your ng-repeat like this:
<div class="dataRow">
<div class="dataLabel">Languages:</div>
<div class="dataValue text-success" data-ng-repeat="language in languages | filter: {checked: true}">
{{language.name + ($last ? '' : ',')}}
</div>
<div class="clear"></div>
</div>
That way you know the last element is checked.
Here's a working fiddle http://jsfiddle.net/q7ch1ysh/
Just specify that in the ternary:
language.name + ($last && language.checked ? '' : ',')
try this:
<div class="dataValue text-success" data-ng-repeat="language in languages track by $index">
<span ng-if="language.checked">{{language.name + (($index==languages.length && language.checked) ? '' : ',')}}</span>
</div>

AngularJS - How to access to the next item from a ng-repeat in the controller

I'd like to access to the parameters of the next item on screen when clicking on a button.
I use a ng-repeat in my html file:
<li ng-repeat="item in items | filter:query" ng-show="isSelected($index)">
<img src="xxx.jpg" />
</li>
And the index in my Controller with a loop:
$scope.itemNext = function () {
$scope._Index = ($scope._Index < $scope.nbItems - 1) ? ++$scope._Index : 0;
$scope.functionToCallWithNextItem(nextItem.param1);
};
A simple $scope.items[$scope._Index].param1 instead of nextItem.param1 wouldn't work as the data is filtered so $index+1 from $scope.items isn't necessarily the good one.
Any idea ?
You can assign your filtered data to a variable:
<li ng-repeat="item in (filteredItems = (items | filter:query))">
Then use $index + 1 to get the next item:
<a ng-click="itemNext(filteredItems[$index + 1])">
Demo: http://plnkr.co/edit/OdL5rIxtTEHnQCC3g4LS?p=preview
It's simpy that
<div ng-repeat="item in items">
current: {{item.value}}
next: {{ items[$index + 1].value}}
previous: {{ items[$index - 1].value}}
</div>

Resources