AngularJS: ng-repeat an array with keys - angularjs

I have an array with unordered keys, and I want to display them. The problem is that angular repeats it for all the keys, even when they are not set.
this is the code:
<div ng-controller="MyCtrl">
Hello, {{a[10]}}!
<p ng-repeat="b in a">
1. {{b}}
</p>
</div>
<script>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.a = [];
$scope.a[10] = "aaa";
}
</script>
and this is the output:
Hello, aaa!
1.
1.
1.
1.
1.
1.
1.
1.
1.
1.
1. aaa
i want only the array keys that are set to output. no empty b's please...
here is a jsfiddle

In essence your problem is not AngularJS related but rather how JavaScript works.
If you have an empty array and you assign an element to position 10, then JavaScript will automatically resize the array to 11 elements (since an array index starts at zero) so the index is large enough to hold your element.
You could write extra code to filter the empty elements, but based on what you write, I think you would be better off with using an object to store your data.
I have created a plnkr for your convenience: http://plnkr.co/edit/apRLuJr4zqS2zbMz322Q?p=preview
// Array
$scope.sampleArray = [];
$scope.sampleArray[10] = 'test';
// Object
$scope.sampleObject = {};
$scope.sampleObject[10] = 'test';
As you can see the syntax is very similar, but the output is completely different.
By using an object, you will automatically eliminate the empty lines.
It will also keep your code simpler since you won't have to deal with the empty array elements.
Hope that helps!

There's plenty of ways to do a cleanup on your array inside the controller (e.g. using $watchcallback on a that would remove the empty elements from it whenever it changes).
Here's a solution that uses a simple custom filter, defined in a controller:
function MyCtrl($scope) {
$scope.namea = 'Superhero';
$scope.a = [];
$scope.a[10] = "aaa";
$scope.myFilter = function(item){
return item;
}
}
<p ng-repeat="b in a | filter:myFilter">
1. {{b}}
</p>
As stated in filter docs, the 'filter' filter can take a function:
function: A predicate function can be used to write arbitrary filters.
The function is called for each element of array. The final result is
an array of those elements that the predicate returned true for.

Related

What is right way to delete element from object Angular JS?

I have ng-repeat with function ng-click inside:
<div ng-repeat="item in data.education_list">
Delete
</div>
I pass object item from ng-repeat to function deleteEducation for deleting element from data.education_list.
So looks function:
$scope.deleteEducation = function (item){
$scope.data.education_list.splice($scope.data.education_list.indexOf(item), 1);
}
So this way works incorrect sometimes. When I have some element in ng-repeat and after delete item my template HTML is updated and removes row with another item, not that I deleted.
What is right way to delete?
data.education_list is array of objects if do {{data.education_list}}:
[{"name":"Test1","time":"01 Hun 2004 - 12 Sun 2006","Idusereducation":"86","usereducationIdToUser":"702","type":"1"}]
Problem two:
If I have object of objects instead array with key:
{"1" : {obj}, 2 : "obj"}
And if I try to delete element from object by key:
delete OBJ[1];
I get the same problem.
The easiest way is to use $index, this is a unique identifier that angular adds to track arrays.
<div ng-repeat="item in data.education_list">
Delete
</div>
If you are filtering a list
you will need to search for the index. Then do the splice. It is a little heavier, but required if you are filtering the list.
JS
this.removeItem = function(item) {
var index = $scope.data.education_list.indexOf(item);
if (index != -1) {
$scope.data.education_list.splice(index, 1);
}
};
HTML
ng-click="myctrl.removeItem(item)"
Working Example click to delete and .indexOf vs $index comparison
<div ng-repeat="item in data.education_list track by $index">
Delete
</div>
Then
$scope.deleteEducation = function (position){
$scope.data.education_list.splice(position, 1);
}
Came across the similar problem. In my case, I had to resolve as below just in case if it helps someone.
If you are dealing with objects, please note indexOf works for array not for an Object inside that array. You can do something like below to identify the index and handle this case;
$scope.removeReport = function(report) {
var index = $scope.contact.reports.map(function(r) { return r.id;}).indexOf(report.id);
if (index >= 0) {
$scope.contact.reports.splice(index, 1);
}
}
To remove a child item from a 2 dimensional array in an object, you need to define the parent item and then the child item to splice
e.g.
myObject[this.parentIndex].children.splice(this.childIndex,1);
myObject is an Object containing an array that includes children
parentIndex is the Index of the parent items
childIndex is the Index of the child items under the parent.
You can figure out your own way of looping through the parent and child arrays and deciding which child items to remove.

