Dependant ng-repeat in AngularJS - angularjs

I have a following data structure coming from REST:
scope.taglist =
[ { name: "mylist", tags: ["tag1", "tag2", "tag3", ...]}, { name:
"mylist2", tags: ["tag2.1", "tag2.2", "tag2.3", ...]} ]
In order to present the names of the objects I have the following html:
<div>
<select ng-model="tagNameSelection">
<option ng-repeat="tagObj in taglist" value="{{tagObj}}">{{tagObj.name}}</option>
</select>
</div>
<div class="tagdetails">
<!-- present the list of tags from tagNameSelection -->
</div>
Now I am a little bit of a loss on how to present the tags list of
individual object. I am able to present the array in raw format (by
sticking {{tagNameSelection}} inside the tagdetails div) but when I
try to iterate through those with ng-repeat angular gives a error
message.
Oddly enough when I hard-code one of the tag lists to the scope in controller the ng-repeat works flawlessly.

Maybe you interesting something like this:
HTML
<div ng-controller="fessCntrl">
<div>
<select ng-model="tagNameSelection"
ng-options="tagObj as tagObj.name for tagObj in taglist"
ng-change="change(tagNameSelection)"></select>
</div>
<pre>{{tagNameSelection.tags|json}}</pre>
<div class="tagdetails">
<ul ng-repeat="tag in tagNameSelection.tags">
<li>{{tag}}</li>
</ul>
</div>
</div>
Controller
var fessmodule = angular.module('myModule', []);
fessmodule.controller('fessCntrl', function ($scope) {
$scope.change = function (value) {
};
$scope.taglist = [{
name: "mylist",
tags: ["tag1", "tag2", "tag3"]
}, {
name: "mylist2",
tags: ["tag2.1", "tag2.2", "tag2.3"]
}]
});
fessmodule.$inject = ['$scope'];
See Fiddle

Related

Display a value from an object based on an id in AngularJS

I'm currently using a custom filter to display a name when a user id is displayed. So selected_user might be 5, so it then displays "bob" instead of "5":
{{ selected_user | display_name:users }}
The users object contains the entire users table.
The code:
angular.module('myApp', []);
function xCtrl($scope) {
$scope.users = [
{"id":1, "name":"joe"},
{"id":5, "name":"bob"},
{"id":10, "name":"charlie"},
];
$scope.select_user = function(user) {
$scope.selected_user = user;
}
}
angular.module('myApp').filter("display_name", function () {
return function (user_id, users) {
if(user_id) {
return(users[users.map(function(x) { return x.id; }).indexOf(user_id)].name);
}
}
});
This works fine, but it feels like I am doing this inefficiently by passing the entire users object to the filter each time a name is displayed. Is there a better way to do this with filters or are filters the wrong approach?
Demo: http://jsfiddle.net/h2rn3qnw/
One approach is to have the ng-click directive return the $index:
<div ng-repeat="user_data in users"
ng-click="select($index)" class="selection">
{{ user_data.id }} - {{ user_data.name }} - {{$index}}
</div>
$scope.select = function(index) {
$scope.selection = index;
};
Selected User ID: {{ users[selection].id }}<br>
Selected User Name: {{ users[selection].name }}
The DEMO
angular.module('myApp', [])
.controller("xCtrl", function xCtrl($scope) {
$scope.users = [
{"id":1, "name":"joe"},
{"id":5, "name":"bob"},
{"id":10, "name":"charlie"},
];
$scope.select = function(index) {
$scope.selection = index;
};
})
.selection { color:blue; cursor:pointer; }
<script src="//unpkg.com/angular/angular.js"></script>
<div ng-app='myApp'>
<div ng-controller="xCtrl">
Users:<br>
<div ng-repeat="user_data in users"
ng-click="select($index)" class="selection">
{{ user_data.id }} - {{ user_data.name }} - {{$index}}
</div> <!-- ng-repeat -->
<hr>
Selected User ID: {{ users[selection].id }}<br>
Selected User Name: {{ users[selection].name }}
</div> <!-- ng-controller -->
</div> <!-- ng-app -->

How to display div of related content from template when clicked in Angular

