Angular Bootstrap Typeahead dropdown - javascript

I have an Angular ui-boostrap typeahead component that is working correctly until I added one requirement to the whole function. I want to call the backend for the suggested results just after user types in 3 letters. It is done correctly but my problem is that the results are visible only when users type in the 4th letter. Is there any way to bypass this, by forcing it to refresh the UI just after user types in the 3rd letter.
Code is :
HTLM
<input name="states" id="states" type="text" ng-model="vm.cityName"
uib-typeahead="municipality as municipality.city + ' (' + municipality.name + ') '+municipality.zipCode for municipality in vm.getMunicipalitiesByCity($viewValue) | filter:$viewValue | limitTo:8" class="form-control" typeahead-on-select="vm.citySelected()" >
JS Controller
vm.getMunicipalitiesByCity = function (cityName) {
if (cityName != undefined && cityName.length == 3) {
CalculationEndpointService.municipalitiesByCity({cityName: cityName}, function (result) {
vm.municipalities = result.map(
function (item) {
return item;
}
);
});
}
if(cityName.length<3){
vm.municipalities=[""];
}
return vm.municipalities;
};

you could use typeahead-min-length attribute. see https://angular-ui.github.io/bootstrap/#/typeahead

Related

set default value in dynamic dropdown angularjs

please help me with my problem I'm a little new to angularjs, my problem is that I need to be able to set the default value in the select option when there is only one item because it's a dynamic select option, it has another dropdown with many items but it's okay , what is needed when only one item must be selected in the select option using ng-options
<select class="form-control form" ng-model="relatedTo" style="height: 40px;" ng-options="c.CUSTCODE as c.CUSTNAME + ' - ' + c.CUSTCODE for c in customers | filter:code" > </select><span style="color:red" ng-show="type === 9 || type !== 9"></span>
ANGULARJS
$http.post('commandCenter.aspx/allCustomer', {}).then(
function onSuccess(response) {
$scope.customers = JSON.parse(response.data.d);
console.log($scope.customers); },
function onError(response) {
console.log('error !!! ');
});
Picture no.1 It's okay in this picture because he has a lot of list of items.
Picture no.2 When there is only one item, it must be default or selected.
What #MrJami said. In code something like
$http.post('commandCenter.aspx/allCustomer', {}).then(
function onSuccess(response) {
$scope.customers = JSON.parse(response.data.d);
if ($scope.customers.length === 1) {
$scope.relatedTo = $scope.customers[0].CUSTCODE;
}
console.log($scope.customers); },
function onError(response) {
console.log('error !!! ');
});

Using array .map and $scope.__.reduce() for advanced angularjs filters

