Capture "updater" action from AngularUI's Bootstrap Typeahead - angularjs

How can I capture the "updater" event from AngularUI's UI Bootstrap directive?
I've defined my HTML:
<input type="text" pattern="[0-9]*" class="span6" placeholder="8675309" ng-model="empid" typeahead="entry for entry in httpEmpIdSearch($viewValue, 8)">
... and my associated function:
$scope.httpEmpIdSearch = function(query, limit)
{
return $http.get(
'/visitorLog/api/v1/employee/?limit=' + limit + '&empid__startswith=' + query
).then(function(response)
{
output = [];
angular.forEach(response.data.objects, function(value, key) {
this.push(value.bems.toString());
}, output);
return output;
});
}
I would like to take additional action (autocomplete a form) when the user clicks on an ID in the popup. If raw Bootstrap I would use the "updater":
$('#sampleElement').typeahead({
minLength: 3,
source: function (query, process)
{
// get the data
},
updater: function (item)
{
// user clicked on an item, do something more!
},
});
I've tried various listeners like:
$scope.$on('typeahead-updated', function(event)
{ ... }
But I've not way to let me capture such an event. Is there some way I can take additional action once a typeahead option is selected?

Also, in version 0.4.0 of the ui-bootstrap library the typeahead-on-select directive is now supported. This should provide your desired event.
See:
https://github.com/angular-ui/bootstrap/commit/91ac17c9ed691a99647b66b3f464e3585398be19

It does not appear that there is a way to hook into that event in the directive. You could however put a watch on the model value and simulate this behavior.
Check out this plunker example
Since you are pulling the list of data from the server on the fly this solution may not work for you. But I would definitely have a look at watch it seems like the best way to achieve the desired outcome.
Something along these lines could work.
$scope.output = [];
$scope.httpEmpIdSearch = function(query, limit)
{
return $http.get(
'/visitorLog/api/v1/employee/?limit=' + limit + '&empid__startswith=' + query
).then(function(response)
{
$scope.output.length = 0;
angular.forEach(response.data.objects, function(value, key) {
this.push(value.bems.toString());
}, $scope.output);
return $scope.output;
});
}
$scope.$watch('empid', function(newValue, oldValue){
if(newValue !== oldValue && $scope.output.indexOf(newValue) !== -1){
//do work here
}
});

The full list of typeahead related directives is here:
https://github.com/angular-ui/bootstrap/tree/master/src/typeahead/docs
typeahead-editable, typeahead-input-formatter, typeahead-loading, typeahead-min-length, typeahead-on-select, typeahead-template-url, typeahead-wait-ms

Related

ng-click doesn't work with external JavaScript

I am creating an ionic project and I am trying to integrate with Algolia autocomplete.js. I managed to make the search system work, however I added a ng-click on my search results and this function is not working as presented in this codepen that I did as example below:
http://codepen.io/marcos_arata/pen/VKVOky
Inside my algolia's result template:
<a ng-click="add_name({{{ name }}})">
Function that should be run when clicked:
$scope.add_name = function(name) {
alert('User added!');
console.log(name);
}
I tried to inject the results inside the scope but didn't work as well:
autocomplete('#search_name', { hint: false, debug: true, openOnFocus: true },[{
source: index.ttAdapter({ hitsPerPage: 15 }),
templates: {
header: '',
suggestion: function(hit) {
$scope.hit = hit;
return template.render(hit);
}
}
}]);
http://codepen.io/marcos_arata/pen/VKVOky
---- SOLVED ----
Instead of creating a ng-click function inside your templates, you can handle the event click of your search inside your "autocomplete:selected" function and use the dataset and suggestion results.
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
## create any functions with the suggestion and dataset results inside
});
EDITING THE ANSWER:
Here is the codepen:
Apparently the suggestion keep the name clicked, so you dont need an extra function:
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
});

how to monitor that the fields have changed and get oldvalues and new values as well

