AngularJs search filter ignore punctuation - javascript

I have a table with some data. It has name, code, phone, etc.. And this table can have dynamic fields, based on the client option.
I could found a code to make this search ignore the punction, but it has some limitations. On the .filter i need to specify which field I'm going to search, so, because my table is dynamic i don't know what field is being displayed.
This is the link i got the answer from.
And this is the code I'm using:
app.js
.filter('filterMaster', function() {
return function(items, searchTerm) {
if (!searchTerm || '' === searchTerm) {
return items;
}
searchTerm = searchTerm.replace(/[^\w\s]|_/g, "").toLowerCase();
return items.filter(function(element, index, array) {
var title = element.cod_order.replace(/[^\w\s]|_/g, "").toLowerCase();
return title.indexOf(searchTerm) > -1;
});
}
})
I also tried using this code, which i got from this answer:
app.js
$scope.ignoreAccents = function(item) {
if (!$scope.searchField)
return true;
var text = removeAccents(item.cod_order.toLowerCase())
var search = removeAccents($scope.searchField.toLowerCase());
return text.indexOf(search) > -1;
};
function removeAccents(value) {
return value
.replace(/á/g, 'a')
.replace(/é/g, 'e')
.replace(/í/g, 'i')
.replace(/ó/g, 'o')
.replace(/[^\w\s]|_/g, "")
.replace(/ú/g, 'u');
};
But again, if i don't specify the cod_order, or other field I want to search, it doesn't work. And if i set the field i want to search, than i can't search in anyother field on the table.
The main problem with this, is because i can't search any other field if it's not being set inside this filter.
Is there a way to optimize it so it work doesn't matter what field the table has? And without the need to specify the field name?

Try something like this
.filter('filterMaster', function () {
return function (items, searchTerm) {
if (searchTerm === '') return items;
var filtered = [];
var str = searchTerm.replace(/\./g, '');
for (var i = 0; i < items.length; i++) {
var itemTmp = items[i];
var found = false;
$.each(itemTmp, function (i, n) {
if(i =='$$hashKey' || found )
return;
var replaced = n.toString().replace(/\./g, '');
if (replaced.indexOf(str) >= 0)
{
filtered.push(itemTmp);
found = true;
}
});
}
return filtered;
};
});
here working code.

Related

Table filter getting reset vue js

I have filters for each column in my table and the filter works fine when i type in for one column and when i filter another column in parallel, the results shows only for the second filter (means the first filter i typed is getting igonored). Always the latest filter works.
//computed
filteredList: function () {
var vm = this.vm;
var columnFilters = this.columnFilters;
var list = [...this.vm.entries];
var search, value;
var filteredIndexes = Object.keys(this.columnFilters);
if (filteredIndexes.length > 0) {
filteredIndexes.forEach(function (index) {
if (columnFilters[index] != '') {
list = vm.entries.filter(function (row) {
search = columnFilters[index].toLowerCase();
value = row.rowItems[index].itemDisplay.toLowerCase();
return value.includes(search);
});
}
});
}
return list;
},
Where i'm going wrong?

How to ng-repeat only after applying the filter?

First of all, let me mention that I am new to AngularJS, and programming aswell.
My situation is as follows:
I am dealing with over 50k entries that I pull from a SQL database.
I have to show those entries on a web platform after a search/filter is applied on those entries.
So I did some research on this and came to the conclussion that the way to go is:
SQL>PHP>JSON>ANGULARJS.
I got all this down , the thing is that ng-repeat sends everything to the browser and what I want is to filter the results and THEN print them with ng-repeat.
I have tried to implement a filter of sorts but I can't seem to solve it.
Code looks like this:
js:
var app = angular.module('myApp' ,[]);
app.controller('MainCtrl', function($scope, $http)
{
$http.get("http:source.php")
.then(function(response)
{
$scope.materiale = response.data.records;
});
});
app.filter('filtruCautare', function () {
return function (items, filtru) {
var filtered = [];
var filtruMatch = new RegExp(filtru, 'i');
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (filtruMatch.test(item.MATKL) || filtruMatch.test(item.MAKTX)) {
filtered.push(item);
} else if (filtruMatch.test(item.MATNR)) {
filtered.push(item);
}
}
return filtered;
};
});
html:
<input ng-model='filtru' type="text" placeholder="...">
<tbody ng-repeat="material in materiale | filtruCautare:filtru">
I've put this together from several blogs, Stackoverflow posts/questions and other several sources...
I think that I need to somehow filter the 50k entries, store them inside a scope and use the ng-repeat to print the results from there, it's just that I'm having trouble creating a scope that pulls info from the filter.
Any help is appreciated!
Simply change your filter in order to return an empty array if the search filter is not defined (or is an empty string).
In this way you will start to see results only when you start filtering:
app.filter('filtruCautare', function () {
return function (items, filtru) {
if (!filtru) {
return [];
}
var filtered = [];
var filtruMatch = new RegExp(filtru, 'i');
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (filtruMatch.test(item.MATKL) || filtruMatch.test(item.MAKTX)) {
filtered.push(item);
} else if (filtruMatch.test(item.MATNR)) {
filtered.push(item);
}
}
return filtered;
};
});