i'm currently building some advanced filters to use in a search input. I'm trying to search through multiple JSON objects at once. I've been able to use the functions .map and .reduce to create these filters. For more information look at this question that gave me this solution to work with. So for the orders page i've been able to add the customer firstname through this function. Take a look at the code below:
$http.get('config/get/getOrders.php', {cache: true}).then(function(response){
$scope.orders = response.data.orders.order;
$scope.orders.map( function addPlace(item) {
item.firstname = $scope.customers.reduce(function(a,customers){
return item.id_customer === customers.id ? customers.firstname : a;
}, '');
return item;
});
});
by using $scope.orders.map() and $scope.customers.reduce() i'm able to add a value to the orders array. By doing this i'm able to extend my search filter with the customer_name.
The problem i'm having now is actually two different things. The first thing is that i'm trying to create a more advanced comparison before obtaining one item. (The Combination_product_code) Take a look at the code below:
$http.get('config/get/getProducts.php', {cache: true}).then(function(response){
$scope.products = response.data.products.product;
$scope.products.map( function addPlace(item) {
item.eanCombination = $scope.productCombinations.reduce(function(a, productCombinations, stock_availables){
return item.id === stock_availables.id + stock_availables.id === stock_availables.id_product_attribute + stock_availables.id_product_attribute === productCombinations.id ? productCombinations.ean13: a;
}, '');
return item;
});
});
So as you can see i need to check through multiple values before i'm able to obtain the ean13 sadly this isn't working and i'm wondering if i'm setting the comparison right? i've checked all the value names multiple times and they are correct.
The second issue is adding multiple new items. I've been trying to add a product_name but also the products reference code. Take a look at the code below:
$http.get('config/get/getStock.php', {cache: true}).then(function (response) {
$scope.stock_availables = response.data.stock_availables.stock_available;
$scope.stock_availables.map( function addPlace(item) {
item.product_name = $scope.products.reduce(function(a,products){
return item.id_product === products.id ? products.name.language : a;
},
item.product_reference = $scope.products.reduce(function(b,products) {
return item.id_product === products.id ? products.reference: b;
}));
return item;
});
});
So my issues are adding multiple comparison queries before obtaining and adding the item and adding multiple items.
Update:
I've solved my issue about the "adding multiple items." The mistake i've made was in closing the function correctly. A working example:
$http.get('config/get/getStock.php', {cache: true}).then(function (response) {
$scope.stock_availables = response.data.stock_availables.stock_available;
$scope.stock_availables.map( function addPlace(item) {
item.product_name = $scope.products.reduce(function(a,products){
return item.id_product === products.id ? products.name.language : a;
}),
item.product_reference = $scope.products.reduce(function(b,products) {
return item.id_product === products.id ? products.reference: b;
});
return item;
});
});
This means that i'm looking for a solution about using multiple comparison queries before obtaining the data.
The workflow for this data is:
i'm using ng-repeat="product in products" within the products data i'm creating a filter that corresponds to the following data:
Products table:
id,
Name,
price,
etc,
stock table:
id,
product_id,
quantity,
product_attribute_id,
Combinations table:
id -> "Corresponds with attribute_id from stock table",
combinationEAN -> "The value i'm trying to reach",
So the workflow is:
Stock table -> id_product from products table -> combination id -> attribute_id from stock table.
I'm able to filter this within my view by using:
<h5 ng-if="combination.id >= 1 && product.id == stock_available.id_product && stock_available.id_product_attribute == combination.id" ng-repeat="combination in productCombinations" ng-bind="combination.ean13"></h5>
What i would like to to do is add that combination.ean13 directly to the products $scope by using the example above. So that i'm able to use a search input to search for that value and only show those products. See the example below:
<input type="text" class="bc-f3f3f3 form-control no-border" ng-model="productSearch" ng-change="filterProduct(productSearch)" placeholder="{{ 'Zoek of scan producten' | translate }}">
<div ng-infinte-scroll="loadMore()" infinite-scroll-disabled='products.busy' infinite-scroll-distance='1' class="align-center tcolor-2a95cf text-center padding-t-25 padding-s-45 no-margin row">
<div class="productimg col-4" ng-repeat="product in products | orderBy: '-date_add' | filter: filterProduct(productSearch) | filter: {id_category_default: productCategory}">
<div class="out-of-stock" ng-if="product.id == stock_available.id_product && stock_available.quantity == 0 && stock_available.id_product_attribute == 0" ng-repeat="stock_available in stock_availables">
<div class="stock-circle"></div>
</div>
<div>
<img ng-value="{{reference}}" data-toggle="modal" data-target=".bd-example-modal-sm{{product.id}}" ng-value="{{ean13}}" alt="{{product.name.language}}" ng-src="{{settings.url}}/{{product.id_default_image}}-home_default/{{product.link_rewrite.language}}.jpg" err-src="{{settings.url}}//img/p/nl-default-home_default.jpg" class="img-responsive product-img"/>
<p ng-bind="product.name.language"></p>
<p ng-bind="product.eanCombination"></p>
</div>
</div>
</div>
// Controller:
$http.get('config/get/getProducts.php', {cache: true}).then(function(response){
$scope.products = response.data.products.product;
$scope.products.map( function addPlace(item) {
item.eanCombination = $scope.productCombinations.reduce(function(a, productCombinations, stock_availables){
return item.id === stock_availables.id_product + stock_availables.id_product === stock_availables.id_product_attribute + stock_availables.id_product_attribute === productCombinations.id ? productCombinations.ean13: a;
}, '');
return item;
});
});
$scope.filterProduct = function(productSearch) {
return (name.language = productSearch) || (ean13 = productSearch) || (reference = productSearch) || (eanCombination = productSearch);
};
If you have any questions please ask them in the comments below.
As always, thanks in advance!

$.getJson won't affect view until event

