I'm using UI-Select 0.8.4 and have a large data set. Then I'm using UI-Select to display property values in a dropdown beside the data set. I'm using that for filters. So when choosing from the dropdown, will filter the results.
Every time when I hover over some item in the dropdown, it always fires the ng-repeat filter.
This is lagging my application because I'm working with a large set in the ng-repeat.
Why is this?
GIF:
http://i.imgur.com/cStlXzy.gif
Plunker (open console and see for yourself):
http://plnkr.co/edit/OxiutZ8t4IX1bOxiOTgo?p=preview
HTML:
<h3>Age list</h3>
<p>Selected: {{age.selected}}</p>
<ui-select ng-model="age.selected" ng-disabled="disabled" style="width: 300px;">
<ui-select-match placeholder="Select a person">{{$select.selected}}</ui-select-match>
<ui-select-choices repeat="age in ageArray | filter: $select.search">
<div ng-bind="age | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
JavaScript:
$scope.theFilter = function(item) {
console.log(item);
return item;
};
$scope.ageArray = [];
$scope.$watch('people', function(item) {
for(var i = 0; i < item.length; i++) {
$scope.ageArray.push(item[i].age);
}
});
$scope.people = [
{ name: 'Adam', email: 'adam#email.com', age: 10 },
{ name: 'Amalie', email: 'amalie#email.com', age: 12 },
{ name: 'Wladimir', email: 'wladimir#email.com', age: 30 },
{ name: 'Samantha', email: 'samantha#email.com', age: 31 },
{ name: 'Estefanía', email: 'estefanía#email.com', age: 16 },
{ name: 'Natasha', email: 'natasha#email.com', age: 54 },
{ name: 'Nicole', email: 'nicole#email.com', age: 43 },
{ name: 'Adrian', email: 'adrian#email.com', age: 21 }
];
Edit: I even tried to filter the property values out of the "data set array" and using that in the dropdown, but it doesn't work.
Edit 2: If you think that the watch was triggering this, I removed the watch and this is still a problem: http://plnkr.co/edit/oD3Tt3vfjtOjADMnemW1?p=preview
Edit 3: Still haven't found a solution for this so I'm stuck with chosen. I created an issue but haven't gotten any response. Please upvote the issue if you want this fixed.
The issue is that the filter is executing on every $digest (every ng-mouseenter, ng-click, etc). For a huge data set, this can obviously kill performance. (See this article http://www.bennadel.com/blog/2489-how-often-do-filters-execute-in-angularjs.htm)
Instead, try a $watch on the age.selected value, then applying a filter only when that value actually changes.
http://plnkr.co/edit/TIeKPAyrAQsGHwakqwEp?p=preview
HTML
<!-- filtered list "ageMatches" -->
<ul ng-show="age.selected">
<li ng-repeat="person in ageMatches">{{person.name}} - {{person.age}}</li>
</ul>
<!-- default list of all "people" -->
<ul ng-hide="age.selected">
<li ng-repeat="person in people">{{person.name}} - {{person.age}}</li>
</ul>
JS
// add age to scope
$scope.age = {};
// add age match placeholder
$scope.ageMatches = [];
// watch age.selected for changes, apply filter
$scope.$watch('age.selected', function(newVal, oldVal){
if(newVal){
$scope.ageMatches = $filter('filter')($scope.people, {age: newVal});
}
});
Related
How do you clear an array with selected values so that values can return to the select?
I have a people array. The people array values are available in select. When I choose names, they are transferred to the multipleDemo array. And you can not reselect them from select because they disappear and are moved to the multipleDemo array. With the Delete button I have to delete all elements from the multipleDemo array (except the first element) into the people array. So that you can again choose a name from the select. Error in function $clearTag.
Expecting behavior:
Example:
Select: Wladimir
Appear tag Wladimir
Select Wladimir (You can't choose Wladimir because he is already chosen)
Click Delete. Cut elements(tags) with multipleDemo array and put them in array people
You can again select Wladimir
Here is my code: http://plnkr.co/edit/TPZjXkkSRrIc5ApzP07F?p=preview
index.html
<!DOCTYPE html>
<html lang="en" ng-app="demo">
<head>
<meta charset="utf-8">
<title>AngularJS ui-select</title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular-sanitize.js"></script>
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.css">
<!-- ui-select files -->
<script src="select.js"></script>
<link rel="stylesheet" href="select.css">
<script src="demo.js"></script>
<!-- Select2 theme -->
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.css">
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/selectize.js/0.8.5/css/selectize.default.css">
<style>
body {
padding: 15px;
}
.select2 > .select2-choice.ui-select-match {
/* Because of the inclusion of Bootstrap */
height: 29px;
}
.selectize-control > .selectize-dropdown {
top: 36px;
}
</style>
</head>
<body ng-controller="DemoCtrl">
<h3>Array of strings</h3>
<button ng-click='clearTag()'>Delete</button>
<ui-select tagging tagging-label="new tag" multiple ng-model="multipleDemo"
on-select="OnClickSelect($item)" on-remove="OnRemoveSelect($item)"
theme="select2" ng-disabled="disabled" style="width: 300px;">
<ui-select-match placeholder="Select name...">{{$item.name}}</ui-select-match>
<ui-select-choices repeat="item in people | filter:$select.search">
{{item.name}}
</ui-select-choices>
</ui-select>
<p>Selected: {{multipleDemo}}</p>
<hr>
</body>
</html>
demo.js
app.controller('DemoCtrl', function($scope, $http, $timeout) {
$scope.multipleDemo =[];
$scope.people = [
{ name: 'Adam', email: 'adam#email.com', age: 12, country: 'United States' },
{ name: 'Amalie', email: 'amalie#email.com', age: 12, country: 'Argentina' },
{ name: 'Estefanía', email: 'estefania#email.com', age: 21, country: 'Argentina' },
{ name: 'Adrian', email: 'adrian#email.com', age: 21, country: 'Ecuador' },
{ name: 'Wladimir', email: 'wladimir#email.com', age: 30, country: 'Ecuador' },
{ name: 'Samantha', email: 'samantha#email.com', age: 30, country: 'United States' },
{ name: 'Nicole', email: 'nicole#email.com', age: 43, country: 'Colombia' },
{ name: 'Natasha', email: 'natasha#email.com', age: 54, country: 'Ecuador' },
{ name: 'Michael', email: 'michael#email.com', age: 15, country: 'Colombia' },
{ name: 'Nicolás', email: 'nicolas#email.com', age: 43, country: 'Colombia' }
];
$scope.OnClickSelect=function(item)
{
$scope.multipleDemo.push(item.name);
}
$scope.OnRemoveSelect = function(item) {
var index = $scope.people.indexOf(item.name);
$scope.people.splice(index, 1);
}
$scope.clearTag = function() {
for(var i =0; i < $scope.multipleDemo.length; i++) {
$scope.multipleDemo.splice($scope.multipleDemo[i], 1000);
$scope.people.push($scope.multipleDemo[i]);
}
}
Angular-UI-Select Common Issues
ng-model not working with a simple variable on $scope
You cannot write:
WRONG
<ui-select ng-model="multipleDemo"> <!-- Wrong -->
[...]
</ui-select>
You need to write:
<ui-select ng-model="vm.multipleDemo"> <!-- Correct -->
[...]
</ui-select>
For more information, see
AngularUI-Select FAQ - Common Issues
Update
vm.multipleDemo doesn't work; I try $parent.multipleDemo - it works. I don't understand $parent. Why it works?
For vm.multipleDemo to work, the controller must initialize the vm object:
app.controller('DemoCtrl', function($scope, $http, $timeout) {
$scope.vm = { multipleDemo: [] };
New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view, ng-include and ng-if all create new child scopes, so the [data hiding] problem often shows up when these directives are involved. (See this example for a quick illustration of the problem.)
This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models – watch 3 minutes worth. Misko demonstrates the primitive binding issue with ng-switch.
— What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
Avoid using $parent to fix a data hiding problem. It is a brittle solution as there can be more than one level of scope heirarchy between the controller and the ui-select directive. I consider the use of $parent to be a code smell, a symptom of a deeper problem.
Update #2
When I can use $ctrl in view and this in controller?
If the controller is instantiated with "controller as" syntax:
<body ng-controller="DemoCtrl as $ctrl">
<ui-select ng-model="$ctrl.multipleDemo">
<!-- -->
</ui-select>
Then there is no need to use $scope:
app.controller('DemoCtrl', function($http) {
this.multipleDemo = [];
And it avoids the data hiding problem.
For more information, see
'this' vs $scope in AngularJS controllers
How can I filter an ng-repeat to show all items where a certain columnfield is an empty string? When I try this it always seem to give the full list. I only want to see the person with id 1.
Fiddlejs example
Controller:
var people = [{
name: '',
age: 32,
id: 1
}, {
name: 'Jonny',
age: 34,
id: 2
}, {
name: 'Blake',
age: 28,
id: 3
}, {
name: 'David',
age: 35,
id: 4
}];
$scope.filteredPeople = $filter('filter')(people, {
name: ''
});
$scope.people = people.slice(0);
View:
<li ng-repeat="p in filteredPeople">
<h4>{{p.name}} ({{p.age}}) id: {{p.id}}</h4>
</li>
You can use Angular's 'filter' in 'ng-repeat':
// In template:
<li ng-repeat="p in filteredPeople | filter : filterPeople">
<h4>{{p.name}} ({{p.age}}) id: {{p.id}}</h4>
</li>
// In controller:
$scope.filterPeople = function(item) {
return !item.name;
};
To list users that have name:
<li ng-repeat="p in filteredPeople" ng-if="p.name !== ''">
<h4>{{p.name}} ({{p.age}}) id: {{p.id}}</h4>
</li>
To list only users that do not have name:
<li ng-repeat="p in filteredPeople" ng-if="p.name === ''">
<h4>{{p.name}} ({{p.age}}) id: {{p.id}}</h4>
</li>
Just pass third parameter to filter saying true which will perform strict check
$scope.filteredPeople = $filter('filter')(people, {
name: ''
}, true);
Forked JSFiddle
I have two select dropdowns, the first being the parent of the second. I am able to successfully bind the parent select back to a 'selected item'. But I am unable to bind the child select using ng-model back to the selected parent. I couldn't quite get my example working as I am new to angular but hopefully you get the picture.
$scope.model = {};
$scope.model = {
categories: [{
id: 1,
name: 'Ford',
subCategory: ['focus', 'ranger', 'F150'],
filterValue: ''
}, {
id: 2,
name: 'Honda',
subCategory: ['accord', 'civic', 'pilot'],
filterValue: ''
}],
selectedCategory: {}
}
$scope.categoryChange = function() {
console.dir($scope.selectedCategory);
}
$scope.subCategoryChange = function() {
console.dir($scope.selectedCategory.subCategory);
}
var init = function() {
$scope.model.selectedCategory = $scope.model.categories[0];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<select ng-model="model.selectedCategory" ng-options="category as category.name for category in model.categories" data-ng-change="categoryChange()"></select>
<select ng-model="model.selectedCategory.filterValue" data-ng-options="subCategory for subCategory in model.selectedCategory.subCategory" data-ng-change="subCategoryChange()"></select>
For me it's working!
Maybe you forgot to put ng-app & ng-controller
See This --> Sample
I'm new to angular. If this is a duplicate, please post a link.
Ok, so I in javascript I have a list of items, lets say this:
[{
name: 'Bob',
age: 24,
},
{
name: 'Smith',
age: 56,
},
{
name: 'Lisa',
age: 12,
}]
All the name properties are printed out in a list at the left of the page, like this:
<li data-ng-repeat="person in persons">{{tournament.name}}</li>
All this works, but here is the thing.
When I click a person in the list, I want to display more detailed information to the right of the list about that person.
If I click on Bob in the list, it should display both name and age to the right of the list.
I can't figure this out in angular. Can anyone explain how I update a part of the page with that information?
You can do that with a simple click on your li like that :
<ul data-ng-repeat="person in persons">
<li ng-click="detail($index)">{{person.name}}</li>
</ul>
The $index is the index of the ng-repeat really useful to mange with arrays !
You add a div where you want to see the person details :
<div>
{{personDetail.name}} {{personDetail.age}}
</div>
In your controller implement the detail function like that :
var app = angular.module('MyApp', []);
app.controller('MyCtrl', function($scope){
$scope.persons = [{
name: 'Bob',
age: 24,
},
{
name: 'Smith',
age: 56,
},
{
name: 'Lisa',
age: 12,
}];
$scope.detail = function(index){
$scope.personDetail = $scope.persons[index];
};
});
And voila !
working plnkr here : http://plnkr.co/edit/Wg4UD6?p=preview
<!-- left -->
<li data-ng-repeat="person in persons" ng-click="obj.selected=$index">
{{person.name}}
</li>
<!-- right -->
<div>
{{persons[obj.selected]["name"]}}
{{persons[obj.selected]["age"]}}
</div>
Controller:
$scope.obj = {
selected:-1
};
HTML
<li data-ng-repeat="person in persons" ng-click="clicked(person)">
controller
$scope.selectedNode = "";
...
$scope.clicked = function(info) {
$scope.selectedNode = info;
};
now create right side:
<div>
<pre>{{selectedNode | json}}</pre>
</div>
http://jsfiddle.net/WcJbu/
When I select a person, I want the favoriteThing selector to display their current selection.
<div ng-controller='MyController'>
<select ng-model='data.selectedPerson' ng-options='person.name for person in data.people'></select>
<span ...> likes </span>
<select ... ng-model='data.favoriteThing' ng-options='thing.name for thing in data.things'></select>
</div>
$scope.data.people = [{
name: 'Tom',
id: 1,
favorite_thing_id: 1
}, {
name: 'Jill',
id: 2,
favorite_thing_id: 3
}];
$scope.data.things = [{
name: 'Snails',
id: 1
}, {
name: 'Puppies',
id: 2
}, {
name: 'Flowers',
id: 3
}];
Do I need to set up a service and add watches, or is there a [good] way to use the favorite_thing_id directly in the select?
Change the second select to this:
<select ng-show='data.selectedPerson' ng-model='data.selectedPerson.favorite_thing_id'
ng-options='thing.id as thing.name for thing in data.things'></select>
Adding the thing.id as to the ng-options will allow you to select the data.things entries based on their id's instead of their references. Changing the ng-model to data.selectedPerson.favorite_thing_id will make angular automatically change to the correct option based on selectedPerson.favorite_thing_id.
jsfiddle: http://jsfiddle.net/bmleite/4Qf63/
http://jsfiddle.net/4Qf63/2/ does what I want - but it's pretty unsatisfying.
$scope.$watch(function() {
return $scope.data.selectedPerson;
}, function(newValue) {
if (newValue) {
$scope.data.thing = $filter('filter')($scope.data.things, {id: newValue.favorite_thing_id})[0];
}
})
I'd like to see all of that be possible from within the select statement.
Maybe I'll try to write a directive.
association = {key: matchValue}
So that I can do
<select ... ng-model='data.thing' ng-options='t.name for t in data.things' association='{id: "data.selectedPerson.favorite_thing_id"}'></select>