In my project i have implemented a web page in angularjs. When i go for the edit page and changes values of some fields,after that i click save button. On clicking the save button i want to get the fieldName in which change occur, old value & new value of the fields and send it to node.js.
i have tried but not working.
function auditChecker(oldObj,newObj,fn){
var newVal={};
var data=[];
for (var key in newObj) {
if(newObj.hasOwnProperty(key)){
for(var key1 in oldObj){
if(oldObj.hasOwnProperty(key1)){
if(oldObj[key1]!=newObj[key]){
newVal.fieldName = 'asdf';
newVal.oldval = oldObj[key];
newVal.newval = newObj[key];
}
}
}
}
}
data.push(newVal);
fn(data);
}
is there any quick way to do this. I need to write one funtion that works on different edit function.
You can use watch to get the old and new value.
For example,
$scope.$watch('yourScopeVariable', function (newValue, oldValue) {
});
Above doesn't look like an Angular approach to me! But to get this done with Angular approach you should try below
app.controller('ctrl', function ($scope) {
$scope.$watch('someValue', function (newValue, oldValue) {
console.log('oldValue=' + oldValue);
console.log('newValue=' + newValue);
});
});
<input type="text" id="exampleab" ng-model="someValue" >
Fiddle here http://fiddle.jshell.net/43onhmya/1/

Programmatically Select AngularJS Typeahead Option

I have a AngularJS Typeahead that retrieves matches asynchronously. When a barcode is scanned into the field, it returns the matching result, but the user still has to select it. I would like to automatically select the result if it's an exact match. I see that the typeahead has a select(idx) function, but am not sure how to get a reference to it from my controller.
I was envisioning something like this:
$scope.SearchItems = function (term) {
return $http.get('api/Items/Search', {
params: {
term: term
}
}).then(function (response) {
if (response.data.length == 1 && response.data[0].Code == term) {
// Somehow inform typeahead control to select response.data[0]
}
return response.data;
});
};
I had a similar issue and never figured how to access the typeahead's select(idx), but I managed to get this functionality working. Here's my hacky workaround....
$promise.then(function(res) {
angular.forEach(res, function(item, key) {
// if we have an exact match
if (item.title === term) {
// update model
$scope.src = item;
// find item in dropdown
var elm = '[id*=option-' + key + ']';
var opt = angular.element(document.querySelectorAll(elm));
//call click handler outside of digest loop
$timeout(function() {
opt.triggerHandler('click');
}, 0);
}
});
// return async results
return res;
});
Basically we just update our model manually, locate the element in our dropdown and then fire off the 'click' handler. Make sure you wrap the triggerHandler call in a $timeout() set to zero, otherwise you will get a $rootScope:inprog error since digest is already in progress.

WebSQL data into AngularJs DropDown