Didn't really know how to word the question. Using angular. Anyways, I'm trying to have it so when the user types in the text box a state, once it has been verified that the state exists (comparing to an array of all 50), it will automatically call a getJSon Jquery request for a JSON object. But for some reason, it doesn't execute right away, instead I have to press a key after doing so.
Code:
$scope.checkState = function(team) {
//check for team
console.log("Searching for " + document.getElementById('team').value)
var teamFind = document.getElementById('team').value;
var team = $.inArray(teamFind, $scope.states);
console.log("team: " + team);
if (team == -1)
{
console.log("Not found");
$scope.selectedState = "Not Found";
teamFound = false;
}
//correct team
if (team > -1)
{
$.getJSON('https://api.myjson.com/bins/1ak21r', function (data) {
//console.log(data.bowl);
$scope.items = data;
console.log($scope.items);
console.log("Team Bowl: " + team_bowl)
console.log("Found: " + $scope.states[team]);
$scope.selectedState = $scope.states[team];
teamFound = true;
});
}
}
html code
<p class="w3-large w3-center">
<input type="text" name="team" id="team" value="Whats Your Team?" ng-keyup="checkState(team)">
</p>
<p class="w3-jumbo w3-center">
<span id="bowl">{{ selectedState }}</span>
</p>
<p class="w3-large w3-center">
<span>f{{ items }}</span>
</p>
</div>
I know it might be hard to understand what I want, but I was creating a sample application that would show what bowl game a team was competing in. The user types the team into the text-box id=team below, and I wanted it done without them having to press enter or submit.
On the key-up, it runs the check function. So for example, once Maryland is entered, the getJson will run and correctly logs the data, but the $scope.items isn't updated until after I type one more key AFTER I entered the state
So like typing:
M-A-R-Y-L-A-N-D(CONSOLE LOGS THE JSON OBJECT CORRECTLY, BUT ON THE HTML {{ items }} STILL SHOWS NOTHING)-any_key_here(NOW IT GETS UPDATED)
Thanks for any help.
EDIT:
So I was able to get it by having another function be called with the data inside the JSON function.
myFunction(data);
which calls
function myFunction(items) {
console.log(items);
document.getElementById('bowl2').innerHTML = items.team;
};
You are using jQuery's ajax method which does not integrate with angular to inform angular when it is done. It's best to use angular's built in $http methods which do the same thing expect when they complete they trigger a digest cycle which will update the view.
You will need to inject $http into your controller or service (depending on where this code lives) in order to use it. Once you do that, you should be able to make the following modification to you code.
Change
$.getJSON('https://api.myjson.com/bins/1ak21r', function (data) {
//console.log(data.bowl);
$scope.items = data;
console.log($scope.items);
console.log("Team Bowl: " + team_bowl)
console.log("Found: " + $scope.states[team]);
$scope.selectedState = $scope.states[team];
teamFound = true;
});
to
$http.get('https://api.myjson.com/bins/1ak21r')
.then(function (response) {
var data = response.data;
//console.log(data.bowl);
$scope.items = data;
console.log($scope.items);
console.log("Team Bowl: " + team_bowl)
console.log("Found: " + $scope.states[team]);
$scope.selectedState = $scope.states[team];
teamFound = true;
});

How to filter through a table using ng-repeat checkboxes with Angularjs

