angularjs on form edit select option does not load value On Second Time - angularjs

My form has got 2 select boxes. The second select options box is loaded when an option is selected in first box. The form is used for both adding and editing. I show the list below the form with edit button against each row. When I click on edit button it loads the values in form to edit. The thing is when I click on a row for first time it loads the values in form perfectly. If I click on another row button to edit, the SECOND select option alone does not show the selected value. But the box is loaded with values.
Code in controller:
$scope.editShift = function(row) {
for (var i = 0; i < $scope.availableShift.length; i++) {
if ($scope.availableShift[i].shiftId === row.entity.shiftId) {
$scope.shift = angular.copy($scope.availableShift[i]);
break;
};
};
$scope.selectLineNames();
};
$scope.selectLineNames = function(){
$scope.availableListOfLineNames = [];
$scope.availableListOfLineNamesTemp = LineDetails.ld_get_ln_resource.query
({plant: $scope.shift.plant}, function(response) {
angular.forEach(response, function(item) {
if (item) {
$scope.availableListOfLineNames.push(item);
}
});
});
};
Second Select Option in Form
<select name="lineName" ng-model="shift.lineName"
ng-options="x for x in availableListOfLineNames" id="selectoption2" required>
<option value="">Select One</option>
</select>
EDIT:
Calling $scope.selectLineNames(); inside $scope.editShift somehow makes the lineName value undefined after copy, on second time. Inside selectLineNames() too the value is available until it reaches the forEach() .Only after the loop starts It becomes undefined.
So used a duplicate method like $scope.selectLineNamesTemp(); and passed values from $scope.editShift and assigned them in the duplicate method like below.
$scope.selectLineNamesTemp = function(){
$scope.availableListOfLineNames = [];
$scope.availableListOfLineNamesTemp = LineDetails.ld_get_ln_resource.query(
{plant: plantTemp}, function(response) {
angular.forEach(response, function(item) {
if (item) {
$scope.availableListOfLineNames.push(item);
$scope.shift.lineName = lnTemp; // assigned from editShift
}
});
}) ;
};
Can someone point out why the value become undefined? Also is there a way to use the same old method without using the duplicate method. Hope someone help.

Related

Angular ng-options ONLY works when ng-repeat operates on same model

