I have a multidimensional array from an API. Is it possible to programatically loop through the array?
{
success: true,
categories: [{
cat_id: "2",
name: "This is category One",
description: null,
photo_url: "/img/test.png",
order: "1",
items: [{
item_id: "1",
title: "Desk",
item_url: "/690928460",
photo_url: "/desk.png",
}, {
item_id: "2",
title: "Chair",
item_url: "/18882823",
photo_url: "/chair.png",
},
}]
}]
}
My controller looks like this:
myApp.controller('items', function($scope, $http, $location, Data) {
var apiUrl = '/api/items';
$http.get(apiUrl).
success(function(data) {
$scope.data = Data;
$scope.allData = data;
});
$scope.changeView = function(view) {
$location.path(view);
}
});
Angular index file just has: <div ng-view=""></div>
View file
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="row" ng-repeat="categories in allData">
<div class="col-xs-6" ng-repeat="category in categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
</div>
I can loop through the category names fine, but when trying to return items for EACH category I don't understand the logic behind it...
I would suggest some simple nested for loops, as for each gives rise to more complexity.
As I'm not sure what you want to do with the data let's just create an array of all item names and one of all category names:
Within your success function:
var items = [], categories = []
for(var i = 0; i < data.categories.length;i++){
categories.push(data.categories[i].name);
for(var j = 0; j < data.categories[i].items.length;j++){
items.push(data.categories[i].items[j].name);
}
}
console.log(categories);
console.log(items);
EDIT:
Completely missed your html code somehow, here is my solution:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="items">
<div class="title">
{{ category.name }}
</div>
</div>
</div>
</div>
</div>
EDIT 2:
As to your comment:
If you want to select the secondary view's contents(ie the items) based on the selection of a category I would suggest a ng-click attribute. A directive could be used but isn't necessary:
<div class="scrollable categories-container animated-fast slideInUp">
<div class="container categories">
<div class="col-xs-6" ng-repeat="category in allData.categories">
<div class="title" ng_click="selected_category = category">
{{ category.name }}
</div>
</div>
<div class="col-xs-6" ng-repeat="item in selected_category.items">
<div class="title">
{{ item.name }}
</div>
</div>
</div>
</div>
So when your categories data is loaded the first ng-repeat is populated with the categories. Each div with class title will have a function called on click which will make the selected_category object equal the selected category.
This will then cause the second view to be populated with all the items in the selected category by Angular's two way bind.
Related
I am taking game info from mlb.com and displaying them using angularjs and ng-repeat directive. an example of the JSON feed is below.
{
"data": {
"games": {
"next_day_date": "2017-08-19",
"modified_date": "2017-08-18T16:57:16Z",
"month": "08",
"year": "2017",
"game": {
"0": [{
"away_team_name": "CUBS"
}, {
"home_team_name": "INDIANS"
}],
"1": [{
"away_team_name": "CUBS"
}, {
"home_team_name": "INDIANS"
}]
},
"day": "18"
}
}
I am only able to display the data properly however, only 1 game. html below
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[0].away_team_name}} ({{x.games.game[0].away_win}}-{{x.games.game[0].away_loss}}) At {{x.games.game[0].home_team_name}} ({{x.games.game[0].home_win}}-{{x.games.game[0].home_loss}})<br>
<small>{{x.games.game[0].time}}</small>
</div>
<div class="card-block"></div>
</div>
I do understand [0] refers to the game ID '0' in the json feed, however is there a way to auto increment that number in {{x.games.game[0].time}} to loop through all games instead of doing each game individually like below?
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[0].away_team_name}} ({{x.games.game[0].away_win}}-{{x.games.game[0].away_loss}}) At {{x.games.game[0].home_team_name}} ({{x.games.game[0].home_win}}-{{x.games.game[0].home_loss}})<br>
<small>{{x.games.game[0].time}}</small>
</div>
<div class="card-block"></div>
</div>
<div class="card mb-3" ng-repeat="x in scoreboard" ng-if="$index > 1">
<div class="card-header" align="center">
{{x.games.game[1].away_team_name}} ({{x.games.game[1].away_win}}-{{x.games.game[1].away_loss}}) At {{x.games.game[1].home_team_name}} ({{x.games.game[1].home_win}}-{{x.games.game[1].home_loss}})<br>
<small>{{x.games.game[1].time}}</small>
</div>
<div class="card-block"></div>
</div>
I have tried
{{x.games.game[$index + 1].time}} but still return only 1 game
Any insight would be appreciated!
I adjusted my $scope in the controller from
.success(function (data) { $scope.scoreboard = data;} to
.success(function (data) {$scope.scoreboard = response.data.games.game;}
html now looks like
<div class="card mb-3" ng-repeat="x in scoreboard track by $index" ng-if="$index > 1">
<div class="card-header" align="center">{{x.away_team_name}} </div>
</div>
works perfectly
There are a couple of problems:
An iteration over the wrong object, the iteration should be made over scoreboard.games.game instead of scoreboard.
The syntax x in list should only be used in the ng-repeat to iterate over arrays, both scoreboard and scoreboard.games.game are NOT arrays, they're Object literals
The syntax to iterate over Object literals in ng-repeat is (key, value) in object as mentioned in the documentation.
To properly solve this problem we must iterate over the right object with the right syntax, here is how:
<div class="card mb-3" ng-if="key > 1"
ng-repeat="(key, game) in scoreboard.games.game">
<div class="card-header" align="center">
{{ game.away_team_name }}
({{ game.away_win }}-{{ game.away_loss }}) At
{{ game.home_team_name }}
({{ game.home_win }}-{{ game.home_loss }})<br />
<small>{{ game.time }}</small>
</div>
<div class="card-block"></div>
</div>
I have a website build on a MEAN stack where I have a selectbox with some citynames being retreived from MongoDB. Now I use Angular to display these names in my HTML, but now I want a default name to be displayed when the page is loaded so at least some information is shown. I've already searched what I could use, and ng-init was suggested but this did not work for me. Are there any other options?
This is my HTML:
<div ng-controller="cityCtrl">
<div class="container plumbers">
<div class="searchbox">
<div class="plumber-by-city col-sm-12" style="margin-bottom: 20px;">
<div class="title-1">Zoek op plaatsnaam</div>
<select ng-model="selectedItem" ng-options="item as item.city for item in items"></select><span class="fa fa-caret-down"></span>
</div>
</div>
</div>
</div>
This is my angular that retrieves data from DB:
app.controller('cityCtrl', function($scope,$http){
$scope.items = [];
$http.get('/getdata').then(function(d){
console.log(d);
$scope.items = d.data;
},function(err){
console.log(err);
});
});
You can use ng-selected and || operator such that if there is a value for selectedItem then it would be selected or else the first object of the array is selected by default.
var app = angular.module("MyApp", []).controller("cityCtrl", function($scope) {
$scope.items = [{city: 'abc'}, {city: 'cde'}, {city: 'fgh'}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="cityCtrl">
<div class="container plumbers">
<div class="searchbox">
<div class="plumber-by-city col-sm-12" style="margin-bottom: 20px;">
<div class="title-1">Zoek op plaatsnaam</div>
<select ng-selected="selectedItem = selectedItem || items[0]" ng-model="selectedItem" ng-options="item as item.city for item in items"></select><span class="fa fa-caret-down"></span>
</div>
{{selectedItem}}
</div>
</div>
</div>
I have a complex object as shown below:
$scope.document =
{
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
I want to display all the fields present in "GENERAL_FIELDS" and "stn". Fields' value can either be string or array of strings. If it is array, I further want to ng-repeat on it and display the content. Following is my html:
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
But ng-if="Array.isArray(value)" is never true and array fields are being displayed in object form: ["Journal","Article"]. What am I missing ?
Or add this in your controller and leave rest like it is.
$scope.isArray = angular.isArray;
html would be like this :
<div ng-if="isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!isArray(value)">
{{value}}
</div>
Instead of accessing a method on the Array object directly in the template, you should do in your controller. So for example:
<div ng-if="vm.isValueAnArray(value)">
// Some html
</div>
Your controller:
function isValueAnArray(val) {
return Array.isArray(val);
}
I haven't tested it, but logic should be in the controller, not in the template.
This is an issue of Scoping
The scope of the template is relative to $scope in the controller, so when it looks for Array, it will look for that in the controller scope (e.g. $scope.Array).
One option is to use ng-if="window.Array.isArray(value)". See the working example below.
Another option is to set $scope.Array = Array.prototype in the controller. That way there is no need to reference window before calling Array.isArray().
Another option is to create an alias for Array.isArray() in the controller scope:
$scope.isValueAnArray = Array.isArray;
Then call that function to determine if the value is an array.
angular.module('ang', [])
.controller('cont', function($scope) {
//use this to avoid referencing window in the template
//$scope.Array = Array.prototype;
$scope.document = {
"GENERAL_FIELDS": {
"Source_Type": "custom",
"Annotations": [
"216/content/Factiva_CM_001/Proteins",
"216/content/Factiva_CM_001/Fact"
],
"Content": [
" Baculovirus; Budded virus; Ultrastructure; Cryo-EM;"
],
"Title": [
"Budded baculovirus particle structure revisited"
]
},
"stn": {
"Document_Type": [
"Journal",
"Article"
]
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ang" ng-controller="cont">
<div id="titsec" class="comdocdet" ng-repeat="(category, group) in document">
<div ng-repeat="(key, value) in group">
<div class="pTitle">
{{key}}
</div>
<div class="contdesc">
<div ng-if="window.Array.isArray(value)">
<div ng-repeat="v in value">
{{v}}
</div>
</div>
<div ng-if="!window.Array.isArray(value)">
{{value}}
</div>
</div>
</div>
</div>
</div>
How to list items in AngularJS and separate them by category, something like this:
<h3>Products</h3>
<div ng-repeat="item in products">
<div>
{{item.category}}
</div>
<p>
{{item.name}}
</p>
<p>
{{item.price}}
</p>
</div>
$scope:
$scope.products = [
{
name:"product1",
price:"450",
category:"cat1"
},
{
name:"product2",
price:"450",
category:"cat2"
},
{
name:"product3",
price:"450",
category:"cat1"
}
];
I want it to look something like this:
cat1
--->product1
--->product3
cat2
--->product2
...
Please help!
See [fiddle])(http://jsfiddle.net/Lvc0u55v/2846/)
I did some formatting to message the data in desired format.
$scope.productCategories = {};
for (var i = 0; i < products.length; i++) {
if ($scope.productCategories[products[i].category] == undefined)
$scope.productCategories[products[i].category] = [];
$scope.productCategories[products[i].category].push({
name: products[i].name,
price: products[i].price
});
}
Html Formatting is off, but I hope this gives you an idea
you can use groupBy filter in ng-repeat. you should add groupBy filter dependency in your app.
var app = angular.module("app",['angular.filter']);
app.controller("ctrl" , function($scope){
$scope.products = [ { name:"product1", price:"450", category:"cat1" }, { name:"product2", price:"450", category:"cat2" }, { name:"product3", price:"450", category:"cat1" } ];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-filter/0.4.7/angular-filter.js"></script>
<div ng-app="app" ng-controller="ctrl" class="panel-group" id="accordion">
<h3>Products</h3>
<div ng-repeat="(key,value) in products | groupBy: 'category'">
<div>
{{key}}
</div>
<div ng-repeat="item in value">
<p>
{{item.name}}
</p>
<p>
{{item.price}}
</p>
</div>
</div>
</div>
I have set up a fiddle to explain my question well. I would like to display the names from the $scope.gem inside ng-repeat [only one name for each ng-repeat and don't loop all] of $scope.knobItems without extending the knobItems scope. I want this to be made possible by maintaining the exact structure of controller as it is now. I am new to angular. I just wanna know if this is possible in angular and if is a good practice.
view
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="knobs in knobItems">
<div ng-repeat="(key, value) in knobItems.nums">{{value.knobTitle}} : {{value.knobColor}}
<div ng-bind="gem[0].name"></div>
</div>
</div>
</div>
</div>
controller
var ngMod = angular.module("myapp", []);
ngMod.controller("Mycont", function ($scope) {
$scope.knobItems = {};
$scope.knobItems.nums = [{
knobTitle: "Company Profile",
knobColor: "#f46607"
}, {
knobTitle: "Deals left This Month",
knobColor: "#ffcc00"
}, {
knobTitle: "Pricelist",
knobColor: "#f40787"
}, {
knobTitle: "Pictures",
knobColor: "#a1b80a"
}, {
knobTitle: "Videos",
knobColor: "#14b9d6"
}];
$scope.gem = [{
name: "Thomas"
}, {
name: "Sebastian"
}, {
name: "June"
}, {
name: "Yuvan"
}];
});
intended output
Easy fix: fiddle
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="knobs in knobItems">
<div ng-repeat="(key, value) in knobItems.nums">{{value.knobTitle}} : {{value.knobColor}}
<div ng-bind="gem[$index].name"></div>
</div>
</div>
</div>
</div>
The output in you fiddle is exactly the same without the first ng-repeat: http://jsfiddle.net/2nrbrfxL/
Going by you description rather than you code:
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="knobs in knobItems">
<div ng-repeat="(key, value) in knobs">{{value.knobTitle}} : {{value.knobColor}}
<div ng-repeat="gemItem in gem">{{gemItem.name}}</div>
</div>
</div>
</div>
</div>
http://jsfiddle.net/p2fuq2du/
<div ng-app="myapp">
<div ng-controller="Mycont">
<div ng-repeat="(key, value) in knobItems.nums">{{value.knobTitle}} : {{value.knobColor}}
<div ng-bind="gem[key].name"></div>
</div>
</div>
</div>