Once upon a time this was working but somehow it's broken. I want to be able to produce checkboxes using ng-repeat to get as many checkboxes as required based on stored data and use these to filter through a table produced.
Additionally I don't want identical values for the checkboxes to be repeated.
I have made a plnkr with the code.
<div class="row">
<label data-ng-repeat="x in projects">
<input
type="checkbox"
data-ng-true-value="{{x.b}}"
data-ng-false-value=''
ng-model="quer[queryBy]" />
{{x.b}}
</label>
</div>
http://plnkr.co/edit/RBjSNweUskAtLUH3Ss6r?p=preview
So in summary.
Checkboxes to filter Ref.
Checkboxes to be unique.
Checkboxes to be made based off ng-repeat using Ref.
Okay, here's how to do it.
First, let's add a couple of lines of CSS in your to make sure all the checkboxes are visible:
<style>
.row { margin-left: 0px }
input[type=checkbox] { margin-left: 30px; }
</style>
Next, add the following lines to your controller:
app.filter('unique', function() {
return function (arr, field) {
var o = {}, i, l = arr.length, r = [];
for(i=0; i<l;i+=1) {
o[arr[i][field]] = arr[i];
}
for(i in o) {
r.push(o[i]);
}
return r;
};
})
app.controller("maincontroller",function($scope){
$scope.query = {};
$scope.quer = {};
$scope.queryBy = '$';
$scope.isCollapsed = true;
$scope.selectedRefs = [];
$scope.myFilter = function (item) {
var idx = $scope.selectedRefs.indexOf(item.b);
return idx != -1;
};
$scope.toggleSelection = function toggleSelection(id) {
var idx = $scope.selectedRefs.indexOf(id);
if (idx > -1) {
$scope.selectedRefs.splice(idx, 1);
}
else {
$scope.selectedRefs.push(id);
}
};
Phew.
For some reason, your Plunkr's version of AngularJS didn't recognise the unique attribute, so I added one to your controller.
Finally, change your html to this:
<div class="row">
<label data-ng-repeat="x in projects | unique:'b' | orderBy:'b'" >
<input
id="x.b"
type="checkbox"
ng-click="toggleSelection(x.b)"
ng-init="selectedRefs.push(x.b)"
ng-checked="selectedRefs.indexOf(x.b) > -1" />
{{x.b}}
</label>
</div>
... and your ng-repeat to this...
<tr ng-click="isCollapsed = !isCollapsed" ng-repeat-start="x in projects | filter:myFilter | orderBy:orderProp">
If you're interested in knowing how this works, add these lines:
<div style="margin:10px 10px 30px 10px">
<pre>{{ selectedRefs }} </pre>
</div>
I love this trick: you can see the exact contents of our "selectedRefs" array, and see it change as we tick/untick our checkboxes. This really helps when developing/testing our bindings!
As you can see, these changes use the new unique function to get your list of distinct values from your project array, and when the page first loads, we push all of the values into our new "selectedRefs" array.
["123","321","456","654","789","987"]
Then, as you tick/untick the checkboxes, we add/remove that item from this list.
Finally, we use that filter in the ng-repeat.
ng-repeat-start="x in projects | filter:myFilter | orderBy:orderProp"
Job done !
Update
If you wanted to start off with all checkboxes unticked, then it's a simple change. Just remove this line...
ng-init="selectedRefs.push(x.b)"
..and change the myFilter function to show all items initially..
$scope.myFilter = function (item) {
if ($scope.selectedRefs.length == 0)
return true;
var idx = $scope.selectedRefs.indexOf(item.b);
return idx != -1;
};
And to add a "Clear all" button, simply add a button to your form which calls a function in your AngularJS controller like this..
$scope.clearAll = function () {
$scope.selectedRefs = [];
};
(I haven't tested these suggestions though.)
ng-false-value directive needs a value set. Try ng-false-value='false' or ng-false-value='null' (in fact you can skip this one entirely if it has to just be a falsy value and not something concrete, like a string or certain number).
As you've pointed out in the comments, after selecting and then clearing the checkboxes, all rows are filtered out. It happens because unchecking the checkbox will set its value to false, and this does not agree with your entities' values (as you probably know, just stating it for others).
Therefore you do need to set this value to empty string in the end. That'd be the way:
$scope.$watch('quer.$', function () {
if ($scope.quer.$ === false) {
$scope.quer.$ = '';
}
});

AngularUI-Bootstrap Typeahead: Grouping results

I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a sample scenario
There are some users in the database, each user has a "Department". One user name can be available in multiple departments.
The end-user types in the names to search users from the database and retrieves the list in the typeahead list. Since one user name can belong to multiple departments, the requirement is to show the user names grouped by different departments. Something like this:
Then the user can select the desired user name and proceed.
As per the Typeahead documentation present here, I don't see any option to cater to my requirement.
I have tried the this workaround: Whenever the typeahead array is getting formed, I appended the user department to the array element:
$scope.fetchUsers = function(val) {
console.log("Entered fetchUsers function");
return $http.get("http://localhost:8080/TestWeb/users", {
params : {
username : val
}
}).then(function(res) {
console.log("Response:",res);
var users = [];
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
console.log("users=",users);
return users;
});
};
This way, at least the end user sees the department. But when I select the record, the selected value is the full content of the array element. Below is sample screenshot to elaborate:
HTML
Users from local service
<pre>Model: {{userList | json}}</pre>
<input type="text" ng-model="userList" placeholder="Users loaded from local database"
typeahead="username for username in fetchUsers($viewValue)"
typeahead-loading="loadingUsers" class="form-control">
<i ng-show="loadingUsers" class="glyphicon glyphicon-refresh"></i>
User types in the string
User selects one record
I want to avoid the department (in this case, string - Desc 4 ) when user selects a record.
Is there any way I can achieve this grouping without any workaround? Or is there any way I can enhance my workaround?
I used to have a similar requirement and here is how I did it that time.
Example Plunker: http://plnkr.co/edit/zujdouvB4bz7tFX8HaNu?p=preview
The trick is to set the typeahead-template-url to a custom item template:
<input type="text" class="form-control" placeholder="Users loaded from local database"
ng-model="selectedUser"
typeahead="user as user.name for user in getUsers($viewValue)"
typeahead-template-url="typeahead-item.html" />
The item template, this represent each item in a dropdown:
<div class="typeahead-group-header" ng-if="match.model.firstInGroup">Desc {{match.model.group}}</div>
<a>
<span ng-bind-html="match.label | typeaheadHighlight:query"></span>
</a>
As you can see, there is an ng-if to show a group header if that item has a property firstInGroup set to true.
The firstInGroup properties are populated like this using lodashjs:
$scope.getUsers = function (search) {
var filtered = filterFilter(users, search);
var results = _(filtered)
.groupBy('group')
.map(function (g) {
g[0].firstInGroup = true; // the first item in each group
return g;
})
.flatten()
.value();
return results;
}
Hope this fit to your requirement too.
please see here http://plnkr.co/edit/DmoEWzAUHGEXuHILLPBp?p=preview
instead of creating new objects here:
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
use create template :
<script type="text/ng-template" id="customTemplate.html">
<a> {{ match.model.name}} - department : {{match.model.dept}}</a>
</script>
and use it in your Typeahead directive
<input type="text" ng-model="selected"
typeahead="user.name as user for user in users | filter:$viewValue | limitTo:8" class="form-control"
typeahead-template-url="customTemplate.html">

Categories

Resources