AngularJS $index always 0 - javascript

i am having a problem with angularjs and $index
i give it at a parameter to a function. It works at HTML in {{}} ({{$index}} works). But it doesnt work in the function. I alert the index and its always 0...
I used it at another position in my project and it works fine..
Here is the code.
<input type="checkbox" id="rounded1" ng-click="setClickEvent($index)" ng-model="clickStatus"/>
JS File with the function:
$scope.setClickEvent = function(index) {
alert(index);
};
the alert is always 0....
i hope someone could help me. Thanks :)

It looks like you're not using this <input> tag in an ng-repeat directive. $index will show 0 unless it's used in an ng-repeat where it will count from 0 to n.
Perhaps you meant to do something like this:
<div ng-repeat="i in [1, 2, 3, 4, 5] track by $index">
<input type="checkbox" ng-click="setClickEvent($index)" ng-model="clickStatus"/>
</div>
I removed the ID because each of the 5 checkboxes would have the same ID which isn't valid.

Sorry, here is the whole html code with the ng-repeat
<div ng-repeat="frage in frageListe">
<div class="input-group">
<div class="container-fluid list-group-item ">
<div class="rounded">
<input type="checkbox" id="rounded1" ng-click="setClickEvent(frage.id)" ng-model="clickStatus"/>
<label for="rounded1"></label>
</div>
<div class="list-text">
<h3 class="list-group-item-heading quest-list-text serif">{{frage.titel}} {{$index}}</h3>
</div>
</div>
</div>
<hr class="divider-line-questlist">
</div>

Not sure if this is relevant, but I had the same problem. For me the issue was that I was nesting multiple ng-repeats. To solve this I set aliases for the indexes with ng-init.
https://docs.angularjs.org/api/ng/directive/ngInit
<script>
angular.module('initExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.list = [['a', 'b'], ['c', 'd']];
}]);
</script>
<div ng-controller="ExampleController">
<div ng-repeat="innerList in list" ng-init="outerIndex = $index">
<div ng-repeat="value in innerList" ng-init="innerIndex = $index">
<span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
</div>
</div>
</div>

Related

ng-repeat is repeating multiple time with same array

