AngularJS, problems with $index variable in ng-repeat - javascript

I'm trying to fill out a 2 column table in AngularJS. I'm using ng-repeat directive to fill out the table, but it's not's working the way I'm planning. My $scope.items is: [Coors, Jameson, Bacardi, Corona]
I want the table to look like this:
| Coors (0) | Jameson (1) |
| Bacardi (2) | Corona (3) |
however, it looks like this:
| Coors (0) | Coors (1) |
| Bacardi (2) | Bacardi (3) |
I'm confused as to why the [$index+1] directive in the my script is only working in the actual text portion of the script (in parenthesis), while the <item-card> div does not seem to properly displaying items[$index+1], and instead is displaying items[$index]. Here is my script:
<div class=row ng-repeat="item in items" ng-if="$index %2 ==0">
<div class="col col-50" ng-if="$index < items.length">
<item-card item="{{item[$index]}}"></item-card>
({{$index}})
</div>
<div class="col col-50" ng-if="$index +1 < items.length">
<item-card item="{{items[$index+1]}}"></item-card>
({{$index+1}})
</div>
</div>
Does anyone know why this might not be working as intended?
Edit: Including is itemcard.html.
<div class = "card" >
<img id = "cardImage" ng-src= "data:image/jpeg;base64,{{item.image}}" width = "100%"/>
{{item.cartQuantity}}
<cardHeader>{{item.itemName}}</cardHeader><br>
<cardHeader ng-if= "item.paksize >1">{{item.paksize}} pack</cardHeader>
<button class="button" ng-click="addToCart(item)">+</button>
<button class="button" ng-click="decrementCart(item)">-</button>
</div>

What you're trying to do seems a little odd to me. Rather than trying to split the array of items into even chunks of 2 columns with Directive Fu, might you consider using lodash.chunk?
<script>
angular.module('example', [ ])
.run(function($scope){
$scope.items = _.chunk([ /* . . . */ ], 2);
});
</script>
<div ng-app="example">
<div class=row ng-repeat="chunk in items">
<div class="col col-50">
<item-card item="{{chunk[0]}}"></item-card>
({{$index}})
</div>
<div class="col col-50">
<item-card item="{{chunk[1]}}"></item-card>
({{$index+1}})
</div>
</div>
</div>
If you wanted to be really Angular about it (a pun!), you could register lodash.chunk as a custom Filter instead:
<script>
angular.module('example', [ ])
.run(function($scope){
$scope.items = [ /* . . . */ ];
})
.filter('chunk', function(){
return _.chunk;
});
</script>
<div ng-app="example">
<div class=row ng-repeat="chunk in items | chunk:2">
<div class="col col-50">
<item-card item="{{chunk[0]}}"></item-card>
({{$index}})
</div>
<div class="col col-50">
<item-card item="{{chunk[1]}}"></item-card>
({{$index+1}})
</div>
</div>
</div>

Related

checking the condition in ng-repeat