I have a question on how best to achieve a feature in Angular. I have tried a number of solutions from ng-click, ng-if, ng-show and combinations of those but no luck in my novice state.
Currently I ng-repeat through an array of json data as li.
What I want to achieve is when I click one of the looped li it opens and displays the 'info div' with the related info to the li clicked (preferably by use of template). If user clicks another li it simply switches out the data to display correctly again.
Any advice on how to proceed to this feature would be greatly appreciated. I have provided my existing code as well as a CodePen.
JS:
var phonecatApp = angular.module('myApp', [])
.controller('starWarsCtrl', function ($scope) {
$scope.data = [
{"name": "Obi-Wan Kenobi",
"index":88,
"cat": "Jedi"},
{"name": "Yoda",
"index":69,
"cat":"Jedi"},
{"name": "Lando",
"index":31},
{"name": "Han Solo",
"index":90},
{"name": "Darth Vader",
"index":98},
{"name": "Jar-Jar Binks",
"index":80},
{"name": "Mace Windu",
"index":45},
{"name": "Chewy",
"index":76}
];
})
HTML:
<div ng-app="myApp" ng-controller="starWarsCtrl">
<input type="text" id="query" ng-model="query"/>
<div class="bscroll">
<ul>
<li class="box" ng-repeat="i in data | filter:query | orderBy: orderList">
<h2>{{i.name}}</h2>
<p>{{i.index}}{{i.cat}}</p>
</li>
</ul>
</div>
<div class="info">The details would appear here</div>
</div>
Simplest solution is to add an ng-click to your <li> that calls a function to select the item:
<li class="box" ng-repeat="i in data | filter:query | orderBy: orderList" ng-click="select(i)">
<h2>{{i.name}}</h2>
<p>{{i.index}}{{i.cat}}</p>
</li>
and then your js:
$scope.select = function (item) {
$scope.selectedItem = item;
}
and your info div:
<div class="info" ng-if="selectedItem">
Name: <span ng-bind="selectedItem.name"></span><br>
Index: <span ng-bind="selectedItem.index"></span>
<div ng-if="selectedItem.cat">
Category: <span ng-bind="selectedItem.cat"></span>
</div>
</div>
This should at least be enough to get you started in the right direction.
Updated pen: http://codepen.io/anon/pen/WrqXJa

Error: [ngRepeat:dupes] on a seemingly valid object [duplicate]

