Ruby: Setting select model value not working via AngularJS binding - angularjs

I have a situation where I'm setting the object on a select element externally (an edit modal dialog). When I set the scope variable value the select control just loses it's value instead of selecting the equivalent item (like the object with the same id).
I wonder if it's a different ng-option value I need, there seem to many ways of populating it which I don't understand from the documentation. I'm using the basic type:
<select ng-model="color" ng-options="c.name for c in colors"></select>
I've created a similar situation here. I need clicking the button to set the proper value in the select drop down.

AngularJS compares for reference rather than for equality. When you make that, AngularJS doesn't find any equivalent object in $scope.colors, and then sets the list to an empty value :
$scope.setColor = function() {
$scope.color = {id:12,name:'white',shade:'light'};
};
Instead, you can simply make that, like in this fiddle :
$scope.setColor = function() {
$scope.color = $scope.colors[1];
};

Related

Why is AngularJS form not updating validity until clicking into another field and changing value?

Good morning! I am still fairly new to AngularJS and did not find an answer that I could fully comprehend.
There is a form structured like this:
Name
Email
Color [Option Red, Option Blue]
REQUIRED Field that only appears if Option Blue is chosen for 'Color'
When selecting Option Blue, the second set of fields appear. However, when unselecting 'Option Blue', the required fields are again hidden, but the form still 'sees' that the field is empty until I click into another field (like 'Name') and type another value.
A condition checks the boolean $form.$valid every time the event listeners (?) fire off, which is onItemSelect and onItemDeselect and onSelectionChange.
From what I understand of Angular, I... think that another digest(?) cycle has not started (?) until I click and type into another field? Sorry for all of the question marks - my knowledge is a little flakey and I can't quite paste the source code for reasons.
Is my understanding correct? And if so, are there any recommended ways to resolve this issue?
Edit: I have added some example code below with dummy variable and object names, plus some pseudo code to summarize larger sections of code.
<script>
var app = angular.module('myApp',[some stuff]);
app.controller('myContropper', function ($scope, $rootScope, $timeout) {
[some variables for scope]
$scope.color = {};
$scope.optionB = {
hue: null;
saturation: null;
};
[some other variables that only have values of OptionB is chosen and subsequent fields are populated]
$scope.colorChange = function() {
if color is changed, clear all associated arrays and values by setting it to either '[];' or 'null;'
};
$scope.checkForm = function() {
if OptionB is not chosen, clear all options related to OptionB
use $scope.form.$valid boolean value and do something with it
};
function onItemSelect(item) {
if OptionB is selected, set the flag for that option to true
$scope.checkForm();
}
function onItemDeselect(item) {
if OptionB is not selected, set the flag for that option to false
$scope.checkForm();
}
}
</script>
I had set up some debugging alerts. I notice that when I unselect OptionBlue, the alert will have the following line:
requiredFieldifOptionBisChosen: object Object
However, upon clicking on another field and typing any value, the form will refresh (?) the validity, and it'll say:
requiredFieldifOptionBisChosen: null

What is the right way to select by value in a dropdown?

I'm having a bit of an issue with Angular and selecting an item from a dropdown and making Angular update the model.
I've been searching through StackOverflow, but only with partial luck.
The problem is that when i'm manually setting a value on a model, my dropdown updates correct, but the model itself does not update;
$scope.setSelected = function(){
$scope.selected.id = 15;
//$scope.$apply();// $apply already in progress
}
From various answers on StackOverflow, I've found out that Angular does not know about this update and the suggested answer is to run either a $scope.$apply() or a $scope.$digest(), but both throw a $apply already in progress exception. I have a feeling that this is not the correct way for me to do it, since it doesn't make sense, that you have to trigger an event to select by value.
What is the correct way to select an item in a dropdown by a given value?
Full code example can be found at https://jsfiddle.net/c2x3jvut/
When clicking the "Select dinnerman" button, the dropdown updates correct, but the shown model and when clicking "Log selected" it only shows the selected model, but with an updated id.
You can use $filter to get whole object instead of each property.
$scope.setSelected = function(){
$scope.selected = $filter('filter')($scope.persons,15,'id')[0];
//$scope.selected.id = 15;
//$scope.$apply();// $apply already in progress
}
Here is the modified version
ng's select binds its value based on its ngModel, which is being manipulated incorrectly in the example. The correct method is to change the reference of the ngModel rather than the select's id:
$scope.setSelected = function () {
$scope.selected = $scope.persons[1]; // yes
// $scope.selected.id = 15; // no
};
Fiddle might have been updated to show the effects.
Not sure I understand your question, but still...
You may include in each entry of the dropdown:
...ng-click="setSelected(<value>)"...
and, of course, add a parameter to the function.
By the way, are you aware that in the fiddle example you are selecting ID 15 in function setSelected?
You are just updating the current model with an ID. But when you see carefully the name and age remains same. Further you need to update the whole object as below:
$scope.setSelected = function(){
$scope.selected.id = 15;
$scope.selected.name = "Dinner-man";
$scope.selected.age = 20;
}
Or anyother way to directly updates the object.

AngularJS typeahead select on blur

I'm using typeahead through in my AngularJS project and I would like to have it select the entry if I type the full value and click out of the field.
I've put together an example of what I mean
http://plnkr.co/edit/NI4DZSXofZWdQvz0Y0z0?p=preview
<input class='typeahead' type="text" sf-typeahead options="exampleOptions" datasets="numbersDataset" ng-model="selectedNumber">
If I type in 'two' and click on 'two' from the drop down then I get the full object {id: 2, name: 'two'}. This is good, if however I type 'two' and click to the next field without selecting is there a way to accept the top of the list on loss of focus on a text field?
I'm not sure if I'd want to have that sort of functionality in my app. The user hasn't actually selected anything. So selecting something for them would introduce frustrations.
But I do understand that often odd requirements are needed. In this case, I'd attack it using ngBlur. Assign a function to be called on blur. You can grab the contents of ng-model and then loop through your data (assuming static & not being sent via server) to find a match.
You can most likely just look at the source code of your typeahead directive and strip out the part does the comparison and then choose the first item in the array.
Unfortunately the underlying component does not emit any events for this condition. This will make the solution more complex. However when the value is being entered and the Typehead magic has happened you can supplement those events and catch them to update your ngModel.
I have created a plnkr based on your plnkr and although have not cleaned up but it is a working plnkr doing by far what you need.
The gist of this is following code however you can put this code wherever best suited
The explanation below:
//Crux - this gets you the Typeahead object
var typeahead = element.data('ttTypeahead');
//This gets you the first
var datum = typeahead.dropdown.getDatumForTopSuggestion();
if (datum){
//you can do lot of things here however
//..I tried to - fill in the functionality best suited to be provided by Typeahead
//for your use case. In future if Typeahead gets this
//..feature you could remove this code
typeahead.eventBus.trigger("hasselections", datum.raw, datum.datasetName);
}
In the above code you can also save the datum somewhere in the scope for doing whatever you like with it later. This is essentially your object {num: 'Six'} Then you may also use ngBlur to set it somewhere (however the plnkr I created doe snot need these gimmicks.)
Then further down - ngModel's value is set as below
element.bind('typeahead:hasselections', function(object, suggestion, dataset) {
$timeout(function(){
ngModel.$setViewValue(suggestion);
}, 1);
//scope.$emit('typeahead:hasselections', suggestion, dataset);
});
I'm with EnigmaRM in that ngBlur seems to be the way to do what you want. However, I agree with the others that this could be somewhat strange for the end users. My implementation is below (and in plnkr). Note that I trigger on ngBlur, but only apply the model if and only if there is only one match from Bloodhound and the match is exact. I think this is probably the best of both worlds, and hope it should give you enough to go on.
$scope.validateValue = function() {
typedValue = $scope.selectedNumber;
if(typedValue.num !== undefined && typedValue.num !== null)
{
return;
}
numbers.get(typedValue, function(suggestions) {
if(suggestions.length == 1 && suggestions[0].num === typedValue) {
$scope.selectedNumber = suggestions[0];
}
});
};

ng-show binding to string expression in angular

Is there a way to bind a ng-show expression to a string which contains the bind expression itself?
For Instance:
field={};
field.a=true;
field.showExpression='a == true';
<input ng-show="field.showExpression">
I´ve tried <input ng-show="{{field.showExpression}}"> as well, but none of them seems to work.
I want the bind to stay active, so that when the field.a object changes from true to false the expression gets evaluated again, hiding the input.
Just as background, i´m trying to implement dependant dropdowns, so my showExpressions should be of form field.showExpression='maindropdownValue!=null', and whenever the maindropdown which will be bound to the maindropdownValue gets selected the second one gets displayed.
I´m using angular 1.0.8
showExpression is evalued as a String not as a JS code. You have to use a function instead.
$scope.isTrue = function() {
return $scope.field.a; // or a more complex check
}
//
ng-show="isTrue()"
if you only have to check for a boolean, you can check var directly in the view:
ng-show="field.a"
If you really want to use eval, this is what you want:
ng-show="$parent.$eval(field.showExpression)"
link: http://docs.angularjs.org/guide/expression

knockout checkbox binding selected value

I am learning knockout and was trying to build a page that will build a list of selectable users.
JSFiddle: http://jsfiddle.net/Just/XtzJk/3/ (I am unable to get the data assignment right).
The data assignment is working in my page as I make a call to Controller, like below and it binds to the controls as expected
$.getJSON("/Wizard/GetUsers",function(allData){
var mappedUsers = $.map(allData.AllUsers, function(item){return new User(item)});
self.AllUsers(mappedUsers);
if(allData.SelectedUsers != null){
var mappedSelectedUsers = $.map(allData.SelectedUsers, function(item){return new User(item)});
self.SelectedUsers(mappedSelectedUsers);}
});
Problems:
a.) What's wrong with the JSFiddle I wrote? Got it working.
b.) In my code I am able to get the function for selected checkbox invoked but I am unable to get the value stored in the "User" parameter that I receive in the function. In Chrome JS console I can see the user object has the right value stored, I just am unable to retrieve it. Got this by doing ko.toJS().
Thanks.
EDIT:
Ok, I got my JSFiddle working, I had to select Knockout.js in the framework. The updated fiddle: http://jsfiddle.net/Just/XtzJk/5/
Also, for getting the selected checkboxe's value I did
ko.toJS(user).userName
But I think I'll take the approach of selecting values from a list and then on click move them to another "Selected" list and remove the values from the previous ones. Got this idea from this post: KnockoutJS: How to add one observableArray to another?
OK, I think I've got the solution you need...
I started by setting up an observable array of selectedUserNames, and I applied this to the <li> elements like this:
<input type="checkbox"
name="checkedUser"
data-bind="value: userName, checked:$root.selectedUserNames" />
[Note: it's important to declare the value before declaring the checked binding, which threw me for a bit… ya learn something new every day!]
Why bind an array of userName values to the checked binding? Well, when an array is passed to the checked binding, KO will compare the value of each checkbox to the values in the checked array and check any checkbox where its value is in that array. (Probably explained better in the KO documentation)
Then, while I left the observableArray for SelectedUsers, I set up a manual subscription to populate it, like so:
self.selectedUserNames.subscribe(function(newValue) {
var newSelectedUserNames = newValue;
var newSelectedUsers = [];
ko.utils.arrayForEach(newSelectedUserNames, function(userName) {
var selectedUser = ko.utils.arrayFirst(self.AllUsers(), function(user) {
return (user.userName() === userName);
});
newSelectedUsers.push(selectedUser);
});
self.SelectedUsers(newSelectedUsers);
});
[I had originally tried to set up a dependent observable (ko.computed) for selectedUserNames with functions for both read and write, but the checkbox wasn't having it.]
This subscription function examines the new selectedUserNames array, looks up the user from AllUsers whose userName matches a value in that selectedUserNames array, and pushes matching User objects to the SelectedUsers array… well, actually it pushes each matching User to a temp array and then that temp array is assigned to SelectedUsers, but the goal is met. The SelectedUsers array will now always contain what we want it to contain.
Oh, I almost forgot… here's the fiddle I created, so you've got the full solution: http://jsfiddle.net/jimmym715/G2hxP/
Hope this helps, but let me know if you have any questions

Resources