AngularJS - Switch between items in ng repeat with ng click - javascript

I have a small JSON Object, with country and city.
The ng-repeat is calling the country. So I want now that when I click on a country item, that this item shows the city and with an other click its switching back to country.
I have two function, where i can switch the item from country to city, but i cannot switch back to the originally (country) one. How can i do that ?
My HTML:
<body ng-controller="test">
<div ng-repeat="module in values" ng-click="select? setSelected() : setSelected2();select = !select">
{{module.country}}
</div>
and my .js file:
var app = angular.module('AngularApp', []);
app.controller('test', ['$scope', function ($scope) {
$scope.values = [
{
country: 'England',
city: 'London',
},
{
country: 'Spain',
city: 'Madrid',
}];
$scope.select = true;
$scope.setSelected = function() {
$scope.values[this.$index].country = $scope.values[this.$index].city;
}
$scope.setSelected2 = function() {
$scope.values[this.$index].country = $scope.values[this.$index].country;
}
return false;
}]);
Here is a plunker with my code:
https://plnkr.co/edit/MMqDXJFE9dUcWvuGKRDV?p=preview
Thanks for your help.

If it's only for display purposes, this can help you, without the need of any Angular functions to be placed in the .js file:
<div ng-repeat="module in values" ng-click="select = !select">
{{select ? module.city : module.country}}
</div>

You should create your own model to store displayed value:
$scope.values = [
{
country: 'England',
city: 'London',
showCountry: true
},
{
country: 'Spain',
city: 'Madrid',
showCountry: true
}];
$scope.setSelected = function() {
if ($scope.values[this.$index].showCountry)
{
$scope.values[this.$index].showCountry = false;
} else {
$scope.values[this.$index].showCountry = true;
}
}
<div ng-repeat="module in values" ng-click="setSelected()">
{{module.showCountry ? module.country : module.city}}
</div>
https://plnkr.co/edit/RsXRMKUETsw9aC0ozkr4?p=preview

Related

Pagination with filters using ng-repeat in angular

