AngularJS Typeahead prepopulation - angularjs

I have a typeahead that I want to contain a description but store the Id.
I'm currently doing it something like this...
<input type="text"
ng-model="SelectedWarehouse.Info"
typeahead="whs.info for whs in warehouses($viewValue)"
typeahead-on-select="POHeader.warehouseId=$item.id;"
class="form-control input-sm "
id="whs" placeholder="Warehouse"
ng-disabled="isReadOnly"
typeahead-wait-ms="500"
ng-enter="say('hello');" />
Where the jsonp that I get from the $scope.warehouses function looks something like this:
([{"id":"10", "info": "Lakeland (10)"}, {"id":"11", "info": "Denver (11)"}])
of course along with the appropriate jsonp callback info -- this is just an example
The sloppy part of it is this:
When the screen opens the $scope.SelectedWarehouse is not populated with anything.
I then wrote something to pre-populate the SelectedWarehouse but there has to be a better way to do this using typeahead.
What's the right/good/best (pick one) way to do this?
Another problem is not knowing when my async call will actually populate my objects. Promises promises... I thought I'd try using the ng-change function call hoping that when the data gets populated the ng-change function would fire ... so far that didn't work. I'm not quite done experimenting with that though...
Here's part of my $resource factory. What would I need to do to get a success call working with this?
ipoModule.factory('SearchRepository', function ($resource) {
return {
get: function (txt) {
return $resource('/api/Search?SearchText=' + txt).query()
},
getBuyers: function () {
return $resource('api/Buyers').query()
},
getVendors: function () {
return $resource('api/Vendors').query()
},
getVendorSearch: function (txt) {
return $resource('api/Vendors/' + txt + "?callback=JSON_CALLBACK").query()
}, ....
Any ideas?

i think something like this might work. You can bind the typeahead to a collection instead of a method, and update that collection on keypress by fetching values from the server. You can also watch for changes on the collection so you will know when your data is about to be populated.
See Demo: http://jsfiddle.net/yfz6S/17/
view.html
<div style="margin: 40px;">
<div ng-controller="WarehousesCtrl">
<pre>query: {{query | json}}</pre>
<pre>selectedWarehouse: {{selectedWarehouse | json}}</pre>
<input
class="form-control input-sm"
ng-model="query"
typeahead="warehouse.info for warehouse in warehouses | filter:$viewValue"
typeahead-on-select="selectedWarehouse = $item;"
ng-keypress="fetchWarehouses();"
/>
</div>
</div>
controller.js
.controller('WarehousesCtrl', ['$scope', '$http', '$q', function ($scope, $http, $q) {
$scope.query = null;
$scope.selectedWarehouse = null;
$scope.warehouses = [];
$scope.fetchWarehouses = function() {
$http.jsonp(
'http:/mysite.com/mywarehouses?q=' + $scope.query
).success(function(data){
$scope.warehouses = data.warehouses;
}).error(function(){
// this request will fail so lets just set some dummy warehouses
$scope.warehouses = [
{id:1, info:"Toronto"},
{id:2, info:"Tobermory"},
{id:3, info:"Oshawa"},
{id:4, info:"Ottawa"},
{id:5, info:"Ajax"},
];
});
};
}]);

I made my own work-around because I couldn't get the pre-population to work with a typeahead.
<input type="text"
ng-model="POHeader.SelectedWarehouse"
typeahead="whs.info for whs in warehouses($viewValue)"
typeahead-on-select="POHeader.warehouseId=$item.id;"
class="form-control input-sm "
id="whs" placeholder="Warehouse"
ng-disabled="isReadOnly"
typeahead-wait-ms="500" />
I changed the view that populates the POHeader to include a field called SelectedWarehouse. This is my pre-formatted typeahead value which looks the same as the whs.info returned from the typeahead warehouses list.
It's not ideal but it works and I can't afford to waste more time on this.

Related

AngularJS - How to pass data from View (HTML) to Controller (JS)

I am really new to AngularJS. I want to pass some object from View (HTML) to my controller (JS).
Actually my Client will send me data in HTML and I have to take that data and process that data in my controller and then display the processed output on screen. He will be using some back-end technology called ServiceNow - https://www.servicenow.com/ .
All the solutions I saw had some event like click event or change event, but in my case this has to be done on page load.
I m using Input type hidden for passing the data to the controller, seems like it's not working.
So is there any other way I can do this ?
Here's the code I am trying to use
<div ng-controller="progressController" >
<input type="hidden" value="ABCD" ng-model="testingmodel.testing">
</div>
app.controller('progressController', function($scope) {
console.log($scope.testingmodel.testing);
});
It says undefined when I console.log my variable in Controller.
You're doing console.log(...) too early. At this time your controller doesn't have any information from the view.
The second problem is that you're binding the view to a variable in controller and not the other way around. Your $scope.testingmodel.testing is undefined and it will obviously the value in the view to undefined.
Solution
Use ng-init to initialize the model and the controller's hook $postLink to get the value after everything has been initialized.
Like this
<div ng-controller="progressController" >
<input type="hidden" ng-model="testingmodel.testing" ng-init="testingmodel.testing = 'ABCD'">
</div>
app.controller('progressController', function($scope) {
var $ctrl = this;
$ctrl.$postLink = function() {
console.log($scope.testingmodel.testing);
};
});
Edit: extra tip
I don't recomment using $scope for storing data since it makes the migration to newer angular more difficult.
Use controller instead.
Something like this:
<div ng-controller="progressController as $ctrl" >
<input type="hidden" ng-model="$ctrl.testingmodel.testing" ng-init="$ctrl.testingmodel.testing = 'ABCD'">
</div>
app.controller('progressController', function() {
var $ctrl = this;
$ctrl.$postLink = function() {
console.log($ctrl.testingmodel.testing);
};
});
You should use the ng-change or $watch
<div ng-controller="progressController" >
<input type="hidden" value="ABCD" ng-model="testingmodel.testing" ng-change="change()">
</div>
app.controller('progressController', function($scope) {
$scope.change = function(){
console.log($scope.testingmodel.testing);
}
});
Or:
app.controller('progressController', function($scope) {
$scope.$watch('testingmodel.testing', function(newValue, olValue){
console.log(newValue);
}
});
If you use ng-change, the function is only called if the user changes the value in UI.
If you use $watch anyway, the function is called.
You can't use value attribute for set or get value of any control, angularJS use ngModel for set or get values.
Here You should try like this way
app.controller('progressController', function($scope) {
//from here you can set value of your input
$scope.setValue = function(){
$scope.testingmodel = {}
$scope.testingmodel.testing = 'ABCD';
}
//From here you can get you value
$scope.getValue = function(){
console.log($scope.testingmodel.testing);
}
});
if you want to bind from html side then you should try like below
<input type="text" ng-model="testingmodel.testing">
<input type="hidden" ng-model="testingmodel.testing">

How to submit selected checkboxes and group name as an objects within an array

I need to pass selected checkbox object data alone into array on form submit.
service returning json data:
[
{
"groupName" : "A",
"groups": [
"Painting",
"coloring"
]
},
{
"groupName" : "B",
"groups": [
"drawing",
"writing"
]
}
]
service expected format when user selected couple of check boxes and submit form:
{
"groups": [
{
"category": "A",
"subCategory": "coloring"
},
{
"category": "B",
"subCategory": "writing"
}
]
}
My controller:
<div ng-controller="groupCtrl">
<form class="form" name="form" role="form" ng-submit="groupData()" autocomplete="off" novalidate>
<div ng-repeat="groups in groupsList">
<p class="category">{{groups.groupName}}</p>
<p ng-repeat="group in groups.groups" >
<input type="checkbox" id="group" name="{{group}}" class="group" value="{{group}}" ng-model="groups"> {{group}}
</p>
</div>
<button type="submit" class="button">Save</button>
</form>
Controller:
angular.module('myApp')
.controller("groupCtrl", function($rootScope, $scope, $state, $http) {
$http.get('group.json').success(function(groupsList) {
$scope.groupsList = groupsList;
});
$scope.groups = {
}
$scope.groupData = function () {
$http.post('<service endpoint>', $scope.groups, {
})
.success(function(){
console.log("success");
})
.error(function(){
console.log("failed.");
});
}
});
I am new to angular. Looking for help on how to construct array object in controller and update array object on user select/un-select check boxes.
First I would put api logic into a service like this:
angular.module('myApp', [])
//service to handle api communication
.service('apiService', ['$http', function($http) {
return {
getGroups: function() {
return $http.get('http://superapi.com');
}
}
}])
Then you need to call the api service from the controller and build up your view model:
api.getGroups().then(function(res) {
//JSON.parse will not be needed if your API returns a JSON body with application/json set as content-type
$scope.groupsList = JSON.parse(res.data.data);
//Build up the view model.
$scope.groupsList.forEach(function(g) {
$scope.selectedGroups.groups.push({
category: g.groupName,
subCategory: []
});
});
},
function(error) {
//handle API errors gracefully here.
});
Then you can use ng-model on the form components to populate it with data
<form class="form" name="form" role="form" ng-submit="groupData()" autocomplete="off" novalidate>
<div ng-repeat="groups in groupsList track by $index">
<p class="category">{{groups.groupName}}</p>
<p ng-repeat="group in groups.groups track by $index">
<input ng-model="selectedGroups.groups[$parent.$index].subCategory" type="checkbox" id="group" name="{{group}}" class="group" ng-true-value="'{{group}}'" ng-init="checked=''" ng-false-value="''"> {{group}}
</p>
</div>
<button type="submit" class="button">Save</button>
</form>
Since your api response model has a double nested hierarchy, we need two nested ng-repeat loops. We can then leverage the fact that ng-repeat creates a child scope, and the parent scope is accessible by using $parent. That way we can use that information to bind to the correct part of the view model with ng-model.
ng-model="selectedGroups.groups[$parent.$index].subCategory"
$parent.$index is the first ng-repeat groups in groupsList
If you want to be able to select more than one subcategory, you can just bind nd-model to each subcategory's index in an array like this:
ng-model="selectedGroups.groups[$parent.$index].subCategory[$index]"
This will use the index of the subCategory to bind to a specific position in an array.
here is a working example of that :
https://jsfiddle.net/zzq16t8u/4/
Here is a working JSFiddle for you:
https://jsfiddle.net/zzq16t8u/3/
Now when you want to map to the request model specified by the system, you will need to handle this in the function handled by the form submit. You could do something clever with ng-true-value, but then you would still need to do a JSON.parse on your data, as it can only work on strings. And I my opinion handling the mapping to the request model is better done once you prepare to make the request.
$scope.groupData = function() {
// map data to the request model specified
var requestModel = {
groups: []
};
$scope.selectedGroups.forEach(function(group) {
group.subCategory.forEach(function(subCat) {
requestModel.groups.push({
category: group.category,
subCategory: subCat
});
});
});
Here is a working JSFiddle of everything, note that I have simplified the view model here, since we no longer bind directly to the request model.
https://jsfiddle.net/zzq16t8u/15/
Please note that the JSON.parse is only necessary here, as I was unable to figure out how to get the JSFiddle echo service to return json as real json, and not a string, and the api service in the example uses this echo service to mock a real http response, so that will need to be replace by a real http call - as I showed in this answer.
Hope it helps.

what is the way to iterate the $resource.get other than doing $promise.then

Once I receive the document say $scope.var = $resource.get(/*one record*/).. I would need to read the received nested object structure (which is now in $scope.var) in order to display each and every field.
I am not able to access the key-value pairs that are present in the $scope.var. So I found a way in doing this using $scope.var.$promise.then(callback) but the format of the $scope.var is changed.
for example:
before parsing:
When I console to see what is in $scope.var, it shows as -
Resource: {$Promise: Promise, $resolved: false}
/*key-value pairs of the object*/
after parsing using $promise.then:
Here the console says
Object {_id: "...", ...}
/*key-value pairs of the object*/
Because of the above format difference I am facing the problem when trying to $update using $resource. Which says $update is not a function.
Is there any other way to access key-value pairs from the $resource.get other than using $promise.then?
Edit: Here is my original code:
Contact.service.js
'use strict';
angular.module('contacts').factory('Contact', ['$resource', function($resource) {
console.log('Iam cliked');
return $resource('/api/contacts/:id', {id: '#_id'}, {
update: {
method: 'PUT'
}
});
}]);
EditController.js
'use strict';
angular.module('contacts').controller('EditController', ['$scope', '$stateParams' ,'$location', '$rootScope', 'Contact',
function($scope, $stateParams, $location, $rootScope, Contact) {
$rootScope.PAGE = 'edit';
$scope.contact = {};
$scope.singleContact = Contact.get({ id: $stateParams.id});
console.log($scope.singleContact);
$scope.singleContact.$promise.then(function(data){
angular.forEach(data, function(value, key) {
if(key !== 'additions')
$scope.contact[key] = value;
else {
angular.forEach(data.additions, function(value, key) {
$scope.contact[key] = value;
});
}
});
console.log($scope.contact);
});
/*parsing my received nested json doument to have all straight key-value pairs and binding this $scope.contact to form-field directive 'record=contact'
I am successful doing this and able to display in web page but when I try to $update any of the field in the webpage it doesnt update. Because it is not being Resource object but it is just the object
please see the images attached[![enter image description here][1]][1]*/
$scope.delete = function(){
$scope.contact.$delete();
$location.url('/contacts');
};
}
]);
Edit.html
<section data-ng-controller='EditController'>
<h2>{{contact.firstName}} {{contact.lastName}}</h2>
<form name='editContact' class='form-horizontal'>
<form-field record='contact' recordType='contactType' field='firstName' required='true'></form-field>
<form-field record='contact' recordType='contactType' field='lastName' required='true'></form-field>
<form-field record='contact' recordType='contactType' field='{{k}}' ng-repeat=' (k, v) in contact | contactDisplayFilter: "firstName" | contactDisplayFilter: "lastName" | contactDisplayFilter: "__v" | contactDisplayFilter: "_id" | contactDisplayFilter: "userName"'></form-field>
<new-field record='contact'></new-field>
<div class='row form-group'>
<div class='col-sm-offset-2'>
<button class='btn btn-danger' ng-click='delete()'>Delete Contact</button>
</div>
</div>
</form>
form-field.html
<div class='row form-group' ng-form='{{field}}' ng-class="{'has-error': {{field}}.$dirty && {{field}}.$invalid}">
<label class='col-sm-2 control-label'>{{field | labelCase}} <span ng-if='required'>*</span></label>
<div class='col-sm-6' ng-switch='required'>
<input ng-switch-when='true' ng-model='record[field]' type='{{recordType[field]}}' class='form-control' required ng-change='update()' ng-blur='blurUpdate()' />
<div class='input-group' ng-switch-default>
<input ng-model='record[field]' type='{{recordType[field]}}' class='form-control' ng-change='update()' ng-blur='blurUpdate()' />
<span class='input-group-btn'>
<button class='btn btn-default' ng-click='remove(field)'>
<span class='glyphicon glyphicon-remove-circle'></span>
</button>
</span>
</div>
</div>
<div class='col-sm-4 has-error' ng-show='{{field}}.$dirty && {{field}}.$invalid' ng-messages='{{field}}.$error'>
<p class='control-label' ng-message='required'>{{field | labelCase}} is required.</p>
<p class='control-label' ng-repeat='(k, v) in types' ng-message='{{k}}'>{{field | labelCase}} {{v[1]}}</p>
</div>
function in form-field.js directive
$scope.blurUpdate = function(){
if($scope.live!== 'false'){
console.log($scope.record);
$scope.record.$update(function(updatedRecord){
$scope.record = updatedRecord;
});
}
};
So in the above $update gives error. saying not a function.
I want the same format in $scope.singleContact and $scope.contact. How can I achieve this?
I don't know if I understand but you want something like this:
Contact.get({id: $stateParams.id}, function (contact){
$scope.var = contact;
});
right?
It will return te promise and you will be able to use the data before loading the templates
Okay, a couple things:
Promises will not return values synchronously
You have to use a then function call. The line
$scope.singleContact = Contact.get({ id: $stateParams.id});
will not put your data into the $scope.singleContact variable immediately because that line returns a Promise and goes onto the next line without waiting for the web request to return. That's how Promises work. If you aren't familiar with them, it might not be a bad idea to read up on them. A consequence of this is that everything inside your controller (or wherever this code is) that needs a working value in $scope.singleContact needs to be inside of a then block or in some way guaranteed to be run after the promise is resolved.
So I think you want (as #EDDIE VALVERDE said):
Contact.get({ id: $stateParams.id}, function(data) {
$scope.singleContact = data;
$scope.contact = data;
});
I'm not sure what the rest of the code is doing but it looks to me like it is trying to do a deep copy of $scope.singleContact. My guess is that you're just doing this cause the promise stuff wasn't working and you can remove it...
Let me know if I missed something.
Ah, now I think I understand. Okay, for 1 I would not add $resource objects to your scope. IMO the scope is intended to be a Model in the MVC paradigm. A $resource is kindof like a model, but it's performing network calls and it's got a bunch of stuff other than data. If you put $resource in your model you remove any clear layer where you can manipulate and transform the data as it comes back from the server. And I'm sure there are other reasons. So I would not add $resource objects like Contact directly to the scope. That being said, I think you want to add a new update function, like you did with delete, only something like this:
$scope.update= function(args){
//make network call and update contact
Contact.$update({/*data from args?*/});
//get the data i just saved
Contact.$get({/* id from args */}, function(result) {
$scope.contact = result;
});
//other stuff?
};
That's my first thought, anyway...

pre-populated form data is undefined in service

jQuery:
$("#inputParentName").val(response.name);
HTML/Angular Form:
<form role="form" ng-submit="addParentService.addParent()">
<div class="form-group">
<label for="inputParentName">Name</label><input class="form-control" id="inputParentName" value="" type="text" ng-model="addParentService.inputParentName" />
</div>
...
<button class="btn btn-default" type="submit">Submit</button>
</form>
The following code when run diplays my name correctly in the input box.
However in my service when I try to see what the value is for inputParentName I get an undefined error. But, when I type something in to the textbox for inputParentName the typed in value displays.
Controller Code:
myapp.controller('AddParentController', function ($scope, addParentService) {
$scope.addParentService = addParentService;
});
Service Code:
myapp.service('addParentService', function () {
var vm = this;
vm.parent = [];
vm.addParent = function () {
alert(vm.inputParentName);
};
});
What can I do differently so I can get the pre-loaded data to register so that my service recognizes the data?
This is just basic code that I'm trying to get working. I realize it isn't pure AngularJS. I am just trying to see how I can get this to work. I will refactor with directives after everything works as I think it should.
If you want the initial value to be "something" when the view displays, you can (technically) use ng-init, though the docs tell us expressly NOT to do this.
The only appropriate use of ngInit is for aliasing special properties
of ngRepeat, as seen in the demo below. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope.
But if you're just trying to test something, ng-init would look like:
<input ng-model="test.val" ng-init="test.val='something'" />
The preferred way though would be to add the value to the controller $scope.
<input ng-model="test.val" />
Then in your controller:
myapp.controller('MyCtrl', function ($scope) {
$scope.test = {
val: 'something'
}
});
#jme11 and This Answer gave me the insight to the following way I figured out how to get this to work:
jQuery code for Facebook logic:
$("#inputParentName").val(response.name);
$("#inputParentEmail").val(response.email);
$("#inputParentBirthday").val(response.birthday);
angular.element(document.getElementById('inputParentName')).scope().$apply();
angular.element($('#inputParentName')).scope().setName(response.name);
angular.element($('#inputParentEmail')).scope().setEmail(response.email);
angular.element($('#inputParentBirthday')).scope().setBirthday(response.birthday);
My Controller code:
$scope.setName = function (val) {
addParentService.inputParentName = val;
}
$scope.setEmail = function (val) {
addParentService.inputParentEmail = val;
}
$scope.setBirthday = function (val) {
addParentService.inputParentBirthday = val;
}
Service Code:
vm.addParent = function () {
alert(vm.inputParentName);
alert(vm.inputParentBirthday);
alert(vm.inputParentEmail);
alert(vm.inputParentCellPhone);
alert(vm.inputParentCarrier);
};
Now when I'm adding my Parent the values pre-populated from Facebook are usable in my service code.
Again - Thanks to jme11 for helping me solve this.

AngularJS - Using a Service as a Model, ng-repeat not updating

I'm creating an ajax search page which will consist of a search input box, series of filter drop-downs and then a UL where the results are displayed.
As the filters part of the search will be in a separate place on the page, I thought it would be a good idea to create a Service which deals with coordinating the inputs and the ajax requests to a search server-side. This can then be called by a couple of separate Controllers (one for searchbox and results, and one for filters).
The main thing I'm struggling with is getting results to refresh when the ajax is called. If I put the ajax directly in the SearchCtrl Controller, it works fine, but when I move the ajax out to a Service it stops updating the results when the find method on the Service is called.
I'm sure it's something simple I've missed, but I can't seem to see it.
Markup:
<div ng-app="jobs">
<div data-ng-controller="SearchCtrl">
<div class="search">
<h2>Search</h2>
<div class="box"><input type="text" id="search" maxlength="75" data-ng-model="search_term" data-ng-change="doSearch()" placeholder="Type to start your search..." /></div>
</div>
<div class="search-summary">
<p><span class="field">You searched for:</span> {{ search_term }}</p>
</div>
<div class="results">
<ul>
<li data-ng-repeat="item in searchService.results">
<h3>{{ item.title }}</h3>
</li>
</ul>
</div>
</div>
</div>
AngularJS:
var app = angular.module('jobs', []);
app.factory('searchService', function($http) {
var results = [];
function find(term) {
$http.get('/json/search').success(function(data) {
results = data.results;
});
}
//public API
return {
results: results,
find: find
};
});
app.controller("SearchCtrl", ['$scope', '$http', 'searchService', function($scope, $http, searchService) {
$scope.search_term = '';
$scope.searchService = searchService;
$scope.doSearch = function(){
$scope.searchService.find($scope.search_term);
};
$scope.searchService.find();
}]);
Here is a rough JSFiddle, I've commented out the ajax and I'm just updating the results variable manually as an example. For brevity I've not included the filter drop-downs.
http://jsfiddle.net/XTQSu/1/
I'm very new to AngularJS, so if I'm going about it in totally the wrong way, please tell me so :)
In your HTML, you need to reference a property defined on your controller's $scope. One way to do that is to bind $scope.searchService.results to searchService.results once in your controller:
$scope.searchService.results = searchService.results;
Now this line will work:
<li data-ng-repeat="item in searchService.results">
In your service, use angular.copy() rather than assigning a new array reference to results, otherwise your controller's $scope will lose its data-binding:
var new_results = [{ 'title': 'title 3' },
{ 'title': 'title 4' }];
angular.copy(new_results, results);
Fiddle. In the fiddle, I commented out the initial call to find(), so you can see an update happen when you type something into the search box.
The problem is that you're never updating your results within your scope. There are many approaches to do this, but based on your current code, you could first modify your find function to return the results:
function find(term) {
$http.get('/json/search').success(function(data) {
var results = data.results;
});
//also notice that you're not using the variable 'term'
//to perform a query in your webservice
return results;
}
You're using a module pattern in your 'public API' so your searchService returns the find function and an array of results, but you'd want to make the function find to be the responsible for actually returning the results.
Setting that aside, whenever you call doSearch() in your scope, you'd want to update the current results for those returned by your searchService
$scope.doSearch = function(){
$scope.searchService.results = searchService.find($scope.search_term);
};
I updated your jsfiddle with my ideas, is not functional but i added some commments and logs to help you debug this issue. http://jsfiddle.net/XTQSu/3/

Resources