I am working with md-autocomplete in angularjs material. It's working fine when the search field is string. But when it's number then it's does not search as expected.
My code: HTML
<md-autocomplete ng-disabled="isDisabled"
name="DriverEMP_ID"
md-selected-item="selectedEMP_ID"
md-no-cache="ctrl.noCache"
md-search-text="driverText"
md-selected-item-change="selectedDriverEMP_IDChange(item)"
md-items="item in querySearchForDriverEMP_ID(driverText)"
md-item-text="item.display"
md-min-length="0"
placeholder="Driver ID"
required>
<md-item-template>
<span md-highlight-text="driverText" md-highlight-flags="^i">{{item.display}}</span>
</md-item-template>
<md-not-found>
No states matching "{{driverText}}" were found.
</md-not-found>
</md-autocomplete>
JS
var self = this;
var simulateQuery = false;
$scope.isDisabled = false;
self.DriverIDList = [{"value":"23869","display":"43721"},{"value":"36407","display":"48188"},{"value":"43942","display":"62924"},{"value":"13911","display":"22831"},{"value":"15531","display":"27175"},{"value":"13531","display":"21609"},{"value":"69526","display":"74854"},{"value":"14085","display":"23122"},{"value":"71018","display":"77915"}];
$scope.querySearchForDriverEMP_ID = querySearchForDriverEMP_ID;
function querySearchForDriverEMP_ID(query) {
var results = query ? self.DriverIDList.filter(createFilterForDriverID(query, objDriverIDListData)) : self.DriverIDList;
var deferred = $q.defer();
$timeout(function () { deferred.resolve(results); }, Math.random() * 1000, false);
return deferred.promise;
}
function createFilterForDriverID(query, DriverIDList) {
var lowercaseQuery = query.toString();
return function filterFn(DriverIDList) {
return (DriverIDList.display.indexOf(lowercaseQuery) !== -1);
};
}
But for string field it's working fine.
self.DriverIDList = [{"value":"23869","display":"Md. Foysal Iqbal"},{"value":"36407","display":"Md. Saiful Islam"},{"value":"43942","display":"Md.Sajib"},{"value":"13911","display":"Alamgir Hossain"},{"value":"15531","display":"Md.Hossain"},{"value":"13531","display":"Md. Masud Sheikh"},{"value":"69526","display":"Md. Sohel Rana"},{"value":"14085","display":"Monirul Islam"},{"value":"71018","display":"Md. Mohoshin Ali"},{"value":"71185","display":"Md. Al Amin"},{"value":"69306","display":"Md. Mohin Uddin"},{"value":"37269","display":"Md Anis Sardar"},{"value":"13909","display":"Md. Rafiqul Islam"},{"value":"10963","display":"Md. Shah Alam"},{"value":"13860","display":"Md. Abul Hashem"},{"value":"67752","display":"Md. Oli Ullah"},{"value":"45015","display":"Md. Abu Taher "},{"value":"560","display":"Md. Rehad Hossain Mamun"}];
Any help would be appreciated.
Why not keep it simple and just handle it right in the filter function instead of creating your own? Guessing the issue is in your filter. Try this and see if it works.
self.driverIDList.filter( item => item.display.indexOf(query.toString()) > -1);
Related
I am using md auto complete for Auto Segessions. while I am entering text I want to make a server call to get the results. but I want to abort the previous server call, when I have to control that in controller script or in service script.?
<md-autocomplete flex required flex-gt-sm="25
md-selected-item="school.college_name"
md-clear-button="true"
md-input-name="school.college"
md-input-minlength="3"
md-input-maxlength="40"
md-no-cache="noCache"
md-search-text="searchText"
md-items="item in ctrl.academyschoolsList"
md-search-text-change = "ctrl.searchAcademySchools(searchText)"
md-item-text="item.name">
<md-item-template>
<span class="item-title">
<span> {{item.name}}, {{item.city}}, {{item.state}}, {{item.zip}}
</span>
</span>
</md-item-template>
<div ng-messages="ctrl.leadEntryForm.school.college.$error">
<div ng-message="required">This field is required</div>
</div>
</md-autocomplete>
function searchAcademySchools(query) {
ctrl.academyschoolsList = [];
if (query.length > 3) {
C2Services.getAcademySchools(query).then(function (data) {
angular.forEach(data, function (value, key) {
ctrl.academyschoolsList.push(value);
});
});
}
}
An XHR can't be aborted once it starts, but new results can replace old results:
function searchAcademySchools(query) {
if (query.length > 3) {
C2Services.getAcademySchools(query).then(function (data) {
ctrl.academyschoolsList = data;
});
} else {
ctrl.academyschoolsList = [];
};
}
In this example each XHR will replace the old results.
There is a multi select element in my page.
<div class="form-group">
<select ng-show="'#Model.IsWinnerFilterVisible'" class="selectpicker" multiple title="Kazanan" ng-model="MainWinnerFilterSelected" id="ddl_winnerid"
data-ng-options="MainWinnerFilter.Id as MainWinnerFilter.Name for MainWinnerFilter in MainWinnerFilters"></select>
</div>
<div class="form-group">
<select ng-show="'#Model.IsReasonFilterVisible'" class="form-control" title="İlgilenilmeme Nedeni" ng-model="MainReasonFilterSelected" id="ddl_reasonid" ng-init="MainReasonFilterSelected= #Model.SelectedReason"
data-ng-options="MainReasonFilter.Id as MainReasonFilter.Name for MainReasonFilter in MainReasonFilters">
<option value="">İlgilenilmeme Nedeni</option>
</select>
</div>
And i can load this first with the code below
$scope.MainWinnerFilterSelected = #Html.Raw(Json.Encode(Model.SelectedWinner));
I want to reload this element with a new data but it dosent work.
$scope.loadReasonAndWinner= function() {
var productGroupFilters=$scope.productGroupFilterSelected;
var res = $http.post('#Url.Action("GetWinnerAndReasonList", "Details")', {'selectedProductGroup':productGroupFilters});
res.success(function(data, status) {
$scope.MainWinnerFilters=data.WinnerList;
$scope.MainReasonFilters=data.NotInterestingReasonList;
});
res.error(function(data, status) {
$scope.addError("alert-danger","Bir Hata oluştu.","Error");
});
}
In controller;
[HttpPost]
public ActionResult GetWinnerAndReasonList(int SelectedProductGroup)
{
var detail = new DetailsModel();
detail.SelectedProductGroup = SelectedProductGroup;
var winnerList = BiddingService.RetrieveWinners(null, VariableHelper.ListOf(detail.SelectedProductGroup));
detail.WinnerList = BiddingService.TranslateToFilter(winnerList);
var notInterestedActionStatus = new List<int>() { VariableHelper.Int(ActionStates.NotInterested) };
var notInterestingReasonList = BiddingService.RetrieveActionReason(notInterestedActionStatus, VariableHelper.ListOf(detail.SelectedProductGroup));
detail.NotInterestingReasonList = BiddingService.TranslateToFilter(notInterestingReasonList);
return Json(detail);
}
New data comes true and it works with combobox (MainReasonFilter) but it dosent change the items in multiselect element(MainWinnerFilter). Any idea with that?
I would like display in md-autocomplete large list (around 50 000 records).
Autocomplete directive uses mdVirtualRepeat which provide infinity scrolling. I couldn't find way to pass md-on-demand option. Maybe someone find way to do that.
I really appreciate any help you can provide
UPDATE
I forget to share the code. I haven't problem with code performance but when list is rendering app is not responsive. In my opinion problem is in virtual rendering which still try to render whole list instead of visible part.
PS. I know $scope is bad but I'm using this example in angular-formly.
JS
$scope.to.options = [];
$scope.ctrl = {
selectedItem: null,
isDisabled: $scope.to.disabled,
noCache: $scope.to.noCache,
placeholder: $scope.to.placeholder || 'Wybierz element',
minLength: $scope.to.minLength || 0,
querySearch: querySearch,
searchTextChange: searchTextChange,
selectedItemChange: selectedItemChange,
delay: $scope.to.delay || 350,
options: []
};
if ($scope.to.dictId) {
dictionariesRepository.get($scope.to.dictId).then(function (res) {
$scope.ctrl.options = createOnDemandObject(res.Data.map(function (elem) {
return { value: elem[$scope.to.FieldVal], name: getLabel($scope.to.FieldFormula, elem) };
}));
var val;
if ((val = getValue())) {
var selected = $scope.ctrl.options.filter(function (elem) {
return elem.value == val;
})[0];
if (selected) {
$scope.ctrl.selectedItem = selected;
}
}
});
}
function createOnDemandObject(list) {
return {
list: list,
getLength: function () {
return this.list.length
},
getItemAtIndex: function (index) {
return this.list[index];
}
}
}
function searchTextChange(text) {
//$log.info('Text changed to ' + text);
}
function selectedItemChange(item) {
var getter = $parse($scope.options.key);
var setter = getter.assign;
setter($scope.model, item[$scope.to.FieldVal]);
}
function querySearch(query) {
var options = $scope.ctrl.options;
return query ? options.filter(createFilterFor(query)) : options;
}
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(elem) {
return (elem.name.indexOf(lowercaseQuery) === 0);
};
}
HTML
<md-autocomplete ng-disabled="ctrl.isDisabled" md-no-cache="ctrl.noCache" md-selected-item="ctrl.selectedItem" md-search-text-change="ctrl.searchTextChange(ctrl.searchText)"
md-delay="ctrl.delay"
md-search-text="ctrl.searchText" md-selected-item-change="ctrl.selectedItemChange(item)"
md-items="item in ctrl.querySearch(ctrl.searchText)"
md-on-demand
md-item-text="item.name" md-min-length="ctrl.minLength" placeholder="{{ctrl.placeholder}}">
<md-item-template>
<span md-highlight-text="ctrl.searchText" md-highlight-flags="^i">{{item.name}}</span>
</md-item-template>
<md-not-found>
Nie znaleziono pasującego wyniku dla "{{ctrl.searchText}}".
</md-not-found>
</md-autocomplete>
I'm quite new to AngularJS and struggling a bit to have some input fields updated after an autocompletion event using google maps.
The idea is that when the user inputs his city/zip code, I would update 3 fields which are themselves linked to an object.
So far, I managed to have a working code except that sometimes the fields are not updated immediately : I have to autocomplete twice so that the good value will appear in the fields.
I've tweaked an existing angular directive in order to get what I want but since this is new to me, I dont know if I'm using the correct approach.
Below is the JS directive I use :
angular.module( "ngVilleAutocomplete", [])
.directive('ngAutocomplete', function($parse) {
return {
scope: {
details: '=',
ngAutocomplete: '=',
options: '=',
data: '='
},
link: function(scope, element, attrs, model) {
//options for autocomplete
var opts
//convert options provided to opts
var initOpts = function() {
opts = {}
if (scope.options) {
if (scope.options.types) {
opts.types = []
opts.types.push(scope.options.types)
}
if (scope.options.bounds) {
opts.bounds = scope.options.bounds
}
if (scope.options.country) {
opts.componentRestrictions = {
country: scope.options.country
}
}
}
}
initOpts()
//create new autocomplete
//reinitializes on every change of the options provided
var newAutocomplete = function() {
scope.gPlace = new google.maps.places.Autocomplete(element[0], opts);
google.maps.event.addListener(scope.gPlace, 'place_changed', function() {
scope.$apply(function() {
scope.details = scope.gPlace.getPlace();
//console.log(scope.details)
var HasCP = false;
for (var i=0 ; i<scope.details.address_components.length ; i++){
for (var j=0 ; j<scope.details.address_components[i].types.length ; j++){
if (scope.details.address_components[i].types[j] == 'postal_code' && scope.data.CP != 'undefined'){
scope.data.CP = scope.details.address_components[i].long_name;
HasCP = true;
} else if (scope.details.address_components[i].types[j] == 'locality' && scope.data.Ville != 'undefined') {
scope.data.Ville = scope.details.address_components[i].long_name;
} else if (scope.details.address_components[i].types[j] == 'country' && scope.data.Pays != 'undefined') {
scope.data.Pays = scope.details.address_components[i].long_name;
}
}
}
if (!HasCP){
var latlng = {lat: scope.details.geometry.location.lat(), lng: scope.details.geometry.location.lng()};
var geocoder = new google.maps.Geocoder;
geocoder.geocode({'location': latlng}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
for (var i=0 ; i<results[0].address_components.length ; i++){
for (var j=0 ; j<results[0].address_components[i].types.length ; j++){
if (results[0].address_components[i].types[j] == 'postal_code' && scope.data.CP != 'undefined'){
scope.data.CP = results[0].address_components[i].long_name;
console.log('pc trouvé :' + scope.data.CP);
}
}
}
}
});
}
//console.log(scope.data)
scope.ngAutocomplete = element.val();
});
})
}
newAutocomplete()
//watch options provided to directive
scope.watchOptions = function () {
return scope.options
};
scope.$watch(scope.watchOptions, function () {
initOpts()
newAutocomplete()
element[0].value = '';
scope.ngAutocomplete = element.val();
}, true);
}
};
});
The matching HTML code is below :
<div class="form-group">
<lable>Code postal : </label>
<input type="text" id="Autocomplete" class="form-control" ng-autocomplete="cities_autocomplete" details="cities_autocomplete_details" options="cities_autocomplete_options" data="client" placeholder="Code postal" ng-model="client.CP" />
</div>
<div class="form-group">
<lable>Ville : </label>
<input type="text" id="Autocomplete" class="form-control" ng-autocomplete="cities_autocomplete" details="cities_autocomplete_details" options="cities_autocomplete_options" data="client" placeholder="Ville" ng-model="client.Ville" />
</div>
<div class="form-group">
<lable>Pays : </label>
<input type="text" class="form-control" name="Pays" ng-model="client.Pays" placeholder="Pays" />
</div>
You'll see that I pass the "client" object directly to my directive which then updates this object. I expected angular to update the html page as soon as the values of the client object are updated but I will not always be the case :
If I search twice the same city, the values are not updated
If I search a city, Google wont send me a zip code so I have to do another request to the geocoding service and I get the zipcode in return but while my client.CP field is correctly updated, changes are not visible in the CP input field until I do another search.
Thanks in advance for any advice on what I'm doing wrong.
I have angular 1.3, and i have the following array:
data : [
{
id :2,
name : "danny davids",
age :9
},
{
id :3,
name : "sanny gordon",
age :9
}
]
I want the filter to do the follwing:
When i start writing the word "s", i want the danny davids to disappear, right now the default behavior is, both of them are still shown (the s is in the end of the last name of danny).
strict mode is something that i dont want to use, the behavior i want is:
if there is no value in the input, i want to see all, if i start to write i want to see the exact one by firstName/lastName.
is there a default filter for this in angular 1.3?
You can filter match by any characters:
Sample condition:
yourDataList.display.toLowerCase().indexOf(searchData) !== -1;
Example:
function createFilterForAnycharacters(searchData) {
var lowercaseQuery = query.toLowerCase();
return function filterFn(yourDataList) {
return (yourDataList.display.toLowerCase().indexOf(searchData) !== -1);
};
}
I suggest using $filter by a custom filter function for you ng-repeat. According to the documentation, $filter expects
function(value, index, array): A predicate function can be used to write arbitrary filters. The function is called for each element of the array, with the element, its index, and the entire array itself as arguments.
And only elements that return true with be shown. So all you have to do is write that function.
Your filter function might look like this:
$scope.filterData = function (obj) {
return anyNameStartsWith(obj.name, $scope.searchFilter);
};
function anyNameStartsWith (fullname, search) {
//validate if name is null or not a string if needed
if (search === '')
return true;
var delimeterRegex = /[ _-]+/;
//split the fullname into individual names
var names = fullname.split(delimeterRegex);
//do any of the names in the array start with the search string
return names.some(function(name) {
return name.toLowerCase().indexOf(search.toLowerCase()) === 0;
});
}
Your HTML might look something like this:
<input type="text" ng-model="searchFilter" />
<div ng-repeat="obj in data | filter : filterData">
Id: {{obj.id}}
Name: {{obj.name}}
</div>
A demo via plnkr
Use this custom filter to get result match starting characters
app.filter('startsWithLetter', function () {
return function (items, letter) {
var filtered = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.substr(0,letter.length).toLowerCase() == letter.toLowerCase()) {
filtered.push(item);
}
}
return filtered;
};
});
it works for your scenario, you can create custom filter
below is html code
<div ng-app="app">
<div ng-controller="PersonCtrl as person">
<input type="text" ng-model="letter" placeholder="Enter a letter to filter">
<ul>
<li ng-repeat="a in person.data | startsWithLetter:letter">
{{a.name}}
</li>
</ul>
</div>
</div>
js code
var app = angular.module('app', []);
app.filter('startsWithLetter', function () {
return function (items, letter) {
var filtered = [];
var letterMatch = new RegExp(letter, 'i');
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (letterMatch.test(item.name.substring(0, 1))) {
filtered.push(item);
}
}
return filtered;
};
});
app.controller('PersonCtrl', function () {
this.data = [
{
id :2,
name : "danny davids",
age :9
},
{
id :3,
name : "sanny gordon",
age :9
}
]
});
Need to create a custom filter function to do this. There is no default method to match first character in angular.
https://docs.angularjs.org/guide/filter