why array become blank after splitting into multiple small array?

hello I have one a array $scope.name .I am spliting the array into small arrays .But after spliting the array .it become blank why ?
actually I assigned the given array into temp variable and splite the temp variable .Again my $scope.name become blank why ?
here is my plunker
http://plnkr.co/edit/iUscrw0xclHSnsIWMMTM
console.log("before");
console.log($scope.name);
var test=$scope.name;
console.log("after");
console.log($scope.name);
console.log("test");
console.log(test);
var arrays = [], size = 3;
while (test.length > 0)
arrays.push(test.splice(0, size));
console.log(arrays);
console.log("name");
console.log($scope.name);
You are directly assigning object to other object, so that will cause change in any of the object will update other object value.
Use angular.copy instead of assigning object directly, that will create a new clone copy of that object will returned.
var test=angular.copy($scope.name);
Forked Plunkr

How to set class to first item in ng-repeat that has been sorted with orderBy?

I have a list of items, which comes in unsorted, I use orderBy to sort by name alphanumerically.
<li class="ticker-li"
ng-repeat="ticker in tickers | orderBy:'ticker'"
ng-class="{'selected':ticker.selected}">
<div class="ticker"
ng-click="unselectAll(); ticker.selected = !ticker.selected;
selectTicker(ticker);
revealTickerOptions()">
{{ticker.ticker}}
</div>
Now in my controller this is how I'm currently setting the first items selected class:
var vs = $scope;
vs.tickers = data;
vs.tickers[0].selected = true;
^ This worked perfectly until I needed to add the orderBy so that items appear by alpha order:
I found this answer here, however it locks the first item to always have the class.
Modifying my code a bit, I was able to have other buttons gain that class on click, however the $first item still stayed with the class.
ng-class="{'selected':$first || ticker.selected}"
In my controller this is my unselectAll function, which doesn't work with 'selected':$first:
vs.unselectAll = function() {
for (var i = 0; i < vs.tickers.length; i++) {
vs.tickers[i].selected = false;
}
};
How should the code either in the markup or controller need to be updated to fix this issue?
Give this a shot, I'm not sure how it reads the $index on the sort by, but get rid of the $first thing and put this init statement in there.
<li class="ticker-li"
ng-repeat="ticker in tickers | orderBy:'ticker'"
ng-init="$index ? ticker.selected = false : ticker.selected = true"
ng-class="{'selected':ticker.selected}" ng-click="unselectFirst($index)">
I think this is a grey area between a hack or not, you aren't technically aliasing a property in the ng-init, but i think it is a fine line. The other solution would be sort the array in your controller, there is an example in the docs that sort on alphabetic order, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

AngularJS custom filter function