I am defining a custom filter like so:
<div class="idea item" ng-repeat="item in items" isoatom>
<div class="section comment clearfix" ng-repeat="comment in item.comments | range:1:2">
....
</div>
</div>
As you can see the ng-repeat where the filter is being used is nested within another ng-repeat
The filter is defined like this:
myapp.filter('range', function() {
return function(input, min, max) {
min = parseInt(min); //Make string input int
max = parseInt(max);
for (var i=min; i<max; i++)
input.push(i);
return input;
};
});
I'm getting:
Error: Duplicates in a repeater are not allowed. Repeater: comment in item.comments | range:1:2 ngRepeatAction#https://ajax.googleapis.com/ajax/libs/angularjs/1.1.4/an
The solution is actually described here: http://www.anujgakhar.com/2013/06/15/duplicates-in-a-repeater-are-not-allowed-in-angularjs/
AngularJS does not allow duplicates in a ng-repeat directive. This means if you are trying to do the following, you will get an error.
// This code throws the error "Duplicates in a repeater are not allowed.
// Repeater: row in [1,1,1] key: number:1"
<div ng-repeat="row in [1,1,1]">
However, changing the above code slightly to define an index to determine uniqueness as below will get it working again.
// This will work
<div ng-repeat="row in [1,1,1] track by $index">
Official docs are here: https://docs.angularjs.org/error/ngRepeat/dupes
For those who expect JSON and still getting the same error, make sure that you parse your data:
$scope.customers = JSON.parse(data)
I was having an issue in my project where I was using ng-repeat track by $index but the products were not getting reflecting when data comes from database. My code is as below:
<div ng-repeat="product in productList.productList track by $index">
<product info="product"></product>
</div>
In the above code, product is a separate directive to display the product.But i came to know that $index causes issue when we pass data out from the scope. So the data losses and DOM can not be updated.
I found the solution by using product.id as a key in ng-repeat like below:
<div ng-repeat="product in productList.productList track by product.id">
<product info="product"></product>
</div>
But the above code again fails and throws the below error when more than one product comes with same id:
angular.js:11706 Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater
So finally i solved the problem by making dynamic unique key of ng-repeat like below:
<div ng-repeat="product in productList.productList track by (product.id + $index)">
<product info="product"></product>
</div>
This solved my problem and hope this will help you in future.
What do you intend your "range" filter to do?
Here's a working sample of what I think you're trying to do: http://jsfiddle.net/evictor/hz4Ep/
HTML:
<div ng-app="manyminds" ng-controller="MainCtrl">
<div class="idea item" ng-repeat="item in items" isoatom>
Item {{$index}}
<div class="section comment clearfix" ng-repeat="comment in item.comments | range:1:2">
Comment {{$index}}
{{comment}}
</div>
</div>
</div>
JS:
angular.module('manyminds', [], function() {}).filter('range', function() {
return function(input, min, max) {
var range = [];
min = parseInt(min); //Make string input int
max = parseInt(max);
for (var i=min; i<=max; i++)
input[i] && range.push(input[i]);
return range;
};
});
function MainCtrl($scope)
{
$scope.items = [
{
comments: [
'comment 0 in item 0',
'comment 1 in item 0'
]
},
{
comments: [
'comment 0 in item 1',
'comment 1 in item 1',
'comment 2 in item 1',
'comment 3 in item 1'
]
}
];
}
If by chance this error happens when working with SharePoint 2010: Rename your .json file extensions and be sure to update your restService path. No additional "track by $index" was required.
Luckily I was forwarded this link to this rationale:
.json becomes an important file type in SP2010. SP2010 includes certains webservice endpoints. The location of these files is 14hive\isapi folder. The extension of these files are .json. That is the reason it gives such a error.
"cares only that the contents of a json file is json - not its file extension"
Once the file extensions are changed, should be all set.
Just in case this happens to someone else, I'm documenting this here, I was getting this error because I mistakenly set the ng-model the same as the ng-repeat array:
<select ng-model="list_views">
<option ng-selected="{{view == config.list_view}}"
ng-repeat="view in list_views"
value="{{view}}">
{{view}}
</option>
</select>
Instead of:
<select ng-model="config.list_view">
<option ng-selected="{{view == config.list_view}}"
ng-repeat="view in list_views"
value="{{view}}">
{{view}}
</option>
</select>
I checked the array and didn't have any duplicates, just double check your variables.
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys.
Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}
Example
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="angular.js"></script>
</head>
<body>
<div ng-app="myApp" ng-controller="personController">
<table>
<tr> <th>First Name</th> <th>Last Name</th> </tr>
<tr ng-repeat="person in people track by $index">
<td>{{person.firstName}}</td>
<td>{{person.lastName}}</td>
<td><input type="button" value="Select" ng-click="showDetails($index)" /></td>
</tr>
</table> <hr />
<table>
<tr ng-repeat="person1 in items track by $index">
<td>{{person1.firstName}}</td>
<td>{{person1.lastName}}</td>
</tr>
</table>
<span> {{sayHello()}}</span>
</div>
<script> var myApp = angular.module("myApp", []);
myApp.controller("personController", ['$scope', function ($scope)
{
$scope.people = [{ firstName: "F1", lastName: "L1" },
{ firstName: "F2", lastName: "L2" },
{ firstName: "F3", lastName: "L3" },
{ firstName: "F4", lastName: "L4" },
{ firstName: "F5", lastName: "L5" }]
$scope.items = [];
$scope.selectedPerson = $scope.people[0];
$scope.showDetails = function (ind)
{
$scope.selectedPerson = $scope.people[ind];
$scope.items.push($scope.selectedPerson);
}
$scope.sayHello = function ()
{
return $scope.items.firstName;
}
}]) </script>
</body>
</html>
If you call a ng-repeat within a < ul> tag, you may be able to allow duplicates. See this link for reference.
See Todo2.html
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: sdetail in mydt, Duplicate key: string: , Duplicate value:
I faced this error because i had written wrong database name in my php api part......
So this error may also occurs when you are fetching the data from database base, whose name is written incorrect by you.
My JSON response was like this:
{
"items": [
{
"index": 1, "name": "Samantha", "rarity": "Scarborough","email": "maureen#sykes.mk"
},{
"index": 2, "name": "Amanda", "rarity": "Vick", "email": "jessica#livingston.mv"
}
]
}
So, I used ng-repeat = "item in variables.items" to display it.

Data-binding is not updating ng-repeat section

