I am trying to write a small filter to hide from my repeater elements.
Let's say that my scope is:
$scope.test = [
{id: 1,name: "Test number 1"},
{id: 2,name: "Test number 2"},
{id: 3,name: "Test number 3"},
{id: 4,name: "Test number 4"},
{id: 5,name: "Test number 5"},
{id: 6,name: "Test number 6"},
{id: 7,name: "Test number 7"},
{id: 8,name: "Test number 8"},
{id: 9,name: "Test number 9"},
{id: 10,name: "Test number 10"}
]
and in my repeater I am doing something like this:
<div ng-repeat="t in test| hide:[1,6]"></div>
I started to write my filter but I got stuck. This is what I have so far:
filter('hideIDs', function() {
newArray= [];
function(zone, IDS) {
var containsObject, newArray;
containsObject = function(obj, list) {
var i;
i = void 0;
i = 0;
while (i < list.length) {
if (list[i] === obj) {
return true;
}
i++;
}
return false;
};
angular.forEach(IDS, function(hide) {
return angular.forEach(test, function(t) {
if (t.id === hide) {
return
} else {
if (containsObject(t, newArray)) {
return
} else {
newArray.push(t);
}
}
});
});
return newArray;
};
});
What I am trying to do with the filter is:
check if t.id should be hidden, and if yes, return without pushing it to newArray
The problem I've got is:
id to hide is 1 on the first loop, and then 6 gets pushed
on the second loop hovewer, the id to hide is 6 and then 1 gets pushed
And I end up having them both anyway.
Suggestions?
What would you achieve this in an efficient angular way?
thanks a lot
How about this?
https://plnkr.co/edit/hPiOnq7jp4kIP6h8Ox1d?p=preview
<div ng-init="data = [{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}]">
<div ng-repeat="test in data | hideByIds:[2, 4]">
{{ test.id }}
</div>
</div>
You should reuse what you can, in this case the filter filter:
var app = angular.module('app', []);
app.filter( 'hideByIds', function( $filter ) {
return function( input, ids ) {
return $filter('filter')( input, function( it ) {
return ids.indexOf( it.id ) == -1;
} );
}
} );
An alternative is to specify the predicate function on the scope, and simply pass it to the filter in the template.
Related
I have an array of json objects like this:
$scope.data = [{ name: "something something", date: 982528 },
{ x: 1, y: { sub: 2}, prop1: "some string" },
{ a: "b", c: "d", e: "some string" }];
and I'm trying to filter it with:
var filteredData = $filter('filter')($scope.data, "some string");
this way in all the properties of the objectes in the array, angular compares with the search string,
in this example it will return the las two objects,
now, what i need is to pass an array of properties like:
var exclude = ['prop1'];
so the filter will omit to compare those properties in each object,
is there an angular filter with this option?
Unfortunately, you should create custom excludeFilter:
angular.module('app', []).controller('ctrl', function($scope){
$scope.data = [
{ name: "something something", date: 982528 },
{ x: 1, y: { sub: 2}, prop1: "some string", prop2: "some string" },
{ a: "b", c: "d", e: "some string" }
];
$scope.search = 'some';
$scope.exclude = ['prop1', 'prop2'];
}).filter('excludeFilter', function(){
return function(data, search, exclude){
if(!search)
return data;
return data.filter(function(x){
for(var prop in x)
if(exclude.indexOf(prop) == -1){
var value = x[prop];
if(value.indexOf && value.indexOf(search) != -1)
return true;
if(!value.indexOf && value == search)
return true;
}
return false;
});
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js">
</script>
<div ng-app='app' ng-controller='ctrl'>
search: <input type='text' ng-model='search'/>
<br>
exclude: <input type='text' ng-model='exclude' ng-list/>
<ul>
<li ng-repeat='x in data | excludeFilter : search : exclude'>{{x | json}}</li>
</ul>
</div>
I have an array (1) with some id's and another array (2) with creatures, they have id (like in first array) and names. And I want to create new array (it will be looks like (1) id array, but only with id that in (2)). So I think that I need use filter.
(1)
$scope.main = ['dog', 'cat', 'bird', 'bug', 'human'];
(2)
$scope.creatures = [
{
id: 'cat',
name : 'fluffy'
},
{
id: 'cat',
name : 'mr.Kitty'
},
{
id: 'human',
name: 'Rachel'
},
{
id: 'cat',
name : 'Lucky'
},
{
id: 'cat',
name: 'Tom'
}
];
filter:
$scope.results = $scope.main.filter(function(item) {
angular.forEach($scope.creatures, function(creature) {
return item === creature.id;
});
});
I expect that it will be
$scope.results === ['cat', 'human'];
But I have
$scope.results // [0] empty array
Where I'm wrong? Plnkr example
It is not working because you are returning in the first iteration itself inside forEach loop. You can get it working as shown below :
Updated Plunker
$scope.results = [];
$scope.main.filter(function(item) {
angular.forEach($scope.creatures, function(creature) {
if(item === creature.id){
if( $scope.results.indexOf(item) === -1){
$scope.results.push(item);
}
}
});
});
Instead of looping again inside the filter, we can get the ids out of creatures array first and then filter them in main array like below :
$scope.results = [];
$scope.ids = $scope.creatures.map(function (creature){
return creature.id;
});
$scope.ids.map(function (id){
if($scope.main.indexOf(id) !== -1){
if( $scope.results.indexOf(id) === -1){
$scope.results.push(id);
}
}
});
console.log($scope.results);
Made few changes in your plunker, it is working now. Can you check with this code.
var app = angular.module('App', []);
app.controller('Ctrl', function($scope, $timeout){
$scope.main = ['dog', 'cat', 'bird', 'bug', 'human'];
$scope.creatures = [
{
id: 'cat',
name : 'fluffy'
},
{
id: 'cat',
name : 'mr.Kitty'
},
{
id: 'human',
name: 'Rachel'
},
{
id: 'cat',
name : 'Lucky'
},
{
id: 'cat',
name: 'Tom'
}
];
var array = [];
$scope.call = function() {
angular.forEach($scope.main,function(item){
angular.forEach($scope.creatures, function(creature) {
if(item == creature.id){
// console.log('item',item,' creatureId',creature.id);
if(array.indexOf(item) < 0 ){
array.push(item);
}
console.log(array);
}
});
$scope.results = array;
});
};
$scope.call();
console.log('result ',$scope.results);
});
It's a small memory game: the player has to remember a list of words and then he is presented with another list which contains few words from the original list where he has to decide if any of these words were present in the original list. For that he can click on 'Yes' or 'No' radio button.
<div align="center" ng-show="showWords" style="margin-bottom:1cm;">
<div ng-repeat="word in words">
<h4>{{word}}</h4>
</div>
</div>
<div align="center" ng-show="showTest" style="margin-bottom:1cm;">
<div ng-repeat="word in wordsPresent | limitTo:limitTo">
<h4>{{word}}</h4>
<label>
<input type="radio" value="yes">
Yes
</label><br/>
<label>
<input type="radio" ng-value="no">
No
</label><br/>
</div>
</div>
...
<div align="center" ng-if="showOk">
<button class="button button-dark" ng-click="pushToArray()">OK</button>
</div>
In my controller I have:
$scope.words=['Okra', 'Musa', 'Sky', 'India', 'Rose', 'Titanic', 'Onion', 'Germany', 'Beer', 'Run', 'Garden', 'Mountain']
$scope.wordsPresent=['King', 'Garden', 'America', 'Sky', 'Potato', 'Germany', 'Rose', 'Whisky', 'Mumbai', 'Walk']
$scope.playerChoices=[]
The first array is the original array against which I have to check, second array is what I am presenting to the user right now, and third array might be the array where I push his choices.
It looks like following:
How can I implement this?
Update
Please see this updated plunker demo.
To populate the data dynamically onto the DOM, you can use ng-repeat. Please see the code on plunker.
<span ng-repeat="item in someArray">{{item}}</span>
To interpolate the looped item, use {{ }}.
Important : Just please avoid duplicate data on $scope.wordsPresent array coz it will produce error on ng-repeat.
Instead of pushing the "choices" at the end of the quiz/survey(?) why not push it every time the radio changes it's value? Push to array if yes is selected and remove from array if no is selected.
In that case you can use ng-change event to trigger the pushing and removing of data in the array, on the controller. You should take advantage of the 2 way data binding angularjs is offering.
You can then cross check your arrays.
Please see this plunker demo I created and I hope it will help you.
If you can make do with a library, I would suggest lodash.
_.intersection([2, 1], [2, 3]);
// ➜ [2]
Or in the context of your problem:
_.intersection($scope.words, [$scope.wordsPresent]);
// ➜ ['Sky', 'Germany', 'Rose', 'Garden']
https://lodash.com/docs#intersection
It seems to me if you want to check for correct answers, you need to do more than just compare arrays. Suppose they select "No" when something is actually in the list. First, I would put a key field on the items in my array so you are not so limited. Here is a working Plunker
Your data should probably look something like this:
$scope.words = [{
id: 11,
name: 'Okra'
}, {
id: 12,
name: 'Musa'
}, {
id: 4,
name: 'Sky'
}, {
id: 13,
name: 'India'
}, {
id: 14,
name: 'Rose'
}, {
id: 15,
name: 'Titanic'
}, {
id: 16,
name: 'Onion'
}, {
id: 6,
name: 'Germany'
}, {
id: 17,
name: 'Beer'
}, {
id: 18,
name: 'Run'
}, {
id: 2,
name: 'Garden'
}, {
id: 19,
name: 'Mountain'
}]
$scope.wordsPresent = [{
id: 1,
name:'King'
},
{
id: 2,
name: 'Garden'
}, {
id: 3,
name: 'America'
}, {
id: 4,
name: 'Sky'
}, {
id: 5,
name: 'Potato'
}, {
id: 6,
name: 'Germany'
}, {
id: 7,
name: 'Rose'
}, {
id: 8,
name: 'Whisky'
}, {
id: 9,
name: 'Mumbai'
}, {
id: 10,
name: 'Walk'
}]
Then I would add an answer field (you could include it when you set up your data or you can include i after like this:
setAnswers();
function setAnswers() {
angular.forEach($scope.wordsPresent, function (value, key) {
$scope.wordsPresent[key].answer = 'no';
});
angular.forEach($scope.words, function (value, key) {
$scope.words[key].answer = 'yes';
})
}
Your HTML could look like this:
<div ng-app="myModule" ng-controller="HomeCtrl">
<div align="center" style="margin-bottom:1cm;">
<div ng-repeat="word in wordsPresent | limitTo:limitTo">
<h4>{{word.name}}</h4>
<input type="radio" ng-model="word.answer" value="yes">
Yes
<br />
<input type="radio" ng-model="word.answer" value="no">
No
<br />
</div>
</div>
<div ng-show="showAnswers">
<span>Correct Answers: {{correct}}</span> <span>Incorrect Answers: {{wrong}}</span>
You selected:
<div ng-repeat="a in playerChoices">
<span>{{a.name}}</span> <span>{{a.answer}}</span>
</div>
</div>
<div align="center">
<button class="button button-dark" ng-click="pushToArray()">OK</button>
</div>
</div>
And in your controller:
$scope.pushToArray = function () {
$scope.correct = 0;
for (var i = 0; i < $scope.wordsPresent.length; i++) {
$scope.playerChoices.push($scope.wordsPresent[i]);
}
$scope.showAnswers = true;
$scope.correct = correctAnswers($scope.playerChoices, $scope.words);
}
function correctAnswers(checkerArray, targetArray) {
correct = 0;
wrong = 0;
for (var i = 0; i < checkerArray.length; i++) {
if (checkerArray[i].answer == 'yes') {
if (containsObject(checkerArray[i], targetArray) == true) {
correct = correct + 1;
}
}
else if (checkerArray[i].answer = 'no') {
if (containsObject(checkerArray[i], targetArray) == false) {
correct = correct + 1;
}
}
}
return correct;
}
function containsObject(obj, list) {
var i;
for (i = 0; i < list.length; i++) {
if (list[i].id === obj.id) {
return true;
}
}
return false;
}
Happy coding!
I need filter for ng-repeat, that explode elements in "general" array, if element exist in "suggest" array (by id field).
$scope.general= [{id: 21323, name: 'alex'}, {id: 8787, name: 'maria'}, {id: 8787, name: 'artem'}];
$scope.suggest = [{id: 21323, name: 'alex'}, {id: 8787, name: 'maria'}];
<div ng-repeat="elem in general">{{elem.name}}</div>
You should create your own custom filter and you'll probably want to use Array.prototype.filter.
You said you wanted to exclude by the property id. The following filler optionally allows specifying a property. If the property is not specified, then the objects are excluded by strict equality (the same method used by the ===, or triple-equals, operator) of the objects.
angular.module('myFilters', [])
.filter('exclude', function() {
return function(input, exclude, prop) {
if (!angular.isArray(input))
return input;
if (!angular.isArray(exclude))
exclude = [];
if (prop) {
exclude = exclude.map(function byProp(item) {
return item[prop];
});
}
return input.filter(function byExclude(item) {
return exclude.indexOf(prop ? item[prop] : item) === -1;
});
};
});
To use this filter in your html:
<div ng-repeat="elem in general | exclude:suggest:'id'">{{elem.name}}</div>
Here is an example jsfiddle:
https://jsfiddle.net/6ov1sjfb/
Note that in your question artem's id matches maria's thus both artem and maria were filtered. I changed artem's id in the plunker to be unique to show that the filter works.
Try this :
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.general = [{
id: 21323,
name: 'alex'
}, {
id: 8787,
name: 'maria'
}, {
id: 8787,
name: 'artem'
}];
$scope.suggest = [{
id: 21323,
name: 'alex'
}, {
id: 8787,
name: 'maria'
}];
$scope.filteredArray = function () {
return $scope.general.filter(function (letter) {
for (i = 0; i < $scope.suggest.length; i++) {
return $scope.suggest[i].id !== letter.id
}
});
};
}
and
<div ng-repeat="elem in filteredArray(letters)">{{elem.name}}</div>
check out the fiddle : http://jsfiddle.net/o2er6msv/
Note: please chk ur id, they are duplicated
I have a code as follow:
function getdata($scope){
$scope.todos = [
{name:"john",data:"1 2 3"},
{name:"Marry",data:"6 7 8"},
{name:"Edward",data:"2 4 5"}
];
var seri=new Array();
for(var item in $scope.todos)
{
seri.push(
{
name: item.name,
data: [1, 0, 4]
});
}
console.log(seri);
}
Now when I check the console for the name it returns undefined. what is the problem?
Try
function getdata($scope) {
$scope.todos = [
{ name: "john", data: "1 2 3" },
{ name: "Marry", data: "6 7 8" },
{ name: "Edward", data: "2 4 5" }
];
var seri = new Array();
angular.forEach($scope.todos, function (item, key) {
seri.push({
name: item.name,
data: [1, 0, 4]
});
});
console.log(seri);
//you can get data from this by using return
}
When you loop through an array in javascript the item is an integer with the position of the value in the array. You'll need to use this position to get the true item, like this:
for (var i in $scope.todos) {
var item = $scope.todos[i];
...
}
If the function is inside a controller and being called as such you should declare it as
$scope.getData = function() {
}
And then it should work fine. If the function is NOT to be part of the scope and you are passing the scope in then try receiving the parameter as "scope" and not "$scope" which has a special meaning in Angular.
this is not how you iterate array in javascript (see materik answer).
you can alternatively use angular.forEach to iterate the todos array like this:
angular.forEach($scope.todos, function(item)
{
seri.push(
{
name: item.name,
data: [1, 0, 4]
});
});