In Angular, is there a way to modify the filter such that it only returns exact matches?
Example:
var words = [
{ title: "ball" },
{ title: "wall" },
{ title: "all" },
{ title: "alloy" }
];
var wordsFiltered = filter('filter')
(
words,
{
'title': 'all'
}
);
The above will match 'ball', 'wall', 'all' and 'alloy'. But I would like it to only match 'all'. Any way to change it?
UPDATE
Starting from AngularJS v.1.1.3 the exact filtering is provided natively:
Find words that exactly match title:
<input ng-model="match.title" />
<br>
and exactly match type:
<input ng-model="match.type" />
<hr>
<table>
<tr ng-repeat="word in words | filter:match:true">
<td>{{word.title}}</td>
</tr>
</table>
Plunker
Your question implies that you would want to match against multiple object properties so here's a filter that does that:
app.controller('AppController',
[
'$scope',
function($scope) {
$scope.match = {};
$scope.words = [
{ title: "ball", type: 'object' },
{ title: "wall", type: 'object' },
{ title: "all", type: 'word' },
{ title: "alloy", type: 'material' }
];
}
]
);
app.filter('exact', function(){
return function(items, match){
var matching = [], matches, falsely = true;
// Return the items unchanged if all filtering attributes are falsy
angular.forEach(match, function(value, key){
falsely = falsely && !value;
});
if(falsely){
return items;
}
angular.forEach(items, function(item){ // e.g. { title: "ball" }
matches = true;
angular.forEach(match, function(value, key){ // e.g. 'all', 'title'
if(!!value){ // do not compare if value is empty
matches = matches && (item[key] === value);
}
});
if(matches){
matching.push(item);
}
});
return matching;
}
});
<body ng-controller="AppController">
Find words that exactly match title:
<input ng-model="match.title" />
<br>
and exactly match type:
<input ng-model="match.type" />
<hr>
<table>
<tr ng-repeat="word in words | exact:match">
<td>{{word.title}}</td>
</tr>
</table>
</body>
PLUNKER
Try this :
var words = [
{ title: "ball" },
{ title: "wall" },
{ title: "all" },
{ title: "alloy" }
];
var wordsFiltered = filter('filter')
(
words,
{
'title': 'all'
},
true
);
You can use Regex to achieve a simple implementation:
<input ng-model="query" />
<tr ng-repeat="word in words | filter: myFilter">
In the controller:
$scope.myFilter = function (word) {
if ($scope.query === '') return true;
var reg = RegExp("^" + $scope.query + "$");
return reg.test(word.title);
};
I would create a new filter. Is this what you want?
HTML
<div ng-controller="MyCtrl">
{{words | exactMatch:'all'}} !
</div>
JavaScript
var myApp = angular.module('myApp',[]);
myApp.filter('exactMatch', function() {
return function(words, pattern) {
var result = [];
words.forEach(function (word) {
if (word.title === pattern) {
result.push(word);
}
});
return result;
}
});
function MyCtrl($scope) {
$scope.words = [
{title: "ball", other: 1},
{title: "wall", other: 2},
{title: "all", other: 3},
{title: "alloy", other: 4},
{title: "all", other: 5},
];
}
JsFiddle: jsfiddle
More information about custom filters: filters, creating custom filters and using filters
If you want use filter in Javascript instead of html you should look here: jsfiddle
I created my own filter.
<select
ng-model="selection.neType"
ng-options="option.NE_TYPE_ID as option.NAME for option in networkElementTypes">
<option value="">Todos</option>
</select>
<select
ng-model="selection.neTypeVendor"
ng-options="option.VENDOR_TYPE_ID as option.NAME for option in networkElementTypeVendors | exactMatch: {FK_NE_TYPE_ID: selection.neType}">
<option value="">All</option>
</select>
app.filter('exactMatch', function() {
return function(elements, pattern) {
var result = [];
var fieldSearch = Object.keys(pattern)[0];
elements.forEach(function (element) {
if (element[fieldSearch] == pattern[fieldSearch]) {
result.push(element);
}
});
return result;
}
});
angular-filter is a useful library of filters.
One of their filters is the where filter that does an exact match.
Related
There is a nested array which consist of bundles and those bundles in turn have items. So, I want to skip identical item(s) of the next bundle based on previous bundle items while iterating. Below is data model and code snippets:
vm.data
[
{
id: '01',
name: 'Dummy1',
items: [{
id: 'itemOne',
name: 'ItemOne',
desc: 'ItemOne description'
}]
},
{
id: '02',
name: 'Dummy2',
items: [{
id: 'itemOne',
name: 'ItemOne',
desc: 'ItemOne description'
},
{
id: 'otherItem',
name: 'OtherItem',
desc: 'OtherItem description'
}]
},
...
]
Html:
<div ng-repeat="bundle in vm.data track by $index">
...
<ul>
<li ng-repeat="item in bundle.items" ng-if="vm.check(item, $parent.$index)">
<span ng-bind="item.name"></span>
...
</li>
</ul>
</div>
vm.check:
vm.check = function(item, bundleIdx) {
if (bundleIdx > 0) {
return _.some(vm.data[bundleIdx-1].items, function(obj) {
return obj.id !== item.id;
});
} else {
// first bundle, so show all items
return true;
}
};
Demo is here.
It works partially, i.e. second bundle correctly matches conditions, but third bundle not. So, what I'm missing? Any help would be appreciated!
I would keep the complex logic out of your template. Instead you should transform vm.data before you attempt to consume it.
var items = {};
vm.bundles = [];
vm.data.forEach(function(data) {
var bundle = {
id: data.id,
name: data.name,
items: []
};
data.items.forEach(function(item) {
if (!items[item.id]) {
bundle.items.push(item);
}
items[item.id] = true;
});
vm.bundles.push(bundle);
});
Then your template can simply consume the transformed data.
<div ng-repeat="bundle in vm.bundles track by $index">
...
<ul>
<li ng-repeat="item in bundle.items">
<span>{{item.name}}</span>
...
</li>
</ul>
</div>
I am new to angularjs and still learning things. I have this code below. What I am aiming here is to load the data into the dropdown from the database. For example the data from the database is New Construction I want to load all data from LoadDropdown function and the New Construction is selected as default.
HTML
<select name="ScopeofWork" class="form-control" ng-model="drpScopeWork" ng-options="SoW as SoW.label for SoW in ScopeOfWorkList">
<option value="" selected hidden></option>
</select>
JS
function LoadDropdown() {
$scope.ScopeOfWorkList = [{ value: 'New Construction', label: 'New Construction' },
{ value: 'Erection', label: 'Erection' },
{ value: 'Addition', label: 'Addition' },
{ value: 'Renovation', label: 'Renovation' },
{ value: 'Repair', label: 'Repair' }];
}
fnLoadDropdown();
function fnLoadDropdown() {
var url = '/AccessoryGroundPreparation/LoadScopeofWork';
$http({
method: "post",
url: url,
//data: { "ScopeOfWork": ScopeOfWork, "projectID": projectID }
}).then(function (res) {
var data = res.data;
if (data.data == null)
LoadDropdown();
else {
// $scope.drpScopeWork = $scope.ScopeOfWorkList[0];
$scope.drpScopeWork = data.data;
}
});
}
Use ng-init to assign a value to your model drpScopeWork form 0 index of your array like ng-init="drpScopeWork = ScopeOfWorkList[0].value"
Here is working code snippet:
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.ScopeOfWorkList = [{
value: 'New Construction',
label: 'New Construction'
},
{
value: 'Erection',
label: 'Erection'
},
{
value: 'Addition',
label: 'Addition'
},
{
value: 'Renovation',
label: 'Renovation'
},
{
value: 'Repair',
label: 'Repair'
}
];
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div>
<select name="ScopeofWork" class="form-control" ng-model="drpScopeWork" ng-options="SoW.value as SoW.label for SoW in ScopeOfWorkList" ng-init="drpScopeWork = ScopeOfWorkList[0].value">
</select>
</div>
</div>
I am having trouble displaying the multiple values form text box i.e an entered values is matching with an values of array we need to display the matching value,for me displaying only one value. please help me to find out the solution.
in this example if type "ball" it's displaying if we type "ball all" it has to show both "ball" "all" but for it's not displaying bcoz here we are not returning an array if i assign to array it's throwing an errors.can any one help me where i did mistake.
var app = angular.module('angularPracticeApp');
app.controller('AppController', function ($scope) {
$scope.match = {};
$scope.words = [
{ title: "ball", type: 'object' },
{ title: "wall", type: 'object' },
{ title: "all", type: 'word' },
{ title: "alloy", type: 'material' }
];
});
app.filter('exact', function(){
return function(items, match){
var matching = [], matches,resultArray = [];
angular.forEach(items, function(item){ // e.g. { title: "ball" }
matches = true;
angular.forEach(match, function(value, key){ // e.g. 'all', 'title'
var stringArray = value.split(/(\s+)/);
console.log(stringArray);
for(var i=0;i<stringArray.length;i++){
if(!!value){ // do not compare if value is empty
matches = matches && (item[key] === stringArray[i]);
/* if(item[key] === stringArray[i]){
console.log(stringArray[i]);
resultArray.push(stringArray[i]);
}*/
}
}
});
if(matches){
console.log(resultArray);
matching.push(item);
}
});
return matching;
}
});
<!doctype html>
<html ng-app="plunker">
<head>
<script src="http://code.jquery.com/jquery-2.0.0.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/js/bootstrap.min.js"></script>
<script src="script.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css" rel="stylesheet">
<link rel="stylesheet" href="main.css" />
</head>
<body ng-controller="AppController">
<div>
Find words that exactly match title:
<input ng-model="match.title" />
<br>
<table>
<tr ng-repeat="word in words | exact:match">
<td>{{word.title}}</td>
</tr>
</table>
</div>
</body>
</html>
Sorry could not debug your filter, so I made my own, please use it as reference.
JSFiddle Demo
JS:
var app = angular.module('myApp', []);
app.controller('MyController', function MyController($scope) {
$scope.match = {};
$scope.words = [
{ title: "ball", type: 'object' },
{ title: "wall", type: 'object' },
{ title: "all", type: 'word' },
{ title: "alloy", type: 'material' }
];
});
app.filter('exact', function($filter){
return function(items, match){
console.log(match);
if(Object.keys(match).length !== 0){
match = match.title.split(" ");
}else{
console.log("itesm", items);
return items;
}
if(match){
return $filter("filter")(items, function(listItem){
return match.indexOf(listItem.title) != -1;
});
}
};
});
Ok I think I understand what you want and I think there might be better approach. Here's solution I prepared for you, it's much cleaner:
http://jsfiddle.net/U3pVM/33950/
HTML:
<div ng-app>
<div ng-controller="SomeCtrl">
<div>
Find words that exactly match title:
<input ng-model="match.title" />
<br>
<table>
<tr ng-repeat="word in wordsCopy">
<td>{{word.title}}</td>
</tr>
</table>
</div>
</div>
</div>
and JS:
function SomeCtrl($scope) {
$scope.words = [{
title: "ball",
type: 'object'
}, {
title: "wall",
type: 'object'
}, {
title: "all",
type: 'word'
}, {
title: "alloy",
type: 'material'
}];
$scope.wordsCopy = $scope.words;
$scope.$watch('match.title', (newVal, oldVal) => {
if (newVal) {
$scope.wordsCopy = $scope.words.filter((val, index) => {
return newVal.split(' ').indexOf(val.title) !== -1;
});
} else {
$scope.wordsCopy = $scope.words;
}
});
};
In my example (https://jsfiddle.net/vv18yjzo/7/) the autocomplete suggestions should be shown, as long as the md-chips input is focused. This works well when I enter a few items.
However, when all available items are added and then removed, the autocomplete suggestions are empty / hidden. I have to enter a new search text to see the autocomplete suggestions again.
HTML:
<md-input-container class="md-block">
<label>Categories</label>
<md-chips ng-model="vm.selectedCategories"
md-autocomplete-snap
md-require-match="true"
md-on-add="vm.onCategoryAdded($chip, $index)"
md-on-remove="vm.onCategoryRemoved($chip, $index)">
<md-autocomplete
md-selected-item="vm.autocomplete.selectedItem"
md-search-text="vm.autocomplete.searchText"
md-selected-item-change="vm.onSelectedItemChanged()"
md-search-text-change="vm.onSearchTextChanged()"
md-items="item in vm.searchCategories()"
md-item-text="item.name"
md-min-length="0"
md-select-on-match="true"
md-match-case-insensitive="true"
md-no-cache="true"
placeholder="Choose categories">
<span md-highlight-text="vm.autocomplete.searchText"
md-highlight-flags="i">{{item.name}}</span>
</md-autocomplete>
<md-chip-template>
<span>
<strong>{{$chip.name}}</strong>
</span>
</md-chip-template>
</md-chips>
</md-input-container>
JS:
(function () {
'use strict';
angular
.module('app', [
'ngMaterial',
'ngMessages'
])
.factory('CategoriesService', CategoriesService)
.controller('CategoriesCtrl', CategoriesCtrl);
function CategoriesService($q, $timeout) {
var that = {
find: find
},
categories = [
{ name: 'a' },
{ name: 'ab' },
{ name: 'abc' },
{ name: 'abcd' },
{ name: 'abcde' },
{ name: 'abcdef' }
];
function find(search, selectedCategories) {
var deferred = $q.defer(),
result = categories.filter(function(category) {
return (-1 === selectedCategories.indexOf(category) && -1 !== category.name.indexOf(search));
});
$timeout(function() {
deferred.resolve(result);
}, 100);
return deferred.promise;
}
return that;
}
function CategoriesCtrl($scope, CategoriesService) {
var vm = this;
vm.selectedCategories = [];
vm.autocomplete = {
selectedItem: null,
searchText: null
};
/* ... */
vm.searchCategories = function() {
return CategoriesService.find(vm.autocomplete.searchText.toLowerCase(), vm.selectedCategories);
};
}
})();
Example: https://jsfiddle.net/vv18yjzo/7/
There seems to be a weird bug with the autocomplete searchtext, if you remove an item it just unsets the searchtext variable, which then breaks the autocomplete events.
Check this working fiddle https://jsfiddle.net/mgsLkf89/3/
vm.onCategoryRemoved = function($chip, $index) {
if (!vm.autocomplete.searchText)
vm.autocomplete.searchText=null;
};
I am trying to write a custom filter. It's purpose is to do one of three things, based on a drop down menu. It needs to either show only hidden items, only notHidden items, or all items.
Here is my drop down menu.
<select class="span1" ng-model="itemfilter.hidden'">
<option value="">All</option>
<option value="hidden">Hidden</option>
<option value="shown">Shown</option>
</select>
Here is the HTML for the ng-repeat and filter
<div ng-repeat="item in items | hiddenFilter:itemfilter.hidden:'hidden"> </div>
Here is the Custom Filter
app.filter('hiddenFilter', function($_ ) {
return function( items, show_or_hide, attribute ) {
var shownItems = [];
$_.each(items, function(item) {
if(show_or_hide === 'shown') {
if (item[attribute] === true)
shownItems.push(item);
} else{
if (item[attribute] !== true)
shownItems.push(item);
}
});
return shownItems;
};
});
I am having trouble figuring out how to make the drop down menu change what this filter displays, any help would be greatly appreciated.
Edits --
Once I passed in the attribute correctly I started getting different results. The all and hidden options now show only nonHidden items, and the shown options shows only hidden items. Still not sure where I am going wrong.
Edit 2 --
Tried to make a jsfiddle for it here http://jsfiddle.net/mindstormy/RVB8A/1/
Fixed the code for you. Working Demo
<div ng-app="app" ng-controller="exampleCtrl">
<select class="span1" ng-model="itemfilter.hidden">
<option value="all">All</option>
<option value="hidden">Hidden</option>
<option value="shown">Shown</option>
</select>
<div ng-repeat="item in items| hiddenFilter:itemfilter.hidden:'hidden'">{{item.name}}, {{item.hidden}}</div>
</div>
var app = angular.module('app', []);
app.controller('exampleCtrl', function ($scope) {
$scope.items = [{
name: 'Foobar',
hidden: true
}, {
name: 'Foobar',
hidden: false
}, {
name: 'Barfoo',
hidden: true
}, {
name: 'Barfoo',
hidden: false
}, {
name: 'FB',
hidden: false
}];
$scope.itemfilter = {};
$scope.itemfilter.hidden = "all";
});
app.filter('hiddenFilter', function () {
return function (items, show_or_hide, attribute) {
var shownItems = [];
if (show_or_hide === 'all') return items;
angular.forEach(items, function (item) {
if (show_or_hide === 'shown') {
if (item[attribute] === true) shownItems.push(item);
} else {
if (item[attribute] !== true) shownItems.push(item);
}
});
return shownItems;
};
});