I'm new to angularjs and I have a data binding issue.
After each repeated line, I want to show a number of mutually exclusive options. These would be images using a background-image, and when the option is selected I add the css class 'selected' to the span containing the image.
I've simplified it below to show options A, B and C and to make the currently selected one bold. By clicking on A, B or C you change the option.
The inner ng-repeat section is not evaluated again (it seems) when the model changes, but as the "Debugging category" shows, the outer ng-repeat is re-evaluated and the "Debugging category" shows correctly that the category has been changed.
I'm clearly missing something about angularjs; how can I get the inner ng-repeat to re-evaluate to that the correct style is applied to the A, B and C options?
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
var testApp = angular.module('testApp', []);
testApp.controller('NamesCtrl', function ($scope) {
$scope.categories = [ 'A', 'B', 'C' ];
$scope.names = [
{ name: 'John', category: 'C' },
{ name: 'Cindy', category: 'A' },
{ name: 'Patrick', category: 'B' }
];
$scope.changeCategory = function(name, category) {
name.category = category;
};
});
</script>
<body ng-app="testApp">
<h1>Names</h1>
<div ng-controller="NamesCtrl">
<ul>
<li ng-repeat="name in names">
<span>{{name.name}}</span>
<span ng-repeat="category in categories" ng-init="selected = (category == name.category)">
<span ng-style="{'true': {'font-weight': 'bold'}, false: {}}[selected]" ng-click="changeCategory(name, category)">{{category}}</span>
</span>
<em>(Debugging category: {{name.category}})</em>
</li>
</ul>
</div>
</body>
ng-init expression is not watched and updated automatically whenever related scope property value changes, you could just solve this by removing ng-init and using the expression directly
<span ng-style="{'true': {'font-weight': 'bold'}}[category == name.category]"
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>
var testApp = angular.module('testApp', []);
testApp.controller('NamesCtrl', function ($scope) {
$scope.categories = [ 'A', 'B', 'C' ];
$scope.names = [
{ name: 'John', category: 'C' },
{ name: 'Cindy', category: 'A' },
{ name: 'Patrick', category: 'B' }
];
$scope.changeCategory = function(name, category) {
name.category = category;
};
});
</script>
<body ng-app="testApp">
<h1>Names</h1>
<div ng-controller="NamesCtrl">
<ul>
<li ng-repeat="name in names">
<span>{{name.name}}</span>
<span ng-repeat="category in categories" >
<span ng-style="{'true': {'font-weight': 'bold'}}[category == name.category]" ng-click="changeCategory(name, category)">{{category}}</span>
</span>
<em>(Debugging category: {{name.category}})</em>
</li>
</ul>
</div>
</body>

Angular update object in array

I wanna update an object within an objects array. Is there another possibility than iterating over all items and update the one which is matching? Current code looks like the following:
angular.module('app').controller('MyController', function($scope) {
$scope.object = {
name: 'test',
objects: [
{id: 1, name: 'test1'},
{id: 2, name: 'test2'}
]
};
$scope.update = function(id, data) {
var objects = $scope.object.objects;
for (var i = 0; i < objects.length; i++) {
if (objects[i].id === id) {
objects[i] = data;
break;
}
}
}
});
There are several ways to do that. Your situation is not very clear.
-> You can pass index instead of id. Then, your update function will be like:
$scope.update = function(index, data) {
$scope.object.objects[index] = data;
};
-> You can use ng-repeat on your view and bind object properties to input elements.
<div ng-repeat="item in object.objects">
ID: <input ng-model="item.id" /> <br/>
Name: <input ng-model="item.name" /> <br/>
</div>
Filters that help in finding the element from the array, can also be used to update the element in the array directly.
In the code below [0] --> is the object accessed directly.
Plunker Demo
$filter('filter')($scope.model, {firstName: selected})[0]
Pass the item to your update method. Take a look at sample bellow.
function MyCtrl($scope) {
$scope.items =
[
{name: 'obj1', info: {text: 'some extra info for obj1', show: true}},
{name: 'obj2', info: {text: 'some extra info for obj2', show: false}},
];
$scope.updateName = function(item, newName){
item.name = newName;
}
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app>
<table ng-controller="MyCtrl" class="table table-hover table-striped">
<tr ng-repeat="x in items">
<td> {{ x.name }}</td>
<td>
Update
<div ng-show="showUpdate" ><input type="text" ng-model="someNewName"> <input type="button" value="update" ng-click="updateName(x, someNewName); showUpdate = false;"></div>
</td>
</tr>
</table>
</body>
Going off your plunker, I would do this:
Change
Edit
to be
Edit
Then use the array index within your $scope.selectSubObject method to directly access your desired element. Something like this:
$scope.selectSubObject = function(idx) {
$scope.selectedSubObject = angular.copy(
$scope.selectedMainObject.subObjects[idx]
);
};
If however, you only have the id to go off of, then you can use the angular filterService to filter on the id that you want. But this will still do a loop and iterate over the array in the background.
See angularjs documentation for ngrepeat to see the variables that it exposes.

Resources