Angular Implementing multi search filter

I am using the angular advanced search box and I want to implement a custom filter in my angular page but I am having trouble figuring out how to handle a string of requirements in a query. Let's say I have several objects that follow the following format:
{
"displayName":"John",
"gender":"male",
"type":"customer",
"items": 3,
}
I would want to be able to search in plain english `Anyone who's name is John and is of type Customer". Here is my angular search code so far:
app.filter('infoFilter', function() {
return function(data, query) {
var output = [];
var index;
//loop over the original array
angular.forEach(data, function(row, index) {
angular.forEach(query, function(input, value) {
if(input) {
if(angular.isNumber(row[value]) && row[value] == input) {
output.push(row);
} else if(!angular.isNumber(row[value]) && row[value].toLowerCase().indexOf(input.toLowerCase()) > -1) {
output.push(row);
}
}
});
});
if(query) {
return data;
} else {
return output;
}
}
});
The query comes in as an object that looks like this:
{
"displayName":"John"
}
This works perfectly fine for 1 search parameter. So if I searched for John my table would update to show all entries with the name of john. However, this wouldn't really work for multi search parameters. So if the query looked like this:
{
"displayName":"John",
"gender":"Female"
}
I need to apply all the parameters at once before i do output.push(row). How exactly would I go about doing this?
If I understand you correctly you want to filter the rows where all query parameters apply (AND). I modified your code slightly to achieve this behavior.
app.filter('infoFilter', function() {
return function(data, query) {
var output = [];
var index;
//loop over the original array
angular.forEach(data, function(row, index) {
var pushRow = true;
angular.forEach(query, function(input, value) {
if(input) {
if(angular.isNumber(row[value]) && row[value] == input) {
return;
} else if(!angular.isNumber(row[value]) && row[value].toLowerCase().indexOf(input.toLowerCase()) > -1) {
return;
}
}
pushRow = false;
});
if (pushRow) {
output.push(row);
}
});
// This bit also seems to be the wrong way around in your code.
if(!query) {
return data;
} else {
return output;
}
}
});
Edit:
Here is also an optimized version of the same filter using javascripts built in array functions.
app.filter('infoFilter', function() {
return function(data, query) {
if(!query || !data) {
return data;
}
return data.filter(function(row) {
return Object.keys(query).every(function(key) {
var rowValue = row[key];
var queryValue = query[key];
return (angular.isNumber(rowValue) && rowValue == input) ||
(angular.isString(rowValue) && rowValue.toLowerCase().indexOf(queryValue.toLowerCase()) > -1);
});
});
};
});

Protractor test hangs when clicking on element

I have been trying to write a protractor test that selects an item from a custom dropdown menu. The only problem is that when it tries to click an element other than the last one in the list it hangs and timesout. When I remove the click() method invocation it seems to work fine. Since all these calls are done asynchronously I also don't see a way of stopping the loop when it finds the element. My code looks like this:
var it = null;
for(var i = 1; i <= totalNumberOfAccounts; i++) {
var listItemLocator = '//div[#id="payment-accounts"]/div/ul/li[' + i + ']/label/div/div[2]/div[2]/span[2]';
var item = browser.driver.findElement(protractor.By.xpath(listItemLocator));
item.getText().then(function(value) {
if(value === accountNumber) {
it = item;
}
console.log(value);
})
.then(function clickOption() {
console.log('Clicking...');
if (it) {
console.log('Clicking desired item');
it.click();
console.log('Clicked..');
}
})
}
I also tried this approach:
this.selectRichSelectOption = function (selector, item) {
var selectList = browser.driver.findElement(selector);
selectList.click();
var desiredOption = '';
var i = 1;
selectList.findElements(protractor.By.tagName('li'))
.then(function findMatchingOption(options) {
console.log(options);
options.some(function (option) {
console.log('Option:');
console.log(option);
var listItemLocator = '//div[#id="payment-accounts"]/div/ul/li[' + i + ']/label/div/div[2]/div[2]/span[2]';
console.log(listItemLocator);
var element = option.findElement(protractor.By.xpath('//label/div/div[2]/div[2]/span[2]'));
console.log('Element:');
console.log(element);
i++;
element.getText().then(function (value) {
console.log('Value: ' + value);
console.log('Item:');
console.log(item);
if (item === value) {
console.log('Found option..');
desiredOption = option;
return true;
}
return false;
});
});
})
.then(function clickOption() {
console.log('Click option');
console.log(desiredOption);
if (desiredOption) {
console.log('About to click..');
desiredOption.click();
}
});
};
The result of this one is even more strange. Now all of a sudden the getText() method invocation returns an empty String. But when I try to retrieve the e.g. the class attribute I get the correct value back. Where did the Text value go?
Can somebody please help me out?
This seems to be an issue with page load. After you select, the page does not load completely.
Try using a browser.sleep(timeInMs);
try using node 8+'s async functions such as await. I went through this headache and it was solved by awaiting for certain things to appear or have certain attributes.
await browser.wait(EC.presenceOf(element(by.xpath('path leading to element based off attribute'))))
Good luck

if url exists in array do something

I have a simple form which users can enter a "tweet". I ahve some javascript behind the scenes to control what happens when a url is entered.
If a url is entered such as test.com then a new input field will appear.
If a url that is stored in an array is entered, it will and the new input field along with a select option.
here is my javascript:
var test = ["test1.com", "test2.com", "test3.com"];
$('#tweet_text_ga').hide();
$('#custom_alias').hide();
$('#tweet_campaign').hide();
$('#tweet_text').keydown(function () {
var val = this.value;
if (/\.[a-zA-Z]{2,3}/ig.test(val)) {
$('#custom_alias').show();
} else {
$('#custom_alias').hide();
}
if ($.inArray(val, test) !== -1) {
$('#tweet_campaign').show();
} else {
$('#tweet_campaign').hide();
}
});
It works fine if just a url is entered. But as soon as you add more text, it disregards if the url is in the array, and removes the select option. I'm not quite sure on how to explain this any better, so i have setup a fiddle to show what i mean.
I hope someone understands and can point me in the right direction
Fiddle
That's because you are checking if a whole input is in the array: if ($.inArray(val, test) !== -1). You need to retrieve URL from the input using a regex and check that.
Write a regex that retrieves any URL, get that URL and check if it's one of your lucky ones:
var urlsInInput = /[a-z0-9]+\.[a-zA-Z]{2,3}/ig.exec(val);
if (urlsInInput.length == 1 && $.inArray(urlsInInput[0], test) !== -1) {
instead of
if ($.inArray(val, test) !== -1) {
Fiddle
Here is my version handling the first url
Live Demo
$('#tweet_text').keydown(function () {
var val = this.value;
var urls = val.match(/[a-z0-9]+\.[a-zA-Z]{2,}/ig);
var alias = (urls && urls.length>0)
$('#custom_alias').toggle(alias);
var tweet = urls && urls.length>0 && $.inArray(urls[0], test) !== -1;
$('#tweet_campaign').toggle(tweet);
});
What #siledh said. Here is how you could use your current test array
var reg = new RexExp(test.join('|').replace(/\./ig, "\\."), 'ig')
if( reg.test(val) ) {
$('#tweet_campaign').show();
} else {
$('#tweet_campaign').hide();
}
The reason the campaign field begins to disappear again is that you compare the whole value of the input with the array. If you just find all domain matches and then compare them to your array it should work.
Like so:
var test = ["test1.com", "test2.com", "test3.com"];
$('#tweet_text_ga').hide();
$('#custom_alias').hide();
$('#tweet_campaign').hide();
$('#tweet_text').keyup(function () {
var alias = false;
var campaign = false;
var domain = /([a-z0-9]+(:?[\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})/ig;
var val = this.value;
var match = val.match(domain);
if (match) {
alias = true;
match.forEach(function(e) {
campaign = campaign || ($.inArray(e, test) !== -1);
});
}
if (alias === true) {
$('#custom_alias').show();
} else {
$('#custom_alias').hide();
}
if (campaign === true) {
$('#tweet_campaign').show();
} else {
$('#tweet_campaign').hide();
}
});
Something wrong with your $.isArray(val, test), the value you use is the whole value.
And not sure your purpose, so write a code like this. hope it would help.
http://jsfiddle.net/sheldon_w/KLuK8/
var test = ["test1.com", "test2.com", "test3.com"];
var urlReg = /[^\s]+\.[a-zA-Z]{2,3}/ig;
$('#tweet_text_ga').hide();
$('#custom_alias').hide();
$('#tweet_campaign').hide();
$('#tweet_text').keydown(function () {
var val = this.value;
var matchedUrls = [];
val.replace(urlReg, function (matched) {
matchedUrls.push(matched);
});
if (matchedUrls.length > 0) {
$('#custom_alias').show();
} else {
$('#custom_alias').hide();
}
$(matchedUrls).each(function (idx, url) {
if ($.inArray(url, test) !== -1) {
$('#tweet_campaign').show();
return false;
} else {
$('#tweet_campaign').hide();
}
});
});

Categories