I am creating app in ionic /angularjs.
The controller fetches the data in JSON format from the URL and displays unique images in div elements. I want to allow these images to be clicked and then display the data according to offer_name, which is coming from the JSON data.
e.g.: Suppose I display the image for Amazon (in background offer_name is amazon (having 10 records)). When the user clicks on that, it displays all the records related to amazon.
Hope you get my point but no database is included; it only works with JSON data.
Also, how can the check current value be checked in ng-repeat?
Here is my code:
.controller('menuCtrl', function($scope,$http) {
$http.get("http://tools.vcommission.com/api/coupons.php?apikey=e159f64e3dd49fddc3bb21dcda70f10c6670ea91aac30c7cb1d4ed37b20c45b8").then(function (response) {
$scope.myData = response.data;
/* $scope.stack=[];
angular.forEach($scope.myData, function(item){
$scope.stack =item.store_image;
var uni_img=[];
for(var i=0 ; i< $scope.stack.length;i++)
{
if(uni_img.indexOf($scope.stack[i] == -1))
uni_img.push($scope.stack[i]);
}
console.log(uni_img);
})*/
});
$scope.dealopen = function($a){
for (var i=0;i<$scope.myData.length;i++)
{
//console.log($scope.data[i].name);
$link=$scope.data[i].offer_name;
if ($link==$a)
{
$window.open($link,"_self","location=yes");
console.log($a);
}
}
}
})
Html
<div class="item col-sm-2 col-sm-3 col-md-2 " ng-repeat="da in myData | unique: 'store_image'" >
<div class="thumbnail">
<img class="thumbnail img-responsive " ng-src="{{ da.store_image}}"
/>
<div class="caption">
<b class="group inner list-group-item-heading center-block">
{{da.offer_name | limitTo: 12 }} Deals</b>
<a class="item item-text-wrap" ng-href="#/Deals/{{da.offer_name}}">View Deal</a>
</div>
</div>
</div>
Here is the output:
.
One way to achieve this is to use ng-click to execute javascript (e.g. inline or call a function in the controller scope. Per the documentation for ngRepeat $index can be referenced for the current index:
Special properties are exposed on the local scope of each template instance, including:
+----------+---------+-----------------------------------------------------------------------------+
| Variable | Type | Details |
+----------+---------+-----------------------------------------------------------------------------+
| $index | number | iterator offset of the repeated element (0..length-1) |
| $first | boolean | true if the repeated element is first in the iterator. |
| $middle | boolean | true if the repeated element is between the first and last in the iterator. |
| $last | boolean | true if the repeated element is last in the iterator. |
| $even | boolean | true if the iterator position $index is even (otherwise false). |
| $odd | boolean | true if the iterator position $index is odd (otherwise false). |
+----------+---------+-----------------------------------------------------------------------------+
1
So the image tag can have the attribute ng-click to utilize that directive, like this:
<img class="thumbnail img-responsive " ng-src="{{ da.store_image}}" ng-click="showData(da.offer_name, $index)"/>
Then use Array.filter() to filter all offers into a filtered array of offers matching the offer_name:
$scope.showData = function (offer_name, index) {
$scope.offerName = da.offer_name;
$scope.filteredOffers = $scope.myData.filter(function(offer) {
return offer.offer_name == $scope.offerName;
});
}
And then add another set of elements to display the items in filteredOffers.
<div ng-repeat="offer in filteredOffers">
<div class="couponCode">{{offer.coupon_code}}</div>
<div class="couponTitle">{{offer.coupon_title}}</div>
<div class="couponDescription">{{offer.coupon_Description}}</div>
</div>
See the example below where the showData function updates the model selectedIndex, offerName and filteredOffers using these components.
angular.module('myApp', ['ui'])
.controller('menuCtrl', ['$scope', '$http',
function($scope, $http) {
$scope.offerName = ''; //set initially
$scope.selectedIndex = -1;
$scope.filteredOffers = [];
$http.get("http://tools.vcommission.com/api/coupons.php?apikey=e159f64e3dd49fddc3bb21dcda70f10c6670ea91aac30c7cb1d4ed37b20c45b8").then(function(response) {
$scope.myData = response.data;
});
$scope.showData = function(offer_name, index) {
$scope.offerName = offer_name;
$scope.filteredOffers = $scope.myData.filter(function(offer) {
return offer.offer_name == $scope.offerName;
});
$scope.selectedIndex = index;
}
}
]);
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js" data-semver="1.1.5" data-require="angular.js#*" context="anonymous"></script>
<script data-require="angular-ui#*" data-semver="0.4.0" src="//rawgithub.com/angular-ui/angular-ui/master/build/angular-ui.js" context="anonymous"></script>
<div ng-app="myApp" ng-controller="menuCtrl">
<div>
OfferName:
<span ng-bind="offerName"></span>
</div>
<div>
selected index:
<span ng-bind="selectedIndex"></span>
</div>
<div class="item col-sm-2 col-sm-3 col-md-2 " ng-repeat="da in myData | unique: 'store_image'">
<div class="thumbnail">
<img class="thumbnail img-responsive " ng-src="{{ da.store_image}}" ng-click="showData(da.offer_name, $index)" />
<div class="caption">
<b class="group inner list-group-item-heading center-block">
{{da.offer_name | limitTo: 12 }} Deals</b>
<a class="item item-text-wrap" ng-href="#/Deals/{{da.offer_name}}">View Deal</a>
</div>
</div>
</div>
<div ng-repeat="offer in filteredOffers">
<div class="couponCode">{{offer.coupon_code}}</div>
<div class="couponTitle">{{offer.coupon_title}}</div>
<div class="couponDescription">{{offer.coupon_Description}}</div>
</div>
</div>
1 https://docs.angularjs.org/api/ng/directive/ngRepeat
You can use $index, that is the variable used in agular to know the index position in ng-repeat, if you want to know more about another contro variables of ng-repeat read the docs in the next link https://docs.angularjs.org/api/ng/directive/ngRepeat
An basic example is.
CONTROLLER
app.controller('menuCtrl', function($scope,$http) {
$scope.myData = [{path:'http://www.imagefree1.com',info:'http://www.infoimage1.com'},
{path:'http://www.imagefree2.com',info:'http://www.infoimage2.com'},
{path:'http://www.imagefree3.com',info:'http://www.infoimage3.com'}];
$scope.callDetail=function(index)
window.open(myData[index].info);
})
HTML
<div ng-repeat="image in myData">
<a href="#" ng-click="callDetail($index)">
<img src="image.path" alt="Description"/>
</a>
</div>
At first look you never call the $scope.dealopen function,
may be this can work....
<div class="item col-sm-2 col-sm-3 col-md-2 " ng-repeat="da in myData | unique: 'store_image'" >
<div class="thumbnail">
<img class="thumbnail img-responsive " ng-src="{{ da.store_image}}"/>
<div class="caption">
<b class="group inner list-group-item-heading center-block">{{da.offer_name | limitTo: 12 }} Deals</b>
<a class="item item-text-wrap" ng-click="dealopen(da.offer_name)">View Deal</a>
</div>
</div>
</div>

Angular - loop over nested javascript arrays

How can I use ng-repeat to loop over data that contains many nested array data?
I have data that will have many "Segments"
Example:
confirm.booking.flightData[0].Segments[0].FlightNumber
confirm.booking.flightData[0].Segments[1].FlightNumber
confirm.booking.flightData[0].Segments[2].FlightNumber
I have done both ng-repeat with angular, and without angular I would end up resorting to javascript that loops over data and creates the html dynamically, but I wish to do this the ANGULAR way.. HOW?
HTML with Angular/Javascript Arrays:
<div class="container-fluid">
<div class="row">
<div class="col-md-4">
<span style="font-weight: bold;">Flight</span>
</div>
<div class="col-md-4">
<span style="font-weight: bold;">Departs</span>
</div>
<div class="col-md-4">
<span style="font-weight: bold;">Arrives</span>
</div>
</div>
<div class="row">
<div class="col-md-4">
{{confirm.booking.flightData[0].Segments[0].FlightNumber}}
</div>
<div class="col-md-4">
({{confirm.booking.flightData[0].Segments[0].DepartureAirport}})
</div>
<div class="col-md-4">
({{confirm.booking.flightData[0].Segments[0].ArrivalAirport}})
</div>
</div>
</div>
Nesting can be done in repeats, but repeating too much in ng-repeats can be costly in terms of performance as angular creates scopes for each element repeated. Hence, filtering data till the perfect abstracted values that you need in terms of html should be done in the js file.
For eg: if u need only segements in the html form do this, or if u need even flight data in html form follow #Rachel's post
<ul data-ng-repeat="item in confirm.booking.flightData[0].Segments">
<li>{{ item.FlightNumber}}</li>
</ul>
Let's say your data is in flightdetails, then you can go about it like this:
<div ng-repeat="a in flightdetails ">
<div ng-repeat="b in a.booking">
<div ng-repeat="c in b.flightdata">
<div ng-repeat="d in c.segments">
{{d.flightnumber}}
</div>
</div>
</div>
</div>
You can use nested ng-repeat to bind your data - see a demo below:
angular.module("app", []).controller("ctrl", function($scope) {
$scope.confirm = {
booking: {
flightData: [{
Segments: [{
FlightNumber: 1
}, {
FlightNumber: 2
}]
}, {
Segments: [{
FlightNumber: 3
}, {
FlightNumber: 4
}]
}]
}
}
// console.log($scope.confirm);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="wrapper" ng-app="app" ng-controller="ctrl">
<div ng-repeat="x in confirm.booking.flightData">
Data {{$index + 1}}:
<div ng-repeat="y in x.Segments">
<div>Flight No: {{y.FlightNumber}}</div>
</div>
<br/>
</div>
</div>
If you want to display only the following:
confirm.booking.flightData[0].Segments[0].FlightNumber
confirm.booking.flightData[0].Segments[1].FlightNumber
confirm.booking.flightData[0].Segments[2].FlightNumber
then you can use limitTo - see demo below:
angular.module("app", []).controller("ctrl", function($scope) {
$scope.confirm = {
booking: {
flightData: [{
Segments: [{
FlightNumber: 1
}, {
FlightNumber: 2
}]
}, {
Segments: [{
FlightNumber: 3
}, {
FlightNumber: 4
}]
}]
}
}
// console.log($scope.confirm);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="wrapper" ng-app="app" ng-controller="ctrl">
<div ng-repeat="x in confirm.booking.flightData | limitTo : 1">
Data {{$index + 1}}:
<div ng-repeat="y in x.Segments">
<div>Flight No: {{y.FlightNumber}}</div>
</div>
<br/>
</div>
</div>
I created an example here:
http://codepen.io/ackzell/pen/ENBymo
It ultimately looks like this, but check the pen as it has some more info:
<ul>
<li ng-repeat="flight in vm.flightData">
<ul>
<li ng-repeat="segment in flight.Segments">
<em>FlightNumber</em> {{ segment.FlightNumber }}
<br />
<em>Departure:</em> {{ segment.DepartureAirport }}
<br />
<em>Arrival:</em> {{ segment.ArrivalAirport }}
</li>
</ul>
</li>
</ul>
Nesting ng-repeats would help, but maybe you want to give your data some treatment first.

How to remove underscores from json key value

I have a json file like
{
"Asian_Cities_Countries":[
{"name":"Beijing",
"country":"China"
},
{"name":"Ankara",
"country":"Turkey"
}
],
"European_Cities_Countries":[
{"name":"Paris",
"country":"France"
},
{"name":"Madrid",
"country":"Spain"
}
]
}
It is just a part of a json file the actual json is quite big.
I am fetching this json through angularjs and displaying it my html page as
<div class="panel-group" id="accordion">
<div ng-repeat="key in notSorted(items) track by $index" class="panel panel-default menu-panel" ng-init="value = items[key]" style="margin-bottom:10px;">
<a data-toggle="collapse" data-parent="#accordion" id="menu-link" href="#{{key}}">
<div class="panel-heading panel-types">
<h4 class="panel-title">
{{key}}
</h4>
</div></a>
<div id="{{key}}" class="panel-collapse collapsing menu-items">
<div ng-repeat="item in value">
<div class="row">
<div class="col-sm-12 col-lg-3">
<p class="item-name">
{{item.name}}
</p>
</div>
<div class="col-sm-12 col-lg-3">
<p>{{item.country}}</p>
</div>
</div>
</div>
</div>
</div>
</div>
Now I want to remove underscores from the key value and replace it with blank spaces.How do i do it.
I tried {{key.replace('_',' ')}} . But it removes only the first underscore and not all of them.
var app = angular.module("app",[]);
app.controller("ctrl" , function($scope){
$scope.items = {
"Asian_Cities_Countries":
[
{"name":"Beijing","country":"China"},
{"name":"Ankara","country":"Turkey"}
],
"European_Cities_Countries":
[
{"name":"Paris","country":"France"},
{"name":"Madrid","country":"Spain"}
]
};
});
app.filter('removeUnderscores', [function() {
return function(string) {
if (!angular.isString(string)) {
return string;
}
return string.replace(/[/_/]/g, ' ');
};
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl" class="panel-group" id="accordion">
<div ng-repeat="(key,value) in items track by $index" class="panel panel-default menu-panel" ng-init="value = items[key]" style="margin-bottom:10px;">
<a data-toggle="collapse" data-parent="#accordion" id="menu-link" href="#{{key}}">
<div class="panel-heading panel-types">
<h4 class="panel-title">
{{key | removeUnderscores}}
</h4>
</div></a>
<div id="{{key}}" class="panel-collapse collapsing menu-items">
<div ng-repeat="item in value">
<div class="row">
<div class="col-sm-12 col-lg-3">
<p class="item-name">
{{item.name}}
</p>
</div>
<div class="col-sm-12 col-lg-3">
<p>{{item.country}}</p>
</div>
</div>
</div>
</div>
</div>
</div>
try this. use a filter similar this
app.filter('removeUnderscores', [function() {
return function(string) {
if (!angular.isString(string)) {
return string;
}
return string.replace(/[/_/]/g, ' ');
};
}])
and in html
{{key | removeUnderscores}}
Seems you forgot the global modifier, try this:
key.replace(/_/g,' ')
with "g" all occurences should be replaced
The AngularJS Docs specifically state that Angular Expressions don't include regular expressions.
Angular Expressions vs. JavaScript Expressions
Angular expressions are like JavaScript expressions with the following differences:
No RegExp Creation With Literal Notation: You cannot create regular expressions in an Angular expression.
-- AngularJS Developer Guide - Expressions
Instead use a custom filter as recommended by #hadijz
app.filter('removeUnderscores', [function() {
return function(string) {
if (!angular.isString(string)) {
return string;
}
return string.replace(/[/_/]/g, ' ');
};
}])
and in html
{{key | removeUnderscores}}
Just a question. Why aren't you processing the json file/data within a service? This much processing (especially string replacing) isn't meant to be done in either the HTML or the controller in AngularJS.
I'd recommend that you parse the json inside a service and then pass it to the controller. That's usually the best practice..
Please see this JSBIN for reference. Thank you

Simple previous\next slider using angular

I have a web page which displays a list of items, the number of items can get quite big so I would like to display only 3 at a time and have next\previous buttons which let the user navigate between items.
I'm very new to Angular but I managed to retrieve all the items and display them on the UI but have no idea where to start to only display three and wire up the next and previous buttons to enable navigation.
Here's my code:
JS:
var app = angular.module('myApp', []);
app.controller('servicesController', function ($scope, $http) {
$http.get(url + "api/MapServiceAPI/GetServers")
.success(function (response) {
$scope.servers = response.Result;
});
});
HTML:
<div class="row top-space" ng-app="myApp" ng-controller="servicesController">
<div class="pull-left">
<img src="~/Content/Images/Service/PREVIOUS.png" />
<h4>PREVIOUS</h4>
</div>
<div class="pull-right">
<img src="~/Content/Images/Service/NEXT.png" />
<h4>NEXT</h4>
</div>
<ul class="col-md-3 text-center" ng-repeat="s in servers" ng-click="serviceClick(s.ServiceId)">
<li>
<div class="container">
<h4>{{ s.ServerName }}</h4>
</div>
<div class="container">
<img src="~/Content/Images/Server/SERVER.png" />
</div>
<div class="container">
<h5>{{ s.ServerDescription }}</h5>
</div>
</li>
</ul>
</div>
you can achieve using filters limitTo property
> <li ng-repeat="datalist in datalists | pagination: curPage * pageSize
> | limitTo: pageSize">
refere this jsfiddle which help you to understand better.
http://jsfiddle.net/dulcedilip/x7tg15v9/

Give a value to an ng-model="searchText" input based on list item clicked in Angular JS

I created a list that is searchable with an input using angular.js (ng-model="searchText"). On click of the list elements you can pull the content of the selected item using {{selected | json}}. I want to apply this selected element as the value of the input when a list item is clicked.
I essentially want to create my own autocomplete that always displays a list of options, filters options while typing, and fills in the input on click of an option.
HTML
<html ng-app>
<section>
<div id="content-body" class="container search-page"><!-- begin content-body -->
<div id="content-frame" class="container"></div><!-- end content-frame -->
<section>
<div class="search-container">
<div class="search-wrapper">
<input type="search" class="input-from" autocomplete="off" placeholder="Search" ng-model="searchText" value="{{searchText}}">
</div>
</div>
</section>
<section>
<div ng-controller="ContentCtrl">
<ul class="list-group search">
<!--<li class="list-group-item group-title">
<span class="icon itinerary-icon"></span>
Popular Destinations
</li>-->
<a ng-click="setMaster(cities)" ng-repeat="city in cities | filter:searchText | limitTo:limit" href="#" class="list-group-item">{{city}}</a>
</ul>
</div>
</section>
</div><!-- end content-body -->
</html>
JS
function ContentCtrl($scope, $http) {
"use strict";
$scope.url = '../mobile-site/cities.json';
$scope.cities = [];
$scope.limit = 10; // max 10 cities loaded
$scope.fetchContent = function() {
$http.get($scope.url).then(function(response){
$scope.cities = response.data.cities;
});
}
$scope.fetchContent(); // build cities list
$scope.setMaster = function(city) {
$scope.searchText = city; // pull selected city using {{selected | json}}
}
}
JSON
{
"version": "082B6AF45261B81358E8F99F0FAEC4A4",
"cities": [
"A Friela Maside, Spain",
"A Gudina, Spain",
"AHA, Germany",
"AL, Norway"
]
}
I think you have a few mistakes in the code:
<div ng-model="chosen">
<a ng-click="setMaster(cities)" ng-repeat="cities in cities | filter:searchText | limitTo:limit" href="#" class="list-group-item">{{cities}}</a>
</div>
shoud be
<div ng-repeat="city in cities | filter:searchText | limitTo:limit">
<a ng-click="setMaster(city)" class="list-group-item">{{city}}</a>
</div>
then I think what you want is
$scope.setMaster = function(city) {
$scope.searchText = city;
}
Also, I'm not sure what the ng-model on the div is for, as far as I know it is used only for inputs, textareas and selects. See http://docs.angularjs.org/api/ng.directive:ngModel.
You probably want something like this fiddle -> http://jsfiddle.net/weSpW/3/.

Categories