I am working on an ASP.Net MVC page that uses a dropdown which currently uses the ng-repeat tag. I'm working to solve the problem where the dropdown does not correctly select the current model value when the page loads so I switched the dropdown to use ng-options.
My new dropdown looks like this:
<select id="one" ng-model="data.CommandProvider"
ng-options="item.ident as item.ProviderName for item in providers">
</select>
When the page loads my new select displays as a large empty rectangle. It's approximately the width and height to match the three items it should contain but it's not a dropdown. No options and no dropdown button.
However, when I follow the new dropdown with the old dropdown like so:
<select id="one" ng-model="data.CommandProvider"
ng-options="item.ident as item.ProviderName for item in providers">
</select>
<select id="two" ng-model="data.CommandProvider">
<option ng-repeat="opt in providers track by opt.ident"
value="{{opt.ident}}">
{{opt.ProviderName}}
</option>
</select>
BOTH dropdowns load their options correctly but NEITHER dropdown correctly displays the current value of the model.
If the page only contains the old dropdown based on ng-repeat that dropdown displays correctly.
I don't understand what could cause such behavior in ng-options and what would cause the dropdowns to never correctly represent the model on page load?
ADDED: So the previous author had mismatched HTML tags and that was causing the error with the new dropdown - why it didn't break the original I don't know. That being said the new dropdown STILL does not display the value of the model when the page is loaded.
So after working this problem for too long this is the solution that worked for me:
There are three http requests in play: one for each select input and one for the model data and whenever the model data returned before the select data one or both of the select would be out of sync with the model. My solution was to synchronize the data requests.
The select inputs:
<select ng-model="data.Connection">
<option ng-repeat="opt in connections track by opt.ident" value="{{opt.ident}}">{{opt.ConnectionName}}</option>
</select>
<select id="two" ng-model="data.CommandProvider">
<option ng-repeat="opt in providers track by opt.ident" value="{{opt.ident}}">{{opt.ProviderName}}</option>
</select>
The javascript:
// connection and provider data variables
$scope.providers;
$scope.connections;
// function to retrieve connection dropdown data
$scope.getConnections = function () {
$scope.getApiData('GetConnections',
{}, function (data) {
$scope.connections = data;
});
}
// function to retrieve the provider dropdown data
$scope.getProviders = function () {
$scope.getApiData('GetProviders',
{}, function (data) {
$scope.providers = data;
});
}
// retrieve the primary page data
$scope.getCommandData = function () {
$scope.getApiCommandDataV1('GetCommandById',
{Id: #ViewBag.ID},
function (data) {
$scope.data = data;
});
}
// retrieves data from the core api
$scope.getApiData = function (alias, params, successFn, errorFn = null) {
var qdata = { SqlAlias: alias, SqlParameters: params };
if (errorFn == null) {
$http.post('/api/request', qdata).success(successFn);
} else {
$http.post('/api/request', qdata).success(successFn).error(errorFn);
}
}
// function to request the data for the page
$scope.init = function () {
$scope.getConnections();
}
// set a watch on the connections variable to fire when the data
// returns from the server - this requests the Providers information.
$scope.$watch('connections', function (newValue, oldValue, scope) {
if (newValue == undefined || newValue == null)
return;
$scope.getProviders();
}, true);
// set a watch function on the providers variable to fire when the data
// returns from the server - this requests the primary data for the Command.
$scope.$watch('providers', function (newValue, oldValue, scope) {
if (newValue == undefined || newValue == null)
return;
$scope.getCommandData();
}, true);
// initialize the page logic and data
$scope.init();
As you can see my use of $scope.$watch forces the data requests to be synchronous rather than asynchronous and using this method insures the two select inputs are correct every time the web page loads.
Feel free to comment on my coding here as there may be better ways to address this problem - just keep in mind that I have only been working with JavaScript and Angular for about a month.

Angular 6 dynamic checkboxes keyvalue return array of IDs

I have a list of dynamic checkboxes using keyvalue pipe that returns objects rather than just array of selected IDs.
can anyone help pls, I need the form to submit just an array of selected user IDs.
https://stackblitz.com/edit/angular-ciaxgj
EDIT
here's the log of a similar form with a multi-select (countries):
console log
I need users (checkboxes) to return an array like countries (multi-select) as in the log above.
Change your OnSubmit function to
onSubmit() {
console.log(this.userForm.value);
var usersObj = this.userForm.value.users;
var selectedUserIds = [];
for (var userId in usersObj) {
if (usersObj.hasOwnProperty(userId)) {
if(usersObj[userId])//If selected
{
selectedUserIds.push(userId);
}
}
}
console.log(selectedUserIds);
}
Here is updated code
https://stackblitz.com/edit/angular-9fd17t
If you want to submit as a Form then add a hidden field to form
<input type="hidden" name="selectedUserIds" value=""/>
set value selectedUserIds from onSubmit and submit the form code.
So, it happens that I have been overthinking the solution in thinking that like the multiselect, checkboxes would/could return a ready array of selected items.
The rather simple solution was from reading another of Eliseo's posts combined with Jaba Prince's answer:
onSubmit() {
var usersObj = this.userForm.value.users;
var selectedUserIds = [];
for (var userId in usersObj) {
if (usersObj.hasOwnProperty(userId)) {
if(usersObj[userId])//If selected
{
selectedUserIds.push(userId);
}
}
}
let data = {
users: selectedUserIds
}
console.log(data);
// post data in service call
}
Many thanks to you two!

Show data in inputbox from 1 object and save it in using another object

I'm clicking a table row to edit the fields in a modal. The modal must have 2 functionalities (Add or Edit) depending on the GET request data like below.
$scope.editInterview = function(id) {
$http.get('/api/getinterview/' + id).then(function(response) {
editedObject = response.data.item
}
HTML
<label ng-if="editedObject.email">{{editedObject.email}}</label>
<label ng-if="!editedObject.email">Email</label>
<input ng-model="newObject.email" />
I am able to display the object in the labels, but that's not much help, because the data needs to be shown in the input boxes to be Edited and Saved.
How can i show the data from editedObject.email in the input, so i can save it using newObject.email?
I tried ng-init="editedObject.email", but it doesn't work. Is there some other ng-something that does this or i should be doing it in another way?
Update:
Edit and Update Methods, both are in the mainController.
$scope.editInterview = function(id) {
$http.get('/api/getinterview/' + id).then(function(response) {
editedObject = response.data.item
})
}
//Controller for the Modal
function DialogController($scope, $mdDialog, editedObject) {
$scope.editedObject = editedObject
$scope.submitObject = function(newObject) {
$http.post('/api/interview', newObject)
}
}
You have to make a deep copy from editObject.email to newObject.email. This could be done this way in controller after editOject.email has a value assigned.
$scope.newObject.email = angular.copy($scope.editObject.email);

Angular - Cant update textarea after user input

Im having an issue with Anuglar. ng-bind doesnt update textarea once its value has been altered by user input.
Note: The reason im using ng-bind instead of ng-model is that I need ng-bind-html as the input is in html sanatized using ngSanitize.
Demo:
http://jsfiddle.net/Lvc0u55v/11682/
Here is how to see what im talking about: Select anyvalue from dropdown and textarea is updated. But select New Value and put some text in the textarea. Then change the select box to any value and textarea never changes again!
Code:
[SCRIPT]
$scope.msg_templates = []; //array of {id, title, msg} filled with required stuff
$scope.msg_templates[0] = {id:'0', title:'{New Value}', msg:''}; //first item is to enter new value
$scope.msg_templates[1] = {id:'1', title:'TTT', msg:'TTT'};
$scope.msg_templates[2] = {id:'2', title:'XXX', msg:'XXX'};
.
.
//set default value
$scope.msg_sel = '';
$scope.msg_field = '';
//change msg function
$scope.change_msg_sel = function()
{
if($scope.msg_sel==null){
$scope.msg_field = '';
return false;
}
$scope.msg_field = $scope.msg_sel.msg;
};
[HTML]
<select ng-model="msg_sel" ng-change="change_msg_sel()" ng-options="v.title for v in msg_templates">
<option value="">Please Select -</option>
</select>
<textarea ng-bind-html="msg_field"></textarea>
Solve the problem myself, just manually override the field's value inside $scope.change_msg_sel using the good-ol-always-working-vintage-JS!
document.getElementById('msg_field').value=$scope.msg_field;
(so much for the amazing angular!!)

Issue with Angularjs Dropdown and a custom filter

I'm having an issue using a dropdown that is populated with ng-repeat option values or even when using ng-options.
Basically I'm pulling a list of subsidiaries from the database. I then have a dropdown to choose a company, which in turn should populate the subsidiary dropdown with subsidiaries of the chosen company. Since many of the subsidiaries are of the same company, if I try and pull the the company name in ng-repeat, I get the same company several times. So I have created a custom filter that filters out the companyName and companyID of each company listed only once.
Everything works in the theory that when I change the value of the company dropdown, the correct subsidiaries are listed. However the value shown in the company box is stuck on the first option listed and will not change. If I remove the custom filter and allow it to list all the repeat names, the box displays correctly.
My first thought is to make a separate HTTP call that would just get companies from my companies table, but I would think I want to limit HTTP calls to as few as possible. Plus it would seem that I should be able to accomplish this.
What concept am I not grasping that prevents this from displaying correctly when I use my filter and what should I do to fix this?
thanks
HTML:
<div class="col-sm-5">
<select ng-model ="parentCompany" name="company">
<option ng-repeat="company in companies | uniqueCompanies:'companyName'" value="{{company.id}}" >{{company.name}}</option>
</select>
</div>
<div class="col-sm-5">
<select name="subsidiary">
<option ng-repeat="subsidary in companies" value="{{subsidary.subID}}" ng-hide="$parent.parentCompany !== subsidary.companyID">{{subsidary.subName}}</option>
</select>
</div>
Controller:
getCompanies();
function getCompanies(){
$http.get("get.php?table=getcompanies").success(function(data) {
$scope.companies = data;
});
}
Filter:
.filter("uniqueCompanies", function() {
return function(data, propertyName) {
if (angular.isArray(data) && angular.isString(propertyName)) {
var results = [];
var keys = {};
for (var i = 0; i < data.length; i++) {
var val = data[i][propertyName];
var val2 = data[i]['companyID'];
if (angular.isUndefined(keys[val])) {
keys[val] = true;
results.push({'name':val, 'id':val2});
}
}
return results;
} else {
return data;
}
};
});
Sample Data :
[{"subID":null,"subName":null,"companyID":"1","companyName":"DWG"},
{"subID":null,"subName":null,"companyID":"2","companyName":"Vista"},
{"subID":"1008","subName":"Data Services","companyID":"3","companyName":"Medcare"},
{"subID":"1009","subName":"Companion","companyID":"3","companyName":"Medcare"},
{"subID":"1010","subName":"GBA","companyID":"3","companyName":"Medcare"},
{"subID":"1011","subName":"PGBA","companyID":"3","companyName":"Medcare"},
{"subID":"1013","subName":"Health Plan","companyID":"3","companyName":"Medcare"},
{"subID":"1014","subName":"PAISC","companyID":"3","companyName":"Medcare"},
{"subID":"1015","subName":"CGS","companyID":"3","companyName":"Medcare"}]
You are creating new objects in your filter with different properties so they will be different every time. You can you track by as mentioned by others. Since filters are executed every digest cycle you may want to set up a $watch and only create a new list of unique companies when your companies change. I actually get the 10 $digest() iterations reached error without doing this.
$scope.$watchCollection('companies', function(newValue) {
$scope.filteredCompanies = $filter('uniqueCompanies')($scope.companies,
'companyName');
});
You could also set a watch on parentCompany and create the list of subsidiaries only when it changes, as well as clear out the value you have for subsidiaryCompany:
$scope.$watch('parentCompany', function(newValue) {
$scope.subsidiaries = [];
for (var i = 0; i < $scope.companies.length; i++) {
var c = $scope.companies[i];
if (c.companyID === newValue) {
$scope.subsidiaries.push(c);
}
}
$scope.subsidiaryCompany = undefined;
});
I may not be fully understanding you're issue here, but it looks like you could filter the data when you get it. Such as ...
function getCompanies(){
$http.get("get.php?table=getcompanies").success(function(data) {
$scope.companies = data.reduce(function (prev, cur) {
// some code for skipping duplicates goes here
}, []);
});
}
Array.reduce may not be the best way to get a new array without duplicates, but that's the general idea, anyway.

Resources