I have very simple question about getting data from WebSql
I have DropDown i.e
<select id="selectCatagoryFood" data-role="listview" data-native-menu="true"
ng-init="foodCatagory = foodCatagories.cast[0]"
ng-options="foodCatagory as foodCatagory.text for foodCatagory in foodCatagories.cast"
ng-model="foodCatagory"
ng-change="changeFoodCatagory()">
</select>
now i want to add data init from webSQL. I already get Data from webSql but i am confuse that how to add that data into DropDown
An example or hints maybe very helpful for me.
Update 1 :: Add Controller Code
myApp.controller('foodSelection',function($scope,foodCatagories){
$scope.foodCatagories = foodCatagories;
$scope.changeFoodCatagory = function(){
alert($scope.foodCatagory.value);
}
});
Update 2 webSQL and JayData
_context.onReady({
success: showData,
error: function (error){
console.log(error);
}
});
function showData(){
var option = '';
_context.FoodGroup.forEach(function(FG)
{
option += '<option value="'+FG.FoodGroupID+'">'+FG.Description+'</option>';
}).then(function(){
console.log(option);
});
}
Update 3
var myApp = angular.module('myApp',[]);
myApp.factory('foodCatagories',function(){
var foodCatagories = {};
foodCatagories.cast = [
{
value: "000",
text: "Select Any"
}
];
return foodCatagories;
});
Update 4
One thing that i didn't mention is that I am using JayData for getting data from webSQL to my App
I will try to explain how it works:
EDIT: Live demo
html
Here is your stripped down select.
<select ng-options="item as item.text for item in foodCategories"
ng-model="foodCategory"
ng-required="true"
ng-change="changeFoodCategory()">
</select>
The directive ng-options will fill automatically the option elements in your select. It will take the foodCategories variable from the $scope of your controller and foreach item in the collection, it will use the text property as the label shown (<option>{{item.text}}</option>') and it will select the whole objectitemas the value of the selectedoption. You could also refer to a property as the value like ({{item.text}}). Then yourng-modelwould be set to theid` value of the selected option.
The directive ng-model corresponds to the variable in the $scope of your controller that will hold the value of the selected option.
The directive ng-required allows you to check if a value has been selected. If you are using a form, you can check if the field is valid formName.ngModelName.$valid. See the docs for more details on form validation.
The directive ng-change allows you to execute a function whenever the selected option changes. You may want to pass the ng-model variable to this function as a parameter or call the variable through the $scope inside the controller.
If no default value is set, angular will add an empty option which will be removed when an option is selected.
You did use the ng-init directive to select the first option, but know that you could set the ng-model variable in your controller to the default value you would like or none.
js
Here I tried to simulate your database service by returning a promise in the case that you are doing an async request. I used the $q service to create a promise and $timeout to fake a call to the database.
myApp.factory('DbFoodCategories', function($q, $timeout) {
var foodCategories = [
{ id: 1, text: "Veggies", value: 100 },
{ id: 2, text: "Fruits", value: 50 },
{ id: 3, text: "Pasta", value: 200 },
{ id: 4, text: "Cereals", value: 250 },
{ id: 5, text: "Milk", value: 150 }
];
return {
get: function() {
var deferred = $q.defer();
// Your call to the database in place of the $timeout
$timeout(function() {
var chance = Math.random() > 0.25;
if (chance) {
// if the call is successfull, return data to controller
deferred.resolve(foodCategories);
}
else {
// if the call failed, return an error message
deferred.reject("Error");
}
}, 500);
/* // your code
_context.onReady({
success: function() {
deferred.resolve(_contect.FoodGroup);
},
error: function (error){
deferred.reject("Error");
}
});
*/
// return a promise that we will send a result soon back to the controller, but not now
return deferred.promise;
},
insert: function(item) {
/* ... */
},
update: function(item) {
/* ... */
},
remove: function(item) {
/* ... */
}
};
});
In your controller you set the variables that will be used in your view. So you can call your DbFoodCategories service to load the data into $scope.foodCategories, and set a default value in $scope.foodCategory that will be used to set the selected option.
myApp.controller('FoodSelection',function($scope, DbFoodCategories){
DbFoodCategories.get().then(
// the callback if the request was successfull
function (response) {
$scope.foodCategories = response; //response is the data we sent from the service
},
// the callback if an error occured
function (response) {
// response is the error message we set in the service
// do something like display the message
}
);
// $scope.foodCategory = defaultValue;
$scope.changeFoodCategory = function() {
alert($scope.foodCatagory.value);
}
});
I hope that this helped you understand more in detail what is happening!
See this example and how use $apply to update the data in scope.
in the new version we released a new module to support AngularJS. We've started to document how to use it, you can find the first blogpost here
With this you should be able to create your dropdown easily, no need to create the options manually. Something like this should do the trick:
myApp.controller('foodSelection',function($scope, $data) {
$scope.foodCatagories = [];
...
_context.onReady()
.then(function() {
$scope.foodCatagories = _context.FoodGroup.toLiveArray();
});
});
provided that FoodGroup has the right fields, of course

Angularjs E2E Testing with Angular-UI Select2 Element

I have a partial with a select2 element utilizing Angular UI http://angular-ui.github.io/
The issue I am running into is that the element is required and although i have successfully set the field through the following code, the required attribute is not removed as Angular's model must not be updating due to the outside change and I am not sure how to either provide a $scope.apply() or utilize another function of Angular to continue the test.
First to allow for direct jQuery functions to run: (taken from How to execute jQuery from Angular e2e test scope?)
angular.scenario.dsl('jQueryFunction', function() {
return function(selector, functionName /*, args */) {
var args = Array.prototype.slice.call(arguments, 2);
return this.addFutureAction(functionName, function($window, $document, done) {
var $ = $window.$; // jQuery inside the iframe
var elem = $(selector);
if (!elem.length) {
return done('Selector ' + selector + ' did not match any elements.');
}
done(null, elem[functionName].apply(elem, args));
});
};
});
Then to change the field value:
jQueryFunction('#s2id_autogen1', 'select2', 'open');
jQueryFunction('#s2id_autogen1', 'select2', "val", "US");
jQueryFunction('#s2id_autogen1', 'select2', 'data', {id: "US", text: "United States"});
jQueryFunction('.select2-results li:eq(3)', 'click');
jQueryFunction('#s2id_autogen1', 'trigger', 'change');
jQueryFunction('#s2id_autogen1', 'select2', 'close');
input('request._countrySelection').enter('US');
Note that not all of those functions are needed to reflect the changes to the ui, merely all that I have utilized to try and get this working...
To get this to work I consulted both Brian's answer and sinelaw, but it still failed in my case for two reasons:
clicking on 'div#s2id_autogen1' does not open the select2 input for me, the selector I used was 'div#s2id_autogen1 a'
getting the select2 element I would get the ElementNotVisibleError, probably because my select2 is inside a bootstrap modal, so I explicitly wait for the element to be visible before clicking it (you can read the original hint I read to use this here).
The resulting code is:
function select2ClickFirstItem(select2Id) {
var select2Input;
// Wait for select2 element to be visible
browser.driver.wait(function() {
select2Input = element(by.css('#s2id_' + select2Id + ' a'));
return select2Input;
}).then(function() {
select2Input.click();
var items = element.all(by.css('.select2-results-dept-0'));
browser.driver.wait(function () {
return items.count().then(function (count) {
return 0 < count;
});
});
items.get(0).click();
});
}
Hope it helps.
I was unable to get this to work within the Karma test runner, however this became significantly easier within the protractor test suite.
To accomplish this within the protractor test suite I used the following to select the first select2 box on the page and select the first option within that box:
var select2 = element(by.css('div#s2id_autogen1'));
select2.click();
var lis = element.all(by.css('li.select2-results-dept-0'));
lis.then(function(li) {
li[0].click();
});
The next select2 on the page has an id of s2id_autogen3
I'll second what #Brian said if you use protractor and the new karma this has worked for me:
function uiSelect(model, hasText) {
var selector = element(by.model('query.cohort')),
toggle = selector.element(by.css('.ui-select-toggle'));
toggle.click();
browser.driver.wait(function(){
return selector.all(by.css('.ui-select-choices-row')).count().then(function(count){
return count > 0;
});
}, 2000);
var choice = selector.element(by.cssContainingText('.ui-select-choices-row',hasText));
choice.click();
};
use it like:
if the value of the item you want to select is "Q3 2013" you can provide it the model of the selector and an exact or partial text match of the item you want to select.
uiSelect('query.cohort','Q3 2013');
or
uiSelect('query.cohort','Q3');
will both work
I made it work under Karma with following changes.
Add following DSL to the top of your e2e test file:
angular.scenario.dsl('jQueryFunction', function() {
return function(selector, functionName /*, args */) {
var args = Array.prototype.slice.call(arguments, 2);
return this.addFutureAction(functionName, function($window, $document, done) {
var $ = $window.$; // jQuery inside the iframe
var elem = $(selector);
if (!elem.length) {
return done('Selector ' + selector + ' did not match any elements.');
}
done(null, elem[functionName].apply(elem, args));
});
};
});
Then to change select2 value in your scenario use
it('should narrow down organizations by likeness of name entered', function() {
jQueryFunction('#s2id_physicianOrganization', 'select2', 'open');
jQueryFunction('#s2id_physicianOrganization', 'select2', 'search', 'usa');
expect(element('div.select2-result-label').count()).toBe(2);
});
Sometimes the select2 may take time to load, especially when working with ajax-loaded data. So when using protractor, and expanding on Brian's answer, here's a method I've found to be reliable:
function select2ClickFirstItem(select2Id) {
var select2 = element(by.css('div#s2id_' + select2Id));
select2.click();
var items = element.all(by.css('.select2-results-dept-0'));
browser.driver.wait(function () {
return items.count().then(function (count) {
return 0 < count;
})
});
items.get(0).click();
}
This uses the fact that driver.wait can take a promise as a result.

Resources