ng-repeat is binding same array with multiple times.
js :
$scope.currentitem = item;
$scope.currentitemCategory = $scope.currentitem.category.split(',');
console.log($scope.currentitemCategory);
html:
<div ng-repeat="category in currentitemCategory track by $index">
<a href="/content/digital-library/us/en/search.html?category={{category}}">
<span class="text"> {{category}} </span>
</a>
</div>
console :
Categorized in
audience/business
audience/business
audience/business
brandguidelines
brandguidelines
brandguidelines
corporateinitiatives/idf
corporateinitiatives/idf
corporateinitiatives/idf
I've created a sample application with your given snippet. I do not see any repeated entries. Everything works fine. Please provide few more details if the issue really exists.
var app= angular.module('sample', []);
app.controller('samplecontroller', function($scope){
var item = {category: 'Tennis, Carroms, Soccer, Volleyball'};
$scope.currentitem = item;
$scope.currentitemCategory = $scope.currentitem.category.split(',');
console.log($scope.currentitemCategory);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="sample">
<div ng-controller="samplecontroller">
<div ng-repeat="category in currentitemCategory track by $index">
<a href="/content/digital-library/us/en/search.html?category={{category}}">
<span class="text"> {{category}} </span>
</a>
</div>
</div>
</body>

can not add new element using ng-repeat in Angular.js

I need one help. I need to create extra input element using ng-repeat in angular.js. I did something but its not working. I explaining my code below.
<div>
<ul>
<li ng-repeat="r in arr">
<input type="text" ng-model="r.uname">
</li>
</ul>
</div>
<button type="button" id="btn" ng-click="create();">Add</button>
Here I need when user will click on add button the new input field will create just below the first one. My scripting side code is given below.
<script type="text/javascript">
function ContactController($scope){
$scope.arr=[{uname:null}];
$scope.create=function(){
console.log('hii');
$scope.arr.push({
uname:null
});
console.log('hi',$scope.arr);
}
}
</script>
But here i am unable to generate the new input type element. Please help me.
I don't see an error in your code. it works: http://jsfiddle.net/Lvc0u55v/9536/
$scope.arr=[{uname: null}];
$scope.create = function(){
console.log('hii');
$scope.arr.push({
uname: null
});
console.log('hi',$scope.arr);
}
Define your array like this..
$scope.arr=[{}];
$scope.create=function(){
$scope.arr.push({});
console.log('hi',$scope.arr);
}
everything else looks fine..
Please refer to this.
Use track by in order to achieve the thing you're aiming for:
<li ng-repeat="r in arr track by $index">
the first thing u are pushing the same element in the array so you want to track the in the ng-repeat
<div ng-repeat="r in arr track by $index">
<input type="text" ng-model="r.uname"> </div>
still it is not creating then try the console is coming and it is not creating
use $scope.$apply() to manually run the digest cycle
Which version of angular are you running. The code which you mentioned would not work in angular 1.3 and above. It works in 1.2 though. See this with 1.2
https://plnkr.co/edit/52ygFNbSFZQr3SdCvlDc?p=preview
<body ng-controller="ContactController">
<p>Hello {{name}}!</p>
<div>
<ul>
<li ng-repeat="r in arr">
<input type="text" ng-model="r.uname">
</li>
</ul>
</div>
<button type="button" id="btn" ng-click="create();">Add</button>
<script type="text/javascript">
function ContactController($scope){
$scope.arr=[{uname:null}];
$scope.create=function(){
console.log('hii');
$scope.arr.push({
uname:null
});
console.log('hi',$scope.arr);
}
}
</script>
For 1.4 and above use the following. https://plnkr.co/edit/t7STLztqoMiPKaiHr62V?p=preview
<body ng-controller="ContactController">
<p>Hello {{name}}!</p>
<div>
<ul>
<li ng-repeat="r in arr">
<input type="text" ng-model="r.uname">
</li>
</ul>
</div>
Add
app.controller('ContactController', function($scope) {
$scope.name = 'World';
$scope.arr=[{uname:null}];
$scope.create=function(){
console.log('hii');
$scope.arr.push({
uname:null
});
console.log('hi',$scope.arr);
}
});

Directive take other directive's data after deletion

Edit: Thanks to Simon Schüpbach, I was able to resolve the issue by changing the template. See the end for the solution.
Let's preface this by saying that we are beginner to soft-intermediate in Angular.
On one of our project, we are using angularjs 1.4.x and also ng-cart (https://github.com/snapjay/ngCart). It worked great but then we were confronted with a demand from our client that created new weird issues.
We added fsCounter, as a directive, to the cart page so user can add or remove items. This all work great but the users also have the option to delete an item from the cart view. Deletion works as expected BUT it seems to affect the scope to the item that takes it place.
Let me make it clearer :
Let's say we have 2 products in our cart page, it displays something like that
Product_1 {price} {counter} {total} delete_btn
Product_2 {price} {counter} {total} delete_btn
Each fsCounter is its own scope
return {
restrict: "A",
scope: {
value: "=value",
index: "=index"
},
link: //other logic
However when we delete the first item, visually and in the directives, the data seems to shift. So our second row will now inherit the first row's counter.
Directive's data looks like this:
Product_1:
itemId:3,
quantity:2,
{other data}
Product_2:
itemId:8,
quantity:5,
{other data}
But once we delete the first directive (We get the scope, remove the DOM element, destroy the scope) the second directive will now have this data:
Product_2:
itemId:3,
quantity:2,
{other data}
Here is the template code :
<div class="unItem" ng-repeat="item in ngCart.getCart().items track by $index">
<div class="photo"><img src="{[{ item.getImage() }]}" alt=""></div>
<div class="details">
<h3>{[{ item.getName() }]} <span>{[{ item.getPrice() | currency:$}]}</span></h3>
<md-select ng-model="attributes" placeholder="Attribut" class="select-attribut" ng-show="item.hasAttributes()" ng-change="item.updateSelected(attributes)">
<md-option ng-repeat="attr in item.getAttributes()" ng-selected="attr == item.getSelected()" ng-value="attr">{[{ attr }]}</md-option>
</md-select>
</div>
<div class="quantity">
<div fs-counter-dynamic value="itemQuantity"
data-min="1"
data-max="999"
data-step="1"
data-addclass="add-quantity"
data-width="130px"
data-itemid="{[{ item.getId() }]}"
data-editable
ng-model="itemQuantity"
name="quantity" id="quantity-{[{ item.getId() }]}",
index="{[{ item.getId() }]}"
></div>
</div>
<div class="total">Total : {[{ item.getTotal() | currency }]}</div>
<div class="delete"><a ng-click="ngCart.removeItemById(item.getId());"></a></div>
</div>
Is this normal behavior? Is there any way to force the directive to keeps its own data? From what I've understood, each directive has its own scope, so what I think happens is that, when we remove the first one, it keeps the data stored in some kind of array that says "directive 1 data is : " and when we delete the first directive, the second one becomes the first.
So basically, are we doing anything wrong or is there anyway to remap the data?
Hope it was clear enough,
Thanks!
Edit: added html code
Edit2: Answer :
New FsCounter template looks like this:
<div fs-counter-dynamic value="item._quantity"
data-min="1"
data-max="999"
data-step="1"
data-addclass="add-quantity"
data-width="130px"
data-itemid="{[{ item.getId() }]}"
data-editable
ng-model="item._quantity"
name="quantity" id="quantity{[{ item.getId() }]}"
></div>
Do you know ng-repeat, then you don't have such problems
<div ng-repeat="product in products">
<fs-counter index="product.index" value="product.value"></fs-counter>
</div>
and in your controller
$scope.products = [
{index:1, value:"Cola"},
{index:2,,value:"Fanta"}
]
to remove an element you just have to do
$scope.products.splice(0,1);
Edit:
I suggest to save all necessary data inside the item you use inside ng-repeat. Your problem is, that you mix data from array with other data from your $scope. It is possible to $watch changes in your directive, but if you set them with ng-repeat everything is done automatically.
$scope.products = [
{index:1, name:"Cola", price:1.50, image:"your/path.png", attributes:{...}},
{index:2, name:"Fanta", price:1.40, image:"your/path.png"}
]
And then in your html
<div class="unItem" ng-repeat="item in ngCart.products track by $index">
<div class="photo"><img ng-src="item.image" alt=""></div>
<div class="details">
<h3>{{item.name}} <span>{{item.price | currency}}</span></h3>
</div>
<div class="quantity">
<div fs-counter-dynamic value="item.quantity"
data-min="1"
data-max="999"
data-step="1"
data-addclass="add-quantity"
data-width="130px"
data-itemid="item.index"
data-editable
ng-model="item.quantity"
name="quantity" id="{{'quantity-' + $index}}",
index="item.index"
></div>
</div>
<div class="total">Total : {{ item.price * item.quantity | currency }}</div>
<div class="delete"><a ng-click="ngCart.removeItemById(item.index);"></a></div>
</div>

Angular - Error: ngRepeat:dupes Duplicate Key in Repeater

I am working on a simple todo app that adds task to an existing array of json data. However when I try to add more than one task I get this error: Error: [ngRepeat:dupes]. I can't for the life of me figure out what is going wrong. I suspect it might have something to do with the namespace, but after changing names around a few times I still get the same error. If anybody could point me in the right direction I would much appreciate it.
The HTML Code
<div id="taskComplete" ng-app="taskComplete">
<div class="container" ng-controller="taskCtrl">
<div id="taskCompleteHeading" class="row">
<div class="col-xs-12">
<div class="page-header">
<h1 class="text-center">TaskComplete <small>An AgularJs App</small></h1>
</div>
</div>
</div>
<div id="newTaskSubmit">
<input type="text" ng-model="newTask.title">
<input type="text" ng-model="newTask.description">
<button type="button" ng-click="addTask(newTask)">Add Task</button>
</div>
<div class="well">
<pre>{{newTask | json}}</pre>
</div>
<div ng-repeat="task in activeTasks">
<h4>{{task.title}}</h4>
</div>
</div>
</div>
UPDATED SOLUTION
<div ng-repeat="task in activeTasks track by $index">
<h4>{{task.title}}</h4>
</div>
The JAVASCRIPT Code
angular
.module('taskComplete')
.controller('taskCtrl', function($scope, taskFactory) {
$scope.activeTasks;
taskFactory.getTasks().success(function(data) {
$scope.activeTasks = data;
console.log($scope.activeTasks);
}).error(function(error) {
console.log(error);
});
$scope.newTask = {};
$scope.addTask = function(newTask) {
$scope.activeTasks.push(newTask);
}
});
Use this
<div ng-repeat="task in activeTasks track by $index">
<h4>{{task.title}}</h4>
</div>
So angular will track your ng-repeat node
Use track by $index:
ng-repeat="task in activeTasks track by $index"
$scope.addTask = function(newTask) {
$scope.activeTasks.push(newTask);
}
Should become
$scope.addTask = function(newTask) {
$scope.activeTasks.push(newTask);
$scope.newTask = {};
}
The error was caused by the same object being used twice. It is also why changes below were being mirrored by the added task.

AngularJS ngRepeat element removal

There are quite a few questions on how to implement item removal inside ngRepeat directive, and as I figured out, it comes down to using ngClick and triggering some remove function passing it item's $index.
However, I couldn't find anywhere an example where I have multiple ngRepeats:
<div ng-controller="MyController">
<div ng-repeat="email in user.emails">
{{ email }} <a href>Remove</a>
</div>
<div ng-repeat="phone in user.phones">
{{ phone }} <a href>Remove</a>
</div>
</div>
For this, I would need to create $scope.removePhone and $scope.removeEmail which would be called using ngClick on Remove anchor. But I'm looking for a more generic solution. Especially since I have many pages with many ngRepeats .
I was thinking about writing a directive which would be placed on Remove anchor and would do something like this:
Find ngRepeat among parent elements.
Read what it's iterating over ('user.emails' in first case, 'user.phones' in second)
Remove $index element from THAT model.
So the markup would look something like this:
<div ng-controller="MyController">
<div ng-repeat="email in user.emails">
{{ email }} <a href remove-directive="$index">Remove</a>
</div>
<div ng-repeat="phone in user.phones">
{{ phone }} <a href remove-directive="$index">Remove</a>
</div>
</div>
Is what I'm looking for possible to achieve and what would be the preferred way to do this?
Current hacky solution
Here is how I do it currently. It's hacky and ugly but gets the job done until I figure out a prettier way.
myAppModule.controller('MyController', function ($scope, $parse, $routeParams, User) {
$scope.user = User.get({id: $routeParams.id});
$scope.remove = function ($index, $event) {
// TODO: Find a way to make a directive that does this. This is ugly. And probably very wrong.
var repeatExpr = $($event.currentTarget).closest('[ng-repeat]').attr('ng-repeat');
var modelPath = $parse(repeatExpr.split('in')[1].replace(/^\s+|\s+$/g, ''));
$scope.$eval(modelPath).splice($index, 1);
};
});
And in DOM:
<div ng-repeat="email in user.email" class="control-group">
<label class="control-label">
{{ "Email Address"|_trans }}
</label>
<div class="controls">
<input type="text" ng-model="email.address">
<span class="help-inline"><a href ng-click="remove($index, $event)">{{ "Delete"|_trans }}</a></span>
</div>
</div>
You could create a generic remove method that would take in the array and the item to remove.
<div ng-app="" ng-controller="MyController">
<div ng-repeat="email in emails">{{ email }} <a ng-click="remove(emails, $index)">Remove</a>
</div>
<div ng-repeat="phone in phones">{{ phone }} <a ng-click="remove(phones, $index)">Remove</a>
</div>
</div>
$scope.remove = function(array, index){
array.splice(index, 1);
}
No JS
<div ng-repeat="option in options" ng-init=options=[1,2,3,4,5]>
<button ng-click="options.splice($index,1)">Remove me</button>
</div>
<div ng-app="" ng-controller="MyController">
<div ng-repeat="email in emails as datasource">{{ email }}
<a ng-click="datasource.splice($index,1)">Remove</a>
</div>
<div ng-repeat="phone in phones as datasource">{{ phone }}
<a ng-click="datasource.splice($index,1)">Remove</a>
</div>
</div>
A very simple and convenient way that works cross-browser is to use the 'remove' utility method from the library lodash.
<div ng-repeat="phone in phones">{{ phone }}
<a ng-click="removeItem(phones, phone)">Remove</a>
</div>
In your controller you declare then
//inject lodash dependency
//declare method in scope
$scope.removeItem = function(list, item){
lodash.remove(list,function(someItem) { return item === someItem});
}
You may of course use indexes if you like. See https://lodash.com/docs#remove
If you have used ng-repeat on an object instead of an array, do the following.
<div ng-app="" ng-controller="MyController">
<div ng-repeat="email in emails">{{ email }}
<a ng-click="remove(emails, email)">Remove</a>
</div>
<div ng-repeat="phone in phones">{{ phone }}
<a ng-click="remove(phones, phone)">Remove</a>
</div>
</div>
$scope.remove = function(objects, o){
delete object[o.id];
}
or the more terse
<div ng-app="" ng-controller="MyController">
<div ng-repeat="email in emails">{{ email }}
<a ng-click="delete emails[email.id]">Remove</a>
</div>
<div ng-repeat="phone in phones">{{ phone }}
<a ng-click="delete phones[phone.id]">Remove</a>
</div>
</div>
presumes that the objects look like this
var emails = { '123' : { id : '123', .... } };
var phones = { '123' : { id : '123', .... } };

Categories