Let's say we have an array in our controller as follows:
$scope.elements = [
{
title: title-1,
desc: description-1
},
{
title: title-2,
desc: description-2
},
{
title: title-3,
desc: description-3
},
{
title: title-4,
desc: description-4
}
]
I wish to loop through the array so I can place the elements as follows:
<div class="row">
<div class="col-sm-6">
{{ elements[0].title }}
{{ elements[0].desc }}
</div>
<div class="col-sm-6">
{{ elements[1].title }}
{{ elements[1].desc }}
</div>
</div>
<div class="row">
<div class="col-sm-6">
{{ elements[2].title }}
{{ elements[2].desc }}
</div>
<div class="col-sm-6">
{{ elements[3].title }}
{{ elements[3].desc }}
</div>
</div>
...and so on.
This could be achieved if we could fetch two consecutive elements simultaneously via an ng-repeat and pass it to the directive. Can this be done? Also if so, how would the fetched array objects be handled inside the directive?
I'd use the $index property to do this. The hiding with ng-if is a little inefficient, if this is a huge repeat, you might want a more elegant solution.
<div class="row" ng-repeat="element in elements" ng-if="$index <= elements.length /2">
<div class="col-sm-6">
{{ elements[$index*2].title }}
{{ elements[$index*2].desc }}
</div>
<div class="col-sm-6" ng-if="elements[$index*2 + 1]"> //ng-if for if you want to remove last element if odd array.
{{ elements[$index*2 + 1].title }}
{{ elements[$index*2 + 1].desc }}
</div>
</div>
Seems like this would be as simple as creating another array to iterate over
$scope.TemplateArray = [
[0,1],
[2,3],
etc...
]
Then use this for your ng-repeat
array in TemplateArray
<div class="row">
<div class="col-sm-6">
{{ elements[array[0]].title }}
{{ elements[array[0]].desc }}
</div>
<div class="col-sm-6">
{{ elements[array[1]].title }}
{{ elements[array[1]].desc }}
</div>
</div>
Probably the best way to do this is going to be to pre-process your array into groups of however many columns you have and then ng-repeat over that array. So pre-process your elements into something that looks like:
$scope.processedElements = [
[{title:'1',desc:'1d'},{title:'2',desc:'2d'},{title:'3',desc:'3d'},{title:'4',desc:'4d'}],
[{title:'5',desc:'5d},....... ]
];
and then
<div ng-repeat="item in processedElements">
{{item[0].title}} {{item[1].title}} // etc. etc
</div>
I think this can solve your problem to a limit
var app = angular.module('plunker', []);
app.controller('example', function($scope) {
$scope.limit = 2;
$scope.elements = [];
$scope.$watch('limit', function() {
$scope.elements = elements;
});
elements = [{
title: 'title-1',
desc: 'description-1'
}, {
title: 'title-2',
desc: 'description-2'
}, {
title: ' title-3',
desc: 'description-3'
}, {
title: 'title-4',
desc: 'description-4'
}];
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="example">
<input type="number" ng-model="limit">
<div class="row">
<div class="col-sm-6" ng-repeat="el in elements" ng-if="$index < limit">
{{el.title}} {{el.desc}}
</div>
</div>
<p>seperator</p>
<div class="row">
<div class="col-sm-6" ng-repeat="el in elements" ng-if="$index >= limit">
{{el.title}} {{el.desc}}
</div>
</div>
</div>
</body>
</html>
Related
Get 2 json files from backend , and need to mix it !
First ng-repeat:
<p ng-repeat="category in categories track by category.id" >
{{ category }}
</p>
result
{"id":"Category-1"}
{"id":"Category-2"}
{"id":"Category-3"}
Second ng-repeat:
<p ng-repeat="game in gamesList track by game.id">
{{ game }}
</p>
result
{"Category-id":"1","title":"Game-1"}
{"Category-id":"1","title":"Game-2"}
{"Category-id":"2","title":"Game-3"}
{"Category-id":"3","title":"Game-4"}
{"Category-id":"3","title":"Game-5"}
I need to mix this 2 values and get something like this:
Category-1
Game-1
Game-2
Category-2
Game-3
Category-3
Game-4
Game-5
You do not have to mix both json, you can just use the 2nd json and use GroupBy Filter
DEMO
angular.module('datepickerBasicUsage', ['angular.filter'])
.controller('AppCtrl', function ($scope) {
$scope.gamesList = [{"Categoryid":"1","title":"Game-1"},
{"Categoryid":"1","title":"Game-2"},
{"Categoryid":"2","title":"Game-3"},
{"Categoryid":"3","title":"Game-4"},
{"Categoryid":"3","title":"Game-5"}];
});
<!doctype html>
<html ng-app="datepickerBasicUsage">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.8/angular-filter.js"></script>
</head>
<body>
<div ng-controller="AppCtrl" style='padding: 40px;'>
<ul ng-repeat="(key, value) in gamesList | groupBy: 'Categoryid'">
<h3> Category: {{ key }} </h3>
<li ng-repeat="player in value">
{{ player.title }}
</li>
</ul>
</div>
</body>
</html>
AngularJS V1.6.4
$scope.aCourse["name"] is logged to console correctly, but in the HTML code nothing is populated into the screen.
$scope.getCourse = function(idd){
$http.defaults.headers.common['Authorization'] = 'Basic ' + btoa($cookieStore.get('username') + ':' + $cookieStore.get('password') );
$http({
method: 'GET',
url: 'http://localhost:8080/course/'+idd,
}).then(function successCallback(response) {
$scope.aCourse = response.data;
console.log($scope.aCourse["name"]);
window.location = "/website-take-course.html";
}, function errorCallback(response) {
alert("Course data in fetching failed");
});
}
HTML Code:
<div class="page-section padding-top-none" ng-repeat="c in aCourse" >
<div class="media media-grid v-middle">
<div class="media-left">
<span class="icon-block half bg-blue-300 text-white">1</span>
</div>
<div class="media-body" >
<h1 class="text-display-1 margin-none" >{{c.name}}</h1>
</div>
</div>
<br/>
<p class="text-body-2">{{c.description}}</p>
</div>
Based on your post, it lookes like $scope.aCourse is a object, not an array.
change it as follows,
<div class="page-section padding-top-none" ">
<div class="media media-grid v-middle">
<div class="media-left">
<span class="icon-block half bg-blue-300 text-white">1</span>
</div>
<div class="media-body">
<h1 class="text-display-1 margin-none">{ aCourse.name }}</h1>
</div>
</div>
<br/>
<p class="text-body-2">{{aCourse.description}}</p>
</div>
or use something like this to iterate over object,
<div ng-repeat="(key,value) in aCourse">
{{key}} : {{value}}
</div>
DEMO
var app = angular.module('filterApp', []);
app.controller('myCtrl', function($scope) {
$scope.aCourse = {
"content": "SO",
"description": "Programmers"
};
});
<!DOCTYPE html>
<html >
<head>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script src="https://code.angularjs.org/1.4.7/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="filterApp" ng-controller="myCtrl">
<div ng-repeat="(key,value) in aCourse">
{{key}} : {{value}}
</div>
</body>
</html>
There could be a two situations :
1. $scope.aCourse is an array of objects [{},{},{}].
DEMO
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope) {
$scope.aCourse = [
{
"name": "alpha",
"description" : "description1"
},
{
"name": "beta",
"description" : "description2"
},
{
"name": "gamma",
"description" : "description3"
}
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<div class="page-section padding-top-none" ng-repeat="c in aCourse" >
<div class="media-body" >
<h1 class="text-display-1 margin-none" >{{c.name}}</h1>
</div>
<p class="text-body-2">{{c.description}}</p>
</div>
</div>
2. $scope.aCourse is an Object {......}.
DEMO
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl', function($scope) {
$scope.aCourse = {
"name": "alpha",
"description" : "description1"
};
console.log($scope.aCourse["name"]);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<div class="page-section padding-top-none" ng-repeat="(key, value) in aCourse" >
<div class="media-body" >
<h1 class="text-display-1 margin-none" >{{value}}</h1>
</div>
</div>
</div>
In your angular code you are setting aCourse to the response data. You then access the data as an object with:
$scope.aCourse["name"]
Then in your html you are running an ng-repeat on $scope.aCourse as if it were an array of objects:
<div class="page-section padding-top-none" ng-repeat="c in aCourse" >
You would either need to make aCourse an array of objects to use your current html, or update your html and access the object in aCourse with aCourse.name and aCourse.description.
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>
My target is to show "noSets" template if observableArray length less 0 and render template with item details - "showSets", if observableArray length greater 0. I'd like to use templates for this purpose, but FireBug show error: Cannot find template with ID "templatename".
Here is ViewModel:
function SetsViewModel() {
var self = this;
self.usersets = ko.observableArray();
self.getTemplate = function () {
return self.usersets().length > 0 ? "showSets" : "noSets";
}
}
$(document).ready(function () {
ko.applyBindings(new SetsViewModel(), document.getElementById('user_sets'));
});
And here is HTML markup:
<div data-bind="template: { name: $root.getTemplate, foreach: usersets }" id="user_sets">
<script type="text/html" id="noSets">
<p>You do not have items yet.</p>
</script>
<script type="text/html" id="showSets">
<div class="block">
<input type="hidden" data-bind="value: $data.SetId" />
<div class="fav" data-bind="css: { fullop: $data.IsFavorite == true }">
<img alt="" src="img/fav.png" data-bind="click: $root.setFavorite">
</div>
<div>
<img alt="" data-bind="attr: { src: $data.SetImg }">
</div>
<div class="txt">
<h3 data-bind="text: $data.SetName, click: $root.go"></h3>
<p><span data-bind="text: $data.ItemsNumber + ' вещей,'"></span><span data-bind=" text: ' общая цена ' + $data.SetPrice + ' руб'"></span></p>
</div>
</div>
</script>
</div>
How can I fix it?
You can declare the templates outside the div that you're binding to as a work around. As #JeffMercado states:
The actual problem was that since the user_sets uses a template binding, the body is discarded (and the templates along with it)
function SetsViewModel() {
var self = this;
self.usersets = ko.observableArray([{SetId: 1, SetName: 'Name 1'}]);
self.getTemplate = function () {
return self.usersets().length > 0 ? "showSets" : "noSets";
}
}
$(document).ready(function () {
ko.applyBindings(new SetsViewModel(), document.getElementById('user_sets'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script type="text/html" id="noSets">
<p>You do not have items yet.</p>
</script>
<script type="text/html" id="showSets">
<div class="block">
<input data-bind="value: $data.SetId" />
<div class="txt">
<h3 data-bind="text: 'Set name:' + $data.SetName"></h3>
</div>
</div>
</script>
<div data-bind="template: { name: getTemplate, foreach: usersets }" id="user_sets">
</div>
I think named templates are potentially the wrong approach to this specific issue - they're more designed for when you want a different template for each item, which isn't the case here. Instead, it would be more appropriate to show an entirely different div when there are 0 items:
<div data-bind="visible: usersets().length == 0">
You have no sets
</div>
<div data-bind="visible: usersets().length > 0, foreach: usersets" id="user_sets">
<div class="block">
<input type="hidden" data-bind="value: $data.SetId" />
<div class="fav" data-bind="css: { fullop: $data.IsFavorite == true }">
<img alt="" src="img/fav.png" data-bind="click: $root.setFavorite">
</div>
<div>
<img alt="" data-bind="attr: { src: $data.SetImg }">
</div>
<div class="txt">
<h3 data-bind="text: $data.SetName, click: $root.go"></h3>
<p><span data-bind="text: $data.ItemsNumber + ' вещей,'"></span><span data-bind=" text: ' общая цена ' + $data.SetPrice + ' руб'"></span></p>
</div>
</div>
</div>
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.