I am trying to do a pagination using filters.
There is a list with names and countries.
I am trying to filter them by country and also alphabetical range, and then generate the pagination by numbers. I am really stuck with it. any help will be really appreciate it
The alphabetical filter will retrieve the names that start with the the range of letters. For example if you select the first option [A - M] will return the person that their name start within that range of letters
Here is my code. The html is over there. Thanks
http://jsbin.com/cifowatuzu/edit?html,js,output
angular.module('app',['angular.filter'])
.controller('MainController', function($scope) {
$scope.selectedCountry = '';
$scope.currentPage = 1;
$scope.pageSize = 3;
$scope.pages = [];
//This should store {StartFrom and To from selected Range}
$scope.selectedRange = '';
$scope.AlphabethicalRange = [
{StartFrom: 'A', To: 'M'},
{StartFrom: 'N', To: 'Z'}
];
$scope.Countries = [
{ Name : 'USA'},
{ Name : 'Japan'},
{ Name : 'France'},
{ Name : 'Canada'},
{ Name : 'China'},
];
$scope.People = [
{ Id: 1, Name: 'Will', Country: 'USA'},
{ Id: 2, Name: 'Ed', Country: 'USA' },
{ Id: 3, Name: 'Peter', Country: 'China'},
{ Id: 4, Name: 'John', Country: 'Japan'},
{ Id: 5, Name: 'Alex', Country: 'France'},
{ Id: 6, Name: 'Jim', Country: 'France'},
{ Id: 7, Name: 'Austin', Country: 'Italy'},
{ Id: 8, Name: 'Men', Country: 'France'},
{ Id: 9, Name: 'Zike', Country: 'Canada'},
];
$scope.numberPages = Math.ceil($scope.People.length / $scope.pageSize);
$scope.init = function () {
for (i = 1; i < $scope.numberPages; i++) {
$scope.pages.push(i);
}
};
$scope.init();
});
I create a custom filter to filter the range that you want.
Here's a snippet working:
var app = angular.module('app', ['angular.filter']);
app.controller('mainCtrl', function ($scope) {
$scope.currentPage = 1;
$scope.pageSize = 3;
$scope.pages = [];
$scope.AlphabethicalRange = [
{
"StartFrom":"A",
"To":"M"
},
{
"StartFrom":"N",
"To":"Z"
}
];
$scope.Countries = [
{
"Name":"USA"
},
{
"Name":"Japan"
},
{
"Name":"France"
},
{
"Name":"Canada"
},
{
"Name":"China"
}
];
$scope.People = [
{
"Id":1,
"Name":"Will",
"Country":"USA"
},
{
"Id":2,
"Name":"Ed",
"Country":"USA"
},
{
"Id":3,
"Name":"Peter",
"Country":"China"
},
{
"Id":4,
"Name":"John",
"Country":"Japan"
},
{
"Id":5,
"Name":"Alex",
"Country":"France"
},
{
"Id":6,
"Name":"Jim",
"Country":"France"
},
{
"Id":7,
"Name":"Austin",
"Country":"Italy"
},
{
"Id":8,
"Name":"Men",
"Country":"France"
},
{
"Id":9,
"Name":"Zike",
"Country":"Canada"
}
];
$scope.numberPages = Math.ceil($scope.People.length / $scope.pageSize);
$scope.init = function() {
for (i = 1; i < $scope.numberPages; i++) {
$scope.pages.push(i);
}
};
$scope.init();
});
app.filter('rangeAlphaFilter', function() {
return function(items, search) {
if (!search || search == ' - ') {
return items;
}
return items.filter(function(element) {
return new RegExp('[' + search.replace(/ /g, '') + ']', 'i').test(element.Name[0]);
});
}
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.8/angular-filter.min.js"></script>
</head>
<body ng-controller="mainCtrl">
<div>
<span>Country Filter</span>
<select name="countriesSelect" ng-options="c as c.Name for c in Countries" ng-model="selectedCountry">
<option value="">-- Select a country --</option>
</select>
<br>
<span>Alphabetical Filter</span>
<select name="AlphabeticalSelect" ng-options="a as a.StartFrom +' - '+ a.To for a in AlphabethicalRange" ng-model="selectedRange">
<option value="">-- Select a range --</option>
</select>
<ul>
<li ng-repeat="person in People | filter: { Country: selectedCountry.Name } | rangeAlphaFilter: selectedRange.StartFrom +' - '+ selectedRange.To" ng-bind="person.Name"></li>
</ul>
<span>Pagination Numbers</span>
{{page}}
</div>
</body>
</html>
PS: To control the pagination, I extremely don't recommend you to do it manually, it gives a lot of work. I recommend you to see my answer in this another question, it's like a "mini" tutorial of how to use the angularUtils-pagination. Check it.
I hope it helps.

Angularjs Select Option set to first item did not work

I am working with angular and semantic ui. I am trying to make a selection of Y and N through a select option. Basically i just want the first item was selected when the page show up. Tried many ways but i couldn't make it works.
Please take a look at this plunker.
angular.module('myapp', [])
.controller('testctrl', function ($scope){
$scope.add = {};
$scope.Consigns = [{value: 'N',label: 'N'}, {value: 'Y',label: 'Y'}];
$scope.add.consign = $scope.Consigns[0].label;
})
.controller('testctrl1', function ($scope){
$scope.add = {};
$scope.Consigns1 = [{value: 'N',label: 'N'}, {value: 'Y',label: 'Y'}];
$scope.add.consign1 = $scope.Consigns1[0].label;
});
https://plnkr.co/edit/cHcLd14xKFxLMS4uy0BM?p=preview
Print the model value in default placeholder. Rather than sending the label value in $scope.add.consign you could send the whole object and print whats required.
Working plunker: https://plnkr.co/edit/XeuiS7p3K1OOx5nHL9c5?p=preview
javascript:
$scope.ListOrder =
[
{ Name: "price", Id: 1 },
{ Name: "exist", Id: 2 },
{ Name: "Sale", Id: 3 },
{ Name: "New", Id: 4 }
];
$scope.Order = { Name: "exist", Id: 1 };
HTML:
<select ng-model="Order" ng-options="obj.Name for obj in ListOrder"></select>
Remove
<script>
$('.ui.dropdown').dropdown();
</script>
and also change select tag to this
<select ng-model="add.consign" class="ui fluid dropdown" ng-options="x.label as x.label for x in Consigns">
<option value=""></option>
</select>

Making Angular Filter Dynamic in the controller

I've seen a couple other posts that I thought could help but haven't had any luck. I have a filter that is working fine when I statically specify what property I want to be filtering with. I am building an angular component where I don't know what data will actual be passed into the component. For example, here is some sample data:
this.customers = [
{ name: 'Jim', city: 'Minneapolis', state: 'MN', zip: 44332 },
{ name: 'Boe', city: 'Scottsdale', state: 'AZ', zip: 44332 },
{ name: 'Tom', city: 'S.F.', state: 'CA', zip: 11223 },
{ name: 'Joe', city: 'Dallas', state: 'TX', zip: 34543 },
{ name: 'Jon', city: 'L.A.', state: 'CA', zip: 56433 }
];
My static filters work great:
public filterTextChangeLocal($event: ng.IAngularEvent) {
if (this.itemDisplayProperty = "name") {
this.filteredItems = this.$filter("filter")(this.items, {name: this.ngModelValue});
} else if (this.itemDisplayProperty = "city") {
this.filteredItems = this.$filter("filter")(this.items, {city: this.ngModelValue});
} else if (this.itemDisplayProperty = "state") {
this.filteredItems = this.$filter("filter")(this.items, {state: this.ngModelValue});
} else if (this.itemDisplayProperty === "zip") {
this.filteredItems = this.$filter("filter")(this.items, {zip: this.ngModelValue});
}
}
The problem is the user of the component may pass in any type of data, with properties that may be completely different so I need something that can account for any property specified. I have an isolate scope property called "itemDisplayProperty" that allows the user to specify which property from their data they want to show in the dropdown, and that is the property that I need to be filtered. They could say, "item-display-property="address", the data in the address property would be displayed in the dropdown. I tried this but doesn't work as multiple "this" words aren't allowed:
this.filteredItems = this.$filter("filter")(this.items, {
this.itemDisplayProperty : this.ngModelValue
});
Here's my input and dropdown for reference:
<input type="text" class="form-control"
ng-change="ctrl.filterTextChangeLocal($event)"
ng-model="ctrl.ngModelValue"
ng-click="ctrl.openDropdown($event)" />
<ul class="dropdown-menu list-group" ng-if="!ctrl.ngDisabled">
<li class="list-group-item"
ng-repeat="row in ctrl.filteredItems"
ng-mousedown="ctrl.onSelectedLocal(row, $event)">
{{row[ctrl.itemDisplayProperty]}}
</li>
</ul>
In order to refactor the static filters inside filterTextChangeLocal all that you need to do is construct a $filter expression with a dynamic key.
This can be achieved by using the so called bracket notation:
var obj = {};
obj[myKey] = value;
The refactored filterTextChangeLocal would look something like this:
public filterTextChangeLocal($event: ng.IAngularEvent) {
var filterExpression = {};
filterExpression[this.itemDisplayProperty] = this.ngModelValue;
this.filteredItems = this.$filter("filter")(this.items, filterExpression);
}
I solved my own question this way, appears to be a new ES6 way to use a variable inside an object. The marked answer would have worked as well:
this.filteredItems = this.$filter("filter")(this.items, {[this.itemDisplayProperty] : this.ngModelValue});
OK, that's right, with ES6 you can use computed proporties.
With ES5 you could create a filter object like this (with var self=this; in your controller):
var filterObj = {};
filterObj[self.itemDisplayProperty] = self.ngModelValue;
self.filteredItems = $filter("filter")(self.items, filterObj);
Please have a look at the demo below or this jsfiddle.
Just a note to your variable names. Avoid ng prefix in your names (e.g. ngDisabled in your code). Because this variable has nothing to do with AngularJs and a different name will make your code more readable. Something like showDrop would be better.
angular.module('demoApp', [])
.controller('mainController', MainController)
.directive('dynFilter', DynFilterDirective);
function DynFilterDirective($filter) {
return {
restrict: 'E',
controllerAs: 'ctrl',
templateUrl: 'components/dynFilterTempl.html',
bindToController: {
items: '=',
itemDisplayProperty: '='
},
scope: {},
controller: function() {
var self = this;
self.filterTextChangeLocal = function($event) {
var filterObj = {};
filterObj[self.itemDisplayProperty] = self.ngModelValue;
console.log(filterObj);
self.filteredItems = $filter("filter")(self.items, filterObj);
};
}
}
}
function MainController($filter) {
var vm = this;
vm.items = [
{ name: 'Jim', city: 'Minneapolis', state: 'MN', zip: 44332 },
{ name: 'Boe', city: 'Scottsdale', state: 'AZ', zip: 44332 },
{ name: 'Tom', city: 'S.F.', state: 'CA', zip: 11223 },
{ name: 'Joe', city: 'Dallas', state: 'TX', zip: 34543 },
{ name: 'Jon', city: 'L.A.', state: 'CA', zip: 56433 },
];
vm.keys = Object.keys(vm.items[0]);
vm.curProp = vm.keys[0];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.4/angular.js"></script>
<div ng-app="demoApp" ng-controller="mainController as mainCtrl">
{{Object.keys(mainCtrl.items[0])}}
Choose your filter: <select ng-options="prop for prop in mainCtrl.keys" ng-model="mainCtrl.curProp"></select>
<dyn-filter item-display-property="mainCtrl.curProp" items="mainCtrl.items"></dyn-filter>
<script type="text/ng-template" id="components/dynFilterTempl.html">
<input type="text" class="form-control" ng-change="ctrl.filterTextChangeLocal($event)"
ng-model="ctrl.ngModelValue" ng-focus="ctrl.showDrop = true" ng-blur="ctrl.showDrop = false" />
<ul class="dropdown-menu list-group" ng-show="ctrl.showDrop">
<li class="list-group-item" ng-repeat="row in ctrl.filteredItems"
ng-mousedown="ctrl.onSelectedLocal(row, $event)">
{{row[ctrl.itemDisplayProperty]}}
</li>
</ul>
</script>
</div>

sort array in angularjs

Im working on sorting on an array using Angular JS using orderBy. But still its not getting sorted on a particular key.
Here is the code
var app = angular.module('sortModule', [])
app.controller('MainController', function($scope,$filter){
$scope.languages = [
{ name: 'English', image: '/images/english.png',key:2 },
{ name: 'Hindi', image: '/images/hindi.png',key:3 },
{ name: 'English', image: '/images/english.png',key:2},
{ name: 'Telugu', image: '/images/telugu.png',key:1 }];
var newLanguages = []
newLanguages = angular.copy($scope.languages);
function sortImages() {
$scope.languages = []
$scope.keys = []
for(language in newLanguages) {
$scope.keys.push(newLanguages[language])
}
$filter('orderBy')($scope.keys, 'key')
console.log(JSON.stringify($scope.keys))
}
sortImages();
});
Fiddle
Im planning to see sorting based on "key". telugu should come first, english next and hindi last.
you need to have:
$scope.keys = $filter('orderBy')($scope.keys, 'key', false)
the order by filter returns a new array, it does not make changes to the passed array.
updated fiddle:
http://jsfiddle.net/kjuemhua/17/
Remove the OrderBy from the html markup to display unordered list:
<div ng-app="sortModule" class="nav">
<div ng-controller="MainController">
<button ng-click="sort()">Sort
</button>
<div></div>
<div >
<ul>
<li ng-repeat="lang in languages">
<span>{{lang.name}}</span>
</li>
</ul>
</div>
</div>
</div>
Now using the button sort sort the list
var app = angular.module('sortModule', [])
app.controller('MainController', function($scope,$filter){
$scope.languages = [
{ name: 'English', image: '/images/english.png',key:2 },
{ name: 'Hindi', image: '/images/hindi.png',key:3 },
{ name: 'English', image: '/images/english.png',key:2},
{ name: 'Telugu', image: '/images/telugu.png',key:1 }];
var newLanguages = []
newLanguages = angular.copy($scope.languages);
$scope.sort = function(){
$scope.languages = []
$scope.keys = []
for(language in newLanguages) {
$scope.keys.push(newLanguages[language])
}
$scope.keys = $filter('orderBy')($scope.keys, 'key', false);
$scope.languages = $scope.keys;
console.log(JSON.stringify($scope.keys))
}
});

Dependent Select Angular JS

I have hierarchical data set. There is one fixed root unit.
What I want to do is to make this tree browsable with dependent selects.
I have created a simple plunkr example with a fixed dataset.
http://plnkr.co/edit/Bz5A1cbDLmcjoHbs5PID?p=preview
The data format in the example mimics the format I would get from a server request in "real" life.
This working fine in this simple first step. What is missing is, that when a user changes a selection somewhere in the middle, the select boxes and the ng-model binding below the new selection need to be destroyed.
So when I select Europe->France->Quimper and change "Europe" to "Asia" - then there should be "Asia" as the first select box and a second one the Asia countries.
Is there an "Angular" way to deal to deal with this? Any other hint is appreciated also ;)
<!DOCTYPE html>
<html ng-app="app">
<head>
<link data-require="bootstrap#3.3.5" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<script src="https://code.angularjs.org/1.3.17/angular.js" data-semver="1.3.17" data-require="angular.js#1.3.17"></script>
</head>
<body>
<div ng-controller="Ctrl">
<select ng-repeat="select in selects track by $index" ng-model="$parent.boxes[$index]">
<option ng-repeat="child in select.children" ng-click="expandSelects(child)">{{child.name}}</option>
</select>
<ul>
<li ng-repeat="item in boxes">{{ item }}</li>
</ul>
</div>
<script>
var app = angular.module('app', []);
app.controller('Ctrl', ['$scope', function($scope) {
var data = {
'europe': {
name: 'europe',
children: [{
name: 'france',
parent: 'europe'
}, {
name: 'italy',
parent: 'europe'
}],
},
'asia': {
name: 'asia',
children: [{
name: 'japan',
parent: 'asia'
}, {
name: 'china',
parent: 'asia'
}],
},
'france': {
name: 'france',
children: [{
name: 'paris',
parent: 'france'
}, {
name: 'quimper',
parent: 'france'
}]
}
};
var root = {
name: 'world',
children: [{
name: 'europe',
parent: 'world'
}, {
name: 'asia',
parent: 'world'
}, ]
};
$scope.selects = [root];
$scope.expandSelects = function(item) {
var select = data[item.name];
if (select) {
$scope.selects.push(select);
}
}
$scope.$watch('boxes', function(item, old) {
}, true);
}]);
</script>
</body>
</html>
This is a classic example of cascading dropdowns, with the added challenge of an unknown number of levels in the cascade. I combined the data set into one object for simplicity, added labels for the dropdowns, and simplified the select element.
This solution allows for any number of levels, so if you needed data below the city level, you could add it without changing any code, as illustrated by the "Street" example I added to Paris.
select {
display: block;
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<link data-require="bootstrap#3.3.5" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<script src="https://code.angularjs.org/1.3.17/angular.js" data-semver="1.3.17" data-require="angular.js#1.3.17"></script>
</head>
<body>
<div ng-controller="Ctrl">
<div ng-repeat="select in selects track by $index" ng-if="select.children">
<label>{{ select.optionType }}</label>
<select ng-model="selects[$index + 1]" ng-options="child.name for child in select.children" ng-change="clearChildren($index)"></select>
<hr />
</div>
</div>
<script>
var app = angular.module('app', []);
app.controller('Ctrl', ['$scope', function($scope) {
var data = {
optionType: 'Continent',
name: 'World',
children: [
{
optionType: 'Country',
name: 'Europe',
children: [
{
optionType: 'City',
name: 'France',
children: [
{
optionType: 'Street',
name: 'Paris',
children: [
{
name: 'First'
},
{
name: 'Second'
}
]
},
{
name: 'Quimper'
}
]
},
{
name: 'Italy'
}
]
},
{
optionType: 'Country',
name: 'Asia',
children: [
{
name: 'Japan'
},
{
name: 'China'
}
]
}
]
};
$scope.selects = [data]
$scope.clearChildren = function (index) {
$scope.selects.length = index + 2;
};
}]);
</script>
</body>
</html>
To go to the children in your hierachy is not as hard as it may seem. If you set up your select with angular and let it do most of the selection for you (for example using ng-options instead of ng-repeating the tag itself), and tell it what options there are, then the list of children you are trying to render just becomes a standard ng-repeat of the children that were picked from the select above.
I modified your plunker to show you how you could accomplish that a slightly different way.
http://plnkr.co/edit/zByFaVKWqAqlR9ulxEBt?p=preview
Main points I changed were
$scope.expandSelects = function() {
var select = data[$scope.selected.name];
if (select) {
console.log('changed');
console.log(select);
$scope.chosen = select;
}
}
Here i just grab the chosen item which the will use. Then the ends up looking like.
<ul>
<li ng-repeat="item in chosen.children">{{ item.name }}</li>
</ul>
The only other set up that was really needed was setting up the with ng-options and giving it a model to bind to.
<select ng-options="child.name for child in selects.children"
ng-model="selected" ng-change="expandSelects()">
</select>
Use can use a filter on the second select to filter de options based on the previous selection.
For example, you can have a first selection to choose the continent:
<select ng-options="c for c in continents" ng-model="selectedContinent" ></select>
and a second selection for the coutries:
<select ng-options="c.name for c in countries | filter : {parent:selectedContinent}" ng-model="selectedCountry" ></select>
Made a fiddle with a simplified data structured just to show how the filter works: http://jsfiddle.net/marcosspn/oarL4n78/

Categories