I have a JSON object that I need to be able to step through each object in the array. I'm currently able to show all items, but need to be able to only show the first one on load. On button click, it needs to iterate to the next object in the array, and display only the next object, so on and so forth.
As an example of this functionality: http://jsbin.com/veyegihogi/edit?html,js,output
The current setup:
JavaScript:
var app = angular.module('app', []);
app.controller('portfolioController', function($scope, $http) {
$scope.indexToShow = 0;
$http.get("shows.json")
.then(function(response) {
console.log(response);
$scope.results = response.data;
});
$scope.change = function(){
$scope.indexToShow = ($scope.indexToShow + 1) % $scope.results.length;
};
});
Markup:
<div id="images" ng-repeat="items in results">
<div class="col-lg-2 col-md-2 col-sm-2">
<img src="{{items.image_url}}" alt="{{items.title}}">
</div>
</div>
<div class="simple-button" ng-click="change()">click me!</div>
Sample data:
[
{
"id": 1,
"title": "title 1",
"count": 14,
"image_url": "images/image.jpg"
},
{
"id": 2,
"title": "title 2",
"count": 10,
"image_url": "images/image2.jpg"
},
{
"id": 3,
"title": "title 3",
"count": 8,
"image_url": "images/image3.jpg"
}
]
If you want to stick with your jsbin...
<div id="images" ng-repeat="item in results track by $index" ng-show="$index == indexToShow">
<div class="col-lg-2 col-md-2 col-sm-2">
<img src="{{item.image_url}}" alt="{{item.title}}">
</div>
</div>
<div class="simple-button" ng-click="change()">click me!</div>
You have all the code ready for this ;)
You just want this view part instead of your ng-repeat
<div id="images">
<img src="{{results[indexToShow].image_url}}" alt="{{results[indexToShow].title}}">
</div>
<div class="simple-button" ng-click="change()">click me!</div>
Can set a scope variable for selectedItem and do the indexing in controller
$scope.results = response.data;
$scope.selectedItem = $scope.results[0];
$scope.change = function(){
var currIdx = $scope.results.indexOf($scope.selectedItem),
nextIdx = currIdx+1 <= $scope.results.length-1 ? currIdx+1 : 0;
$scope.selectedItem=$scope.results[nextIdx ];
};
View
<img src="{{selectedItem.product_image_url}}"...>
To create pager buttons set selected item in ng-click or pass item to a function and set it in controller:
<span ng-repeat="item in results"
ng-class="{active: item == selectedItem}}"
ng-click="selectedItem = item">{{$index+1}}</span>
Related
How to apply ng-style on ng-click dynamically without using ng-class in angular? something like changing color for selected active menu only. something like this DEMO but with ng-style. Below is my code which is toggle function. Can anyone solve this by changing the code or use your own example to change color or font-size for active item when clicked and rest of the items to default state.
<div ng-style="x.selected && {'color':'white','font-weight':'bold'}"
ng-click="select(x)" ng-repeat="x in myData" ></div>
var app = angular.module('anApp', ['angular.filter']);
app.controller('aCtrl', function($scope) {
$scope.data = [
{
"id": 100,
"value": "2452"
},
{
"id": 100,
"value": "2458"
},
{
"id": 1,
"value": "2457"
},
{
"id": 1,
"value": "2459"
},
{
"id": 4,
"value": "2460"
},
{
"id": 5,
"value": "3458"
}
];
$scope.select = function(x){
x.selected = !x.selected;
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-filter/0.4.7/angular-filter.js"></script>
<div ng-app="anApp" ng-controller="aCtrl">
<div ng-style="x.selected && {'color':'red','font-weight':'bold'}"
ng-click="select(x)" ng-repeat="x in data" >
{{x.value}}
</div>
</div>
Try like this.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.menuItems = ['Home', 'Contact', 'About', 'Other'];
$scope.activeMenu = $scope.menuItems[0];
$scope.setActive = function(menuItem) {
$scope.activeMenu = menuItem
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-filter/0.4.7/angular-filter.js"></script>
<div ng-app="plunker" ng-controller="MainCtrl">
<div class="account-item" ng-repeat='item in menuItems'>
<div class="account-heading" ng-style="activeMenu === item && {'background' :'red' }">
<h4 class="account-title">
{{ item }}
</h4>
</div>
</div>
</div>
Can you try using $index as below example with plunker below,
http://plnkr.co/edit/Z977olOlajTNZENbqx7D?p=preview
<div ng-repeat="x in data" ng-click="setSelected($index)" ng-style="$index === selectedId && {'background' :'red' }">
{{x.value}}
</div>
I am trying to use ngRepeat. Within that, I have a select element whos options vary depending on another select. I currently am calling vm.getOperators(filter.keyIndex) in the ng-options, but I get and indefinite loop error from angular. How can I have the following "filterOperator" select options depend on filterColumn select value within an ngRepeat?
html:
<div class="form-group row" ng-repeat="filter in vm.filters">
<div class="col-sm-4">
<select class="form-control" name="filterColumn" ng-model="filter.keyIndex">
<option ng-repeat="key in vm.filterOptions.keys"
value="{{ $index }}">
{{ key.column }}
</option>
</select>
</div>
<div class="col-sm-2">
<select class="form-control" name="filterOperator" ng-model="filter.operator" ng-options="key as value for (key, value) in vm.getOperators(filter.keyIndex)"></select>
</div>
<div class="col-sm-4" ng-model="filter.value">
<input type="text" class="form-control"/>
</div>
<div class="col-sm-1" ng-show="$last" ng-click="removeFilter($index)">
<button type="button" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span></button>
</div>
<div class="col-sm-1" ng-show="vm.filters.length > 1" ng-click="addFilter()">
<button type="button" class="btn btn-danger"><span class="glyphicon glyphicon-remove"></span></button>
</div>
</div>
controller.js:
app.controller('searchCtrl', function() {
var defaultFilter = {
keyIndex: "0",
operator: "",
value: "",
};
var operatorMap = {
"0": "=",
"1": "<",
};
var vm = this;
vm.filterOptions = {
"operators": {
"type1": ["0", "3"],
"type2": ["1", "2", "3"]
},
"keys": [
{
"datatype": "type1",
"name": "a",
"column": "col1"
},
{
"datatype": "type1",
"name": "b",
"column": "col2"
},
{
"datatype": "type2",
"name": "c",
"column": "col3"
}
]
};
vm.filters = [];
//vm.removeFilter = removeFilter;
//vm.addFilter = addFilter;
vm.getOperators = getOperators;
function getOperators(keyIndex) {
var operators = [];
var dataType = vm.filterOptions.keys[keyIndex].datatype;
if (vm.filterOptions.operators[dataType]) {
angular.forEach(vm.filterOptions.operators[dataType], function (operator, index) {
var obj = {};
obj[dataType] = (operatorMap[operator] ? operatorMap[operator] : operator);
operators.push(obj);
});
}
return operators;
}
(function init() {
// I am actually getting the filterOptions with a REST call, but I've included the data already
// The following is done after the successful REST call
// add the first filter
var filter = defaultFilter;
filter.operator = Object.keys(getOperators(filter.keyIndex))[0];
vm.filters.push(filter);
}).call();
});
Here's a plunker: https://plnkr.co/edit/yGyvwThuWWnNph72OAT0
Okay, so I figured this out.
First, don't generate an array in the getOperators method. You can return and array that is already defined, but you can't generate a new array here. This leads to an indefinite loop as angular detects a change and call the getOperators method again.
So what I did was...
after the data was fetched from the REST service, I call a function "transformOperators". This implements the looping I was doing in "getOperators" and stores this "transformed data" in a class level object.
in "getOperators" I simply do an object lookup and return the results.
For some reason, when I splice an object into my ng-repeat array, it doubles what I splice in and hides the last object in the array.
However, if I click the toggle hide and "refresh" the ng-repeat, it shows the right data.
Does anyone know why this would be happening and what I can do to fix it?
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.workflow = {
flow: [{
"id": "1334f68db820f664",
"step_number": 1,
"tasks": [{
"id": "1334f68e3f20f665"
}]
}, {
"id": "134967a5ba205f5b",
"step_number": 2,
"tasks": [{
"id": "134972c5b420e027"
}]
}, {
"id": "1334f68e7d209ae6",
"step_number": 3,
"tasks": [{
"id": "1334f68ef6209ae7"
}]
}]
};
$scope.insertStep = function() {
var insertStepIndex = 1,
task_data = {
"id": null,
"step_number": (insertStepIndex + 2),
"tasks": []
};
//go through each item in the array
$.each($scope.workflow.flow, function(index, step) {
//if the step number is greater then the place you want to insert it into, increase the step numbers
if (step.step_number > $scope.workflow.flow[insertStepIndex].step_number) step.step_number++;
});
$scope.workflow.flow.splice((insertStepIndex + 1), 0, task_data);
}
$scope.toggleHide = function() {
$scope.hide = !$scope.hide;
}
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-click="insertStep()">Insert Step</div>
<br />
<br />
<div ng-click="toggleHide()">Toggle Repeat</div>
<br />
<br />
<div ng-if="!hide">
<div ng-repeat="data in workflow.flow | orderBy:'+step_number'" ng-init="$stepIndex = workflow.flow.indexOf(data)">
{{ workflow.flow[$stepIndex].step_number }}
</div>
</div>
</div>
I got the problem. This is a tricky but the simple part. The ng-init directive only execute once. So your assignment to $stepIndex = workflow.flow.indexOf(data) will not updated when you push a new data to the array/list.
So adding a scope function will fix this problem since Angular will auto call the function when it's returning value changes.
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.workflow = {
flow: [{
"id": "1334f68db820f664",
"step_number": 1,
"tasks": [{
"id": "1334f68e3f20f665"
}]
}, {
"id": "134967a5ba205f5b",
"step_number": 2,
"tasks": [{
"id": "134972c5b420e027"
}]
}, {
"id": "1334f68e7d209ae6",
"step_number": 3,
"tasks": [{
"id": "1334f68ef6209ae7"
}]
}]
};
$scope.insertStep = function() {
var insertStepIndex = 1
var task_data = {
"id": null,
"step_number": (insertStepIndex + 2),
"tasks": []
};
//go through each item in the array
angular.forEach($scope.workflow.flow, function(step, index) {
//if the step number is greater then the place you want to insert it into, increase the step numbers
if (step.step_number > $scope.workflow.flow[insertStepIndex].step_number) step.step_number++;
});
$scope.workflow.flow.splice((insertStepIndex + 1), 0, task_data);
}
// This is a new function which I added to fix the problem
$scope.getIndex = function(data) {
return $scope.workflow.flow.indexOf(data);
};
$scope.toggleHide = function() {
$scope.hide = !$scope.hide;
};
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-click="insertStep()">Insert Step</div>
<br />
<br />
<div ng-click="toggleHide()">Toggle Repeat</div>
<br />
<br />
<div ng-if="!hide">
<div ng-repeat="data in workflow.flow | orderBy:'+step_number'">
{{ workflow.flow[getIndex(data)].step_number }}
</div>
</div>
</div>
I have been struggling on how to insert JSON items into a dynamic list :
I have a json file which look like :
[
{
"id":"34",
"City":"New York",
"Country":"USA"
},
{
"id":"22",
"City":"Las vegas",
"Country":"USA"
},
{
"id":"44",
"City":"Paris",
"Country":"France"
},
{
"id":"45",
"City":"Lyon",
"Country":"France"
}
]
I want to show it like that :
Here is my code :
<div ng-controller="customersCtrl">
<div class="list">
<div ng-repeat="c in cities">
<div class="item item-divider" >
{{ c.Country }}
</div>
<a class="item" href="#" ng-repeat="ci in cities" ng-if="c.Country == ci.Country"> {{ ci.City }} </a>
</div>
var app = angular.module('starter', []);
app.controller('customersCtrl', function($scope, $http) {
$http.get('data.json')
.then(function(result){
$scope.cities = result.data;
});
});
it's showing like that :
The easiest way to do this is pre-process the data to show the inherent structure of your data. Then you can use nested repeaters.
angular.module('cityModule', []).
controller('CityController', ['$scope',
function($scope) {
var rawData = [{
"id": "34",
"City": "New York",
"Country": "USA"
}, {
"id": "22",
"City": "Las vegas",
"Country": "USA"
}, {
"id": "44",
"City": "Paris",
"Country": "France"
}, {
"id": "45",
"City": "Lyon",
"Country": "France"
}];
$scope.citiesByCountry = {};
for (var i = 0; i < rawData.length; i++) {
var city = rawData[i];
if ($scope.citiesByCountry[city.Country] == undefined) {
$scope.citiesByCountry[city.Country] = {};
$scope.citiesByCountry[city.Country].name = city.Country;
$scope.citiesByCountry[city.Country].cities = [];
}
$scope.citiesByCountry[city.Country].cities.push(city);
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="cityModule">
<div ng-controller="CityController">
<div ng-repeat="(countryName, country) in citiesByCountry">
<h1>{{country.name}}</h1>
<div ng-repeat="city in country.cities">
{{city.City}}
</div>
</div>
</div>
</body>
This is due to the first loop, there are 4 items in the list and there is not any logic to stop the list from repeating on countries which have already been written.
Issue
<div ng-repeat="c in cities">
<div class="item item-divider" >
{{ c.Country }}
Create a filter to loop over unique Countries (this may involve creating a new list of unique countries).
Unique Filter
app.filter('unique', function() {
return function(collection, keyname) {
var output = [],
keys = [];
angular.forEach(collection, function(item) {
var key = item[keyname];
if(keys.indexOf(key) === -1) {
keys.push(key);
output.push(item);
}
});
return output;
};
});
<div ng-repeat="c in cities | unique: 'Country'"></div>
Often times I want to deal with both arrays and single objects in the same fashion. For example I have the property of an object that can either be an array or just a string (look at the scale property):
[
{
"name": "Experiment type14",
"id": "00000000014",
"scale": ["Whole Brain", "Cell"],
},
{
"name": "Experiment type15",
"id": "00000000015",
"scale": "Cell",
}
]
What I want is to show my scale like here:
<span ng-repeat="scale in experimentType.scale">
<!--some decoration here--> {{scale}}
</span>
Of course this won't work for a single string values. Is there any elegant way not to worry whether I'm dealing with a string or with an array?
You can create your custom filter, please see below
var app = angular.module("app", []);
function MainCtrl() {
this.message = "Welcome";
this.data = [{
"id": "00000000014",
"name": "Experiment type14",
"scale": ["Whole Brain", "Cell"],
},{
"id": "00000000015",
"name": "Experiment type15",
"scale": "Cell",
}];
}
function toArray() {
return function(input) {
console.log(input);
if (angular.isArray(input)) {
return input;
} else {
return [input];
}
};
}
angular.module("app").controller("MainCtrl", MainCtrl);
angular.module("app").filter('toArray', toArray);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="MainCtrl as vm">
<div class="container">
<h3>{{ vm.message }}</h3>
<div ng-repeat="item in vm.data">
<p>{{ item.name }}</p>
<ul>
<li ng-repeat="scale in item.scale | toArray">{{ scale }}</li>
</ul>
</div>
</div>
</div>
</body>
That's my best try. I just implement the following function in the scope:
$scope.toArrayIfNot = function (input) {
if (Object.prototype.toString.call(input) === '[object Array]') {
return input;
}
return [input];
};
I then use it like that:
<span ng-repeat="scale in toArrayIfNot(experimentType.scale)">
<!--some decoration here--> {{scale}}
</span>