How to use a variable inside the ng-repeat? - angularjs

I'm using the ng-repeat to list all the products, and I want to print out a star symbol x times depending on the product.rating. So, the code looks like following:
<p class="bs-rating"><i class="fa fa-star" ng-repeat="i in getRating({{ product.rating }}) track by $index"></i></p>
However, it parsed the product.rating as a String: Error: [$parse:syntax] http://errors.angularjs.org/1.3.0-beta.8/$parse/syntax?p0=product.rating&p1=is
I have tried to remove the curly brackets, {{ }}:
<p class="bs-rating"><i class="fa fa-star" ng-repeat="i in getRating(product.rating) track by $index"></i></p>
It gave me undefined. My getRating() is declared as following:
$scope.getRating = function(rating){
return new Array(parseInt(rating));
}
Could you help me figure it out how to pass a variable to ng tag?

The problem is on getRating function ie on generating the array.
I have developed an alternative solution on this link rating using ng-repeat #jsfiddle
<div ng-app="myApp" ng-controller="myCtrl">
<ol>
<li ng-repeat="product in products">
{{product.name}}
(
<span style="font-weight:bold" ng-repeat="i in
getRating(product.rating)">
x
</span>
)
</li>
</ol>
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope){
$scope.products = [
{name : "product1", rating:3},
{name : "product2", rating:5},
{name : "product3", rating:1}
];
$scope.getRating = function(rating){
var ratingArray = [];
for (var i = 1; i <= rating; i++) {
ratingArray.push(i);
}
return ratingArray;
};
});
Hopefully it will help.
Cheers

Related

Grabbing value from nested json array with Angular

I'm having trouble accessing the artists array within the items array here to be able to render the name field:
I'm currently able to grab other values at the same level as the artists that are simple objects. How can I loop through the array of the nested array?
Controller
$scope.search = "";
$scope.listLimit = "10";
$scope.selectedSongs = [];
$scope.addItem = function(song){
$scope.selectedSongs.push(song);
}
function fetch() {
$http.get("https://api.spotify.com/v1/search?q=" + $scope.search + "&type=track&limit=50")
.then(function(response) {
console.log(response.data.tracks.items);
$scope.isTheDataLoaded = true;
$scope.details = response.data.tracks.items;
});
}
Template
<div class="col-md-4">
<div class="playlist">
<h3>Top 10 Playlist</h3>
<ul class="songs" ng-repeat="song in selectedSongs track by $index | limitTo: listLimit">
<li><b>{{song.name}}</b></li>
<li>{{song.artists.name}}</li>
<li contenteditable='true'>Click to add note</li>
<li contenteditable='true'>Click to add url for image</li>
</ul>
<div id="result"></div>
</div>
</div>
You should do another ng-repeat to access the artists,
<div class="songs" ng-repeat="song in selectedSongs track by $index | limitTo: listLimit">
<ul ng-repeat="artist in song.artists">
<li><b>{{song.name}}</b></li>
<li>{{artist.name}}</li>
<li contenteditable='true'>Click to add note</li>
<li contenteditable='true'>Click to add url for image</li>
</ul>
</div>

iteration scope name in ng-repeat

This is my controller
var i;
for (i = 1; i <= 3; i++ ) {
$scope["items"+i].push({
notification: item.notification,
admin_id: item.admin_id,
user_id: item.user_id,
chat_time: item.chat_time
})
}
This is my static scope in ng-repeat
<ul>
<li ng-repeat="x in items1"></li>
<li ng-repeat="x in items2"></li>
<li ng-repeat="x in items3"></li>
</ul>
How to make dynamic iteration scope variabel in ng-repeat in single ng-repeat like this
<ul>
<li ng-repeat="x in items[i++]">
<!-- generate from here-->
<li ng-repeat="x in items1"></li>
<li ng-repeat="x in items2"></li>
<li ng-repeat="x in items3"></li>
<!-- to here -->
</li>
</ul>
and it will display like my static scope
Thanks , appreciate ur help and comment
You need to store all the different arrays in a new Array and then, you can loop it around as shown (I have created a sample):
JS:
$scope.item1 = [ /* json array 1 */];
$scope.item2 = [ /* json array 2 */];
$scope.item3 = [ /* json array 3 */];
$scope.itemsList = [];
for (i = 0; i < 3; i++) {
var varName = '$scope.item' + (i + 1);
$scope.itemsList.push(eval(varName));
}
HTML:
<li ng-repeat="mainList in itemsList">
<p ng-repeat="specificList in mainList">
<span>Id: {{specificList.id}}</span>
<br />
<span>Name: {{specificList.name}}</span>
</p>
</li>
Have a look at the demo.

Hide and Display Subsections