Inside my controller, I would like to filter an array of objects. Each of these objects is a map which can contain strings as well as lists
I tried using $filter('filter')(array, function) format but I do not know how to access the individual elements of the array inside my function. Here is a snippet to show what I want.
$filter('filter')(array, function() {
return criteriaMatch(item, criteria);
});
And then in the criteriaMatch(), I will check if each of the individual property matches
var criteriaMatch = function(item, criteria) {
// go thro each individual property in the item and criteria
// and check if they are equal
}
I have to do all these in the controller and compile a list of lists and set them in the scope. So I do need to access the $filter('filter') this way only. All the examples I found in the net so far have static criteria searches inside the function, they don't pass an criteria object and test against each item in the array.
You can use it like this:
http://plnkr.co/edit/vtNjEgmpItqxX5fdwtPi?p=preview
Like you found, filter accepts predicate function which accepts item
by item from the array.
So, you just have to create an predicate function based on the given criteria.
In this example, criteriaMatch is a function which returns a predicate
function which matches the given criteria.
template:
<div ng-repeat="item in items | filter:criteriaMatch(criteria)">
{{ item }}
</div>
scope:
$scope.criteriaMatch = function( criteria ) {
return function( item ) {
return item.name === criteria.name;
};
};
Here's an example of how you'd use filter within your AngularJS JavaScript (rather than in an HTML element).
In this example, we have an array of Country records, each containing a name and a 3-character ISO code.
We want to write a function which will search through this list for a record which matches a specific 3-character code.
Here's how we'd do it without using filter:
$scope.FindCountryByCode = function (CountryCode) {
// Search through an array of Country records for one containing a particular 3-character country-code.
// Returns either a record, or NULL, if the country couldn't be found.
for (var i = 0; i < $scope.CountryList.length; i++) {
if ($scope.CountryList[i].IsoAlpha3 == CountryCode) {
return $scope.CountryList[i];
};
};
return null;
};
Yup, nothing wrong with that.
But here's how the same function would look, using filter:
$scope.FindCountryByCode = function (CountryCode) {
// Search through an array of Country records for one containing a particular 3-character country-code.
// Returns either a record, or NULL, if the country couldn't be found.
var matches = $scope.CountryList.filter(function (el) { return el.IsoAlpha3 == CountryCode; })
// If 'filter' didn't find any matching records, its result will be an array of 0 records.
if (matches.length == 0)
return null;
// Otherwise, it should've found just one matching record
return matches[0];
};
Much neater.
Remember that filter returns an array as a result (a list of matching records), so in this example, we'll either want to return 1 record, or NULL.
Hope this helps.
Additionally, if you want to use the filter in your controller the same way you do it here:
<div ng-repeat="item in items | filter:criteriaMatch(criteria)">
{{ item }}
</div>
You could do something like:
var filteredItems = $scope.$eval('items | filter:filter:criteriaMatch(criteria)');

AngularJS - ngRepeat and ngModel

I have spent the better part of a few hours writing and re-writing this, and am probably just going to write my own directive here if there isn't an answer.
I have a columnized display of inputs, 10 in each of the 6 columns. I am using 2 ngRepeat directives to display them. I am placing 6 fiddles below with my varied attempts at getting them to work right. The problem is, when I use an array of objects, all the data are updated simultaneously. View Fiddle #1 below to see the example.
Here is a quick snippet of the code, which you can also see on the fiddle page. If anyone has some pointers or a way to get #1, #2, or #6 to work, please let me know!
HTML:
<div ng-controller='EntryCtrl'>
<div class="span2" ng-repeat="a in [0,1,2,3,4,5]">
<div ng-repeat="i in entry.slice($index*10, ($index*10)+10)" ng-class="{'control-group': true, error: !entry[($parent.$index*10)+$index].correct}">{{($parent.$index*10)+$index}}<input type="text" class="span12" id="entry-{{($parent.$index*10)+$index}}" ng-model="entry[($parent.$index*10)+$index].input" /></div>
</div>
</div>
Javascript:
var myApp = angular.module('myApp', []);
myApp.controller('EntryCtrl', ['$scope', function($scope) {
$scope.entry=filledArray(60,{input:'',correct:true});
}]);
function filledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = val;
}
return rv;
}
Fiddle #1: Array of objects using ng-model pointing to entry using $index and $parent.$index: http://jsfiddle.net/DFEkG/ -- All models update simultaneously
Fiddle #2: Array of objects using ng-model pointing to i instead of $index: http://jsfiddle.net/DFEkG/1/ -- All models update simultaneously
Fiddle #3: Array using ng-model pointing to entry using $index and $parent.$index: http://jsfiddle.net/DFEkG/2/ -- Weird behavior
Fiddle #4 Array using ng-model pointed to i instead of $index: http://jsfiddle.net/DFEkG/3/ -- Broken
Fiddle #5 Array using only $index and $parent.$index and array only in ng-repeat directive: http://jsfiddle.net/DFEkG/4/ -- WORKS! But not as object
Fiddle #6 Same as technique as 5, but with an object: http://jsfiddle.net/DFEkG/5/ -- Same as fiddles 1 and 2
The problem is in your filledArray function. It assigns the same exact object to each array item. The same object {input: '', correct: true} is referenced in all 60 instances.
So, to fix it you can simply make a new copy of it on each iteration:
function filledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = angular.copy(val);
}
return rv;
}
Fiddle.

Resources