I have menu with sections and subsections, like this:
Section 1
Sub 1.1
Sub 1.2
Section 2
Sub 2.1
Sub 2.2
I want to hide subsections and show one of them by clicking on section (click on Section 2):
Section 1
Section 2
Sub 2.1
Sub 2.2
Here is my code and JSFiddle:
<div ng-controller="MyCtrl">
<div ng-repeat="(meta, counts) in info">
{{ meta }}
<ul class="subsection">
<li ng-repeat="(group, cnt) in counts">
{{ group }}
</li>
</ul>
</div>
</div>
Controller:
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.info = { "one": { "a": 1, "b": 2 },
"two" : { "c": 3, "d": 4 }};
$scope.display = function(meta) {
// ????
};
}
CSS:
ul.subsection {
display: none;
}
How can I fix this code to show one of the subsection by click on the section ?
Update: I fixed the link on JSFiddle
Since ng-repeat creates its own scope, you can simply toggle a variable within the loop, and use ng-show on that variable:
<div ng-repeat="(meta, counts) in info">
{{ meta }}
<ul class="subsection" ng-show="display">
<li ng-repeat="(group, cnt) in counts">
{{ group }}
</li>
</ul>
</div>
Edit: If you can only show one function at a time, then you can do what you were trying to do in your original code with a function:
<div ng-repeat="(meta, counts) in info">
{{ meta }}
<ul class="subsection" ng-show="sectionIndex == $index>
<li ng-repeat="(group, cnt) in counts">
{{ group }}
</li>
</ul>
</div>
$scope.display = function(index) {
$scope.sectionIndex = index;
}
You can simply do something like this:
<div ng-repeat="(meta, counts) in info">
{{meta}}
<ul class="subsection" ng-show="$parent.display == meta">
<li ng-repeat="(group, cnt) in counts">
{{ group }}
</li>
</ul>
</div>
Note, that you can refer $parent scope to avoid local scope display property.
In this case you don't need CSS rule. Bonus point is that you can set in controller $scope.display = 'two' and the second item will expand.
However cleaner way would be using a controller function as demonstrated by #tymeJV, this is the best approach.
Demo: http://plnkr.co/edit/zXeWjXLGHMv0BZZRZtud?p=preview

Two way binding is not working with angular js

I have the html code like this:
<div ng-controller="MainCtrl">
<tr data-ng-repeat="element in awesomeThings">
<div ng-if="$even">
<td ng-click="getServiceDetails(element)">
<a href="#">
{{element['serviceDetail']['name']}}
</a>
</td>
</div>
</tr>
//after some html
<span >{{publicName}}</span>
</div>
My controller looks like this:
angular.module('dcWithAngularApp')
.controller('MainCtrl', function ($scope,$http,test) {
$scope.awesomeThings = {"serviceDetail":{"name":"batman"}}
$scope.getServiceDetails = function(serviceDetails)
{
console.log('Called')
$scope.publicName = serviceDetails.name
}
});
After clicking on the td tag, the span text are not changing! Even though me changing the publicName in the current scope!
Where I'm making the mistake?
You don't have a name property in your $scope.awesomeThings array.
Here I added some animals for you.
angular.module('dcWithAngularApp')
.controller('MainCtrl', function ($scope,$http,test) {
$scope.awesomeThings = [
{ name: "Dog" },
{ name: "Cat" }
];
$scope.getServiceDetails = function(serviceDetails)
{
console.log('Called')
$scope.publicName = serviceDetails.name
}
});
Edit:
I put together a JSFiddle that solves your edit.
http://jsfiddle.net/HB7LU/7191/
$scope.awesomeThings is an array of string so element does not have a 'name' property.
You also have a missing ; after your console.log which may be stopping the code from running.
It may help you to log what the object is.
<div ng-controller="MainCtrl">
<tr data-ng-repeat="element in awesomeThings">
<div ng-if="$even">
<td ng-click="getServiceDetails(element)">
<a href="#">
{{element.name}}
</a>
</td>
</div>
</tr>
//after some html
<span >{{publicName}}</span>
</div>
angular.module('dcWithAngularApp')
.controller('MainCtrl', function ($scope,$http,test) {
$scope.awesomeThings = {"serviceDetail":{"name":"batman"}};
$scope.getServiceDetails = function(serviceDetails)
{
console.log(serviceDetails);
$scope.publicName = serviceDetails.name;
}
});
Your main issue is this:
$scope.publicName = serviceDetails.name
It needs to be
$scope.publicName = serviceDetails;
$scope.awesomeThings is an array of strings, not objects. You're ng-repeating through the string array and passing the string to your getServiceDetails method, so 'serviceDetails' is just a string.
As an unrelated warning, I see you're using tr inside a div. The tr should be inside a table, thead, or tbody.

How do I only show an element if nested ng-repeat is not empty?

I have a List of lists, created with a nested ng-repeat. Each outer ng-repeat contains a div with the label of its inner list (eg: "Group A"). I'm now trying to create a way to avoid showing this label if the inner list is empty due to filtering(Applied by an input searchtext)
Here is a plunker explaining my issue and my attempted solution : Plnkr
Having a 'heavy' function like isGroupEmpty seems extremely cumbersome - Is there any way to do this in a much simpler fashion? I was toying with the idea of moving the label inside the inner ng-repeat and having ng-show="$first" but it doesnt look great
I ended up with the following solution which worked perfectly. Plnkr
By setting a variable in the inner ng-repeat I was able to evaluate ng-show based on this variables length like so :
<input ng-model='searchText'/>
<span ng-show='filtered.length > 0'>
<ul>
<li ng-repeat='el in filtered = (model | filter:searchText)'>
<div>{{el.label}}</div>
</li>
</ul>
</span>
you could leverage ng-init, that way you'll call the filter only once:
<div ng-repeat='(key,group) in model'>
<div ng-init="filtered = (group | filter:filterFn)"></div>
<div ng-show="filtered.length !== 0">
<div>{{key}}</div>
<ul>
<li ng-repeat="el in filtered">
<div>{{el.label}}</div>
</li>
</ul>
</div>
</div>
usually it is not a good practice to use ng-init out of no where, but I guess it solves calling the filter twice.
Another way is to use the filter through javascript - you could inject $filter and retrieve 'filter' $filter('filter') in your controller, calling it with group as its first argument, the filterFn as its second, and store its result in your scope.
I used the following:
<ul>
<li ng-repeat="menuItem in menuItems"><span class="fa {{menuItem.icon}} fa-lg"></span>{{menuItem.itemName}}
<span ng-show='menuItem.subItems.length > 0'>
<ul>
<li ng-repeat="subItem in menuItem.subItems">{{subItem.itemName}}</li>
</ul>
</span>
</li>
checking if an array has a length of 0 is not an expensive operation. if you want to only show lists that have item, put a filter on the outer array that takes an array of arrays and returns only the arrays that have a length different than 0.
you can also hide the inner div if the array == false.
http://plnkr.co/edit/gist:3510140
http://plnkr.co/edit/Gr5uPnRDbRfUYq0ILhmG?p=preview
Your plunkr was pretty complicated and hard to weed through so I re-created what you wanted using a fiddle. The general idea behind my approach is to filter out the items from the array, not the sub array. And only do the filtered items when the text changes. So here's the markup:
<div ng-app="app">
<div ng-controller="ParentCtrl">
<input data-ng-model="filterText" data-ng-change="updateTypes()" />
<div data-ng-repeat="type in filteredTypes">
{{ type.name }}
<ul>
<li style="margin-left:20px;" data-ng-repeat="entry in type.entries">
- {{ entry.name }}
</li>
</ul>
</div>
</div>
</div>
And here's the code:
angular.module('app', [])
function ParentCtrl($scope){
$scope.filterText = "";
$scope.types = [
{ name: "type1", entries: [{ name: "name1"}, { name: "name2"}, { name: "name3"}]},
{ name: "type2", entries: [{ name: "name1"}, { name: "name3"}, { name: "name3"}]},
{ name: "type3", entries: [{ name: "name1"}, { name: "name2"}, { name: "name5"}]},
{ name: "type4", entries: [{ name: "name4"}, { name: "name2"}, { name: "name3"}]}
];
$scope.filteredTypes = [];
$scope.updateTypes = function(){
$scope.filteredTypes.length = 0;
for(var x = 0; x < $scope.types.length; x++){
if($scope.filterText === ""){
$scope.filteredTypes.push($scope.types[x]);
}
else{
var entries = [];
for(var y = 0; y < $scope.types[x].entries.length; y++){
if($scope.types[x].entries[y].name.indexOf($scope.filterText) !== -1){
entries.push($scope.types[x].entries[y]);
}
}
if(entries.length > 0){
$scope.filteredTypes.push({
name: $scope.types[x].name,
entries: entries
});
}
}
}
}
$scope.updateTypes();
}
Fiddle: http://jsfiddle.net/2hRws/
The reason I'm creating a new array and not using an actual filter to remove the results is that angular doesn't like creating dynamic arrays on the fly in filters. This is because it doesn't assign $$hashKey and things just don't line up correctly when dirty checking. I got the idea of how to do what you needed from this topic on the matter: https://groups.google.com/forum/#!topic/angular/IEIQok-YkpU
I have only slightly modified your list-widget.html, see it in action: plunkr
The idea is simple - use the same filter for ng-show:
<div ng-show="group | filter:searchText">{{ key }}</div>
The label will be visible only if there are some unfiltered items.
In my example I'm using searchText for filter because I'm not familiar with CoffeeScript.

Resources