I have a complex object being bound to a drop down. See the jsfiddle.
Is this the correct way to bind to a complex object for a drop down menu.
The drop down must bind to an initial value(currently working)
Changing the selected index in the drop down needs to update the knockout object. This is sort of working. The object is updated when save is called so the drop down's object value is being passed to the Format object. -- However.. This value is not updated in the UI.
I am not sure if it is the mapping that needs some work to make format into an observable. The value of SelectedFormat never seems to change from the first load.
any help on getting this to update the ui and text output of the object would be appreciated.
Edited: question to give requirements more clarity
code on js fiddle
1) No its easier than that, just use optionsText point out the member like
optionsText: 'Name'
Object reference is already implicit the optionsValue so you can skip that, if you want to explicit set it anway you can do the same there and just point to the member optionsValue: $data but its not needed. In other words, this would do http://jsfiddle.net/QrvJN/7/
2) The value and options binding are matched on object reference so if initial value and the list of options do not share references you need to match them yourself. You are doing it a little strange though, I did a binding for this that takes care of the problem http://jsfiddle.net/ewSU2/
3) THis is not needed if you check my solution 2)
Related
this is not exactly a programming problem but more of a quest for more clarity on DOM manipulation using JQuery or Javascript. Any help will be appreciated.
i have a couple lines below i just need to understand how they are treated in the browser. the idea is just understand a little more
I know this line creates a reference to an Object, but what Object??
var child = $("select[Title='Country'], select[Title='Country Required Field'],select[Title='Country possible values']");
Does line empty the value property in this object? Why do you need this?
$(child).empty();
var options = <option value="volvo">Volvo</option>;
i know this is supposed to append the variable to the child object but what happens if there is already other values in the child object. What if you need to replace the values already in the child object with new the values in the option variable.
$(child).append(options);
the question is just for more clarity any answers will be welcomed thanks
Not sure if that what you mean but if you want to override the current options in the select fields by the options you've in the options variable without using empty you could use .html() :
$(child).html(options);
Hope this helps.
$(child).empty(); this line doesn't empty the value property of the object but it clears all the inner HTML of this object. This means it clears all the option tags inside the object. If you need to modify an existing value of an option with some other value you can do this:
`$("option[value='oldvalue']", child).val('new value');
The above code selects the option whose value you need to modify and then set its value to new value.
I know there is a small problem staring right at my face by can't figure it out.
Consider
{{view Ember.Select
content=baseList
optionLabelPath="content.desc"
optionValuePath="content.id"
selectionBinding="selectedItem"
}}
baseList = [{"id":"item1","desc":"item number is 1"},{"id":"item2","desc":"item number is 2"}]
Below does not work
selectedItem = {"id":"item1","desc":"item number is 1"};
The select drop down does not show any selected item
Below works
selectedItem = baseList.filterBy('id','item1')[0];
Now the select drop down shows the selected item.
What is the problem? I even checked if the order of the properties(id and desc) are proper. Is it because two objects cant be compared directly unless certain algorithm is employed or rather use JSON.stringify?
The problem is that when you specify
selectedItem = {"id":"item1","desc":"item number is 1"};
that hash is a different object from the one in baseList, even though it's lexically identical. So Ember cannot find it in baseList (it's doing a === compare, not a deep compare). When you do the filterBy, on the other hand, it returns the actual object from within baseList, which Ember.Select can then find in baseList.
You might want to try using valueBinding instead; then you can just specify "item1".
BTW, the order of properties makes no difference here or anywhere else in JS.
I have a complex object which contains some nested arrays of objects.
Inside one of those inner objects is a value which is the id for an item in another list.
The list is just a look-up of Codes & Descriptions and looks like the following:
[
{ "id": 0, "value": "Basic"},
{ "id": 1, "value": "End of Month (EOM)"},
{ "id": 2, "value": "Fixed Date"},
{ "id": 3, "value": "Mixed"},
{ "id": 4, "value": "Extra"}
]
However, I only carry the value in the nested object.
The Select Option list (drop list) will display all of the values in the previous list so the user can make his/her selection.
Binding Via ng-model
I then bind the value returned from the Select/Option directly to the nested object.
That way, when the user makes a selection my object should be updated so I can just save (post) the entire object back to the server.
Initialization Is The Problem
The selection does work fine and I can see that the values are all updated properly in my nested object when a user selects. However, I couldn't get the UI (select/option) to be initialized to the proper value when I retrieved the (nested) object from the server.
Input Type Text Was Binding Properly
My next step was to add an text box to the form, bind it to the same ng-model and see if it got initialized. It did.
This is a large project I was working on so I created a plnkr.co and broke the problem down. You can see my plnkr in action at: http://plnkr.co/edit/vyySAmr6OhCbzNnXiq4a?p=preview
My plunker looks like this:
Not Initialized
I've recreated the exact object from my project in Sample 1 and as you can see the drop list is not selected properly upon initialization since the value(id) is actually 3, but the drop list doesn't show a selected value.
Keep In Mind: They Are Bound And Selecting One Does Update Values
If you try the plunker you will see that the values are bound to the select/option list because even in the samples which do not initialize properly, when you select an item the other bound items are instantly updated.
Got It Working : Hack!
I worked with it a long time and kept created fake objects to see which ones work and which don't.
It only works, once I changed the value object to one that looks like the following:
$scope.x = {};
$scope.x.y = 3;
Now, I can bind x.y (ng-model="x.y") to select/option and it initializes the select/option list as you would expect. See Sample 2 in the plunker and you will see that "mixed" (id value 3) is chosen as expected.
Additional One Works
I also learned that the following will work:
$scope.lastObj = {};
$scope.lastObj.inner = [];
$scope.lastObj.inner.push(3);
More Nesting
In that case I can bind lastObj.inner to the select/option list and again you can see in Example 3 that it still works. That is an object which contains an array which contains the value.
Minimal Nesting That Fails
However, Sample 4 in the plunker displays the final amount of nesting which does not work with the AngularJS binding.
$scope.thing = {};
$scope.thing.list=[];
$scope.thing.list.push({"item":"3"});
This is an object which contains an array which contains an object with a value. It fails to bind properly on the select/option but not the text box.
Can Anyone Explain That, Or Is It A Bug, Or Both?
Can anyone explain why the select/option fails to bind / initialize properly in this case?
A Final Note
Also, be strong and do not try to explain why you think the nesting of my objects should be different. That's not under discussion here, unless you can tell me that JavaScript itself does not support that behavior.
However, if you can explain that Angular cannot handle this deep of nesting and why, then that is a perfectly valid answer.
Thanks for any input you have.
You are messed up with primitive types. It means you should insert
$scope.vm.currentDocument.fieldSets[0].fields.push({"value":3});
instead of
$scope.vm.currentDocument.fieldSets[0].fields.push({"value":"3"});
Note the difference of {"value":3} and {"value":"3"}
First one defines an object with property "value" with Integer type, and the second one defines an object with property "value" with String type. As Angular checks type match, it becomes that ("3" === 3) evaluates as false, this is why angular cant find selected option.
This is how it supposed to work.
Also note that - as Armen points out - objects are passed by reference as opposed to primitives which are pass-by-value.
Because of this fact, normally initializing a select box via ngModel from JSON (say, from a $resource record) you will need to set the model value to the specific array element/object property that is being internally checked for equality by Angular to the elements in the ngOptions (or repeated options elements with ng-values assigned to the same record objects). No two distinct objects in JS are considered equal, even if they have identical property names/values.
Angular has one way around this: use the "track by" clause in your ngOptions attribute. So long as you have a guaranteed-unique value (such as a record index from a db) Angular will check the value of the property between the model value and the records in ngOptions.
See https://docs.angularjs.org/api/ng/directive/select for more.
I am trying to do the following quite unsuccessfully so far.
I have an string that is semicolon separated. Say a list of emails, so
'email1#example.com;email2#example.com;email3#example.com'
What I am trying to accomplish is split this string (using split(';')) into an array of strings or array of objects (to aid binding). Each of the items I would like to bind to different input elements. After editing I want to read the concatenated value again to send to my backend.
Problem is that when editing one of the split inputs, the original item value is not update (which makes sense as I am guessing the individual items are copies of parts of the original), but I am wondering if there is a way to do something like that.
Note that I want this to go both ways, so watching the individual inputs and updating the original one manually, would just fire an infinite loop of updates.
I have tried a few different ways, including creating an items property get/set using Object.defineProperty to read and right to the string (set was never fired).
take a look at this plnker
You can construct a temporary array on each field update in order to do the string replacement of the old segment with the new value. In order to tackle the lost focus problem you will have to use the ngReapeat's track by $index. The internal array will not be recreated unless you add the separator to your original string.
Here is the complete solution on Plunker
Your main issue is your ng-model attribute on your repeated input element. I would start with making use of ng-repeat's $index variable to properly bind in ng-model. In your original Plunker 'name' is NOT a scope property you can bind to, so this should be changed to ng-model="names[$index]"
Here is a Plunker to reflect this. I made quite a few changes for clarity and to have a working example.
NOTE: You will find that when editing fields directly bound to a repeater, every change will fire a $digest and your repeated <input> elements will refresh. So the next issue to solve is regaining focus to the element you are editing after this happens. There are many solutions to this, however, this should be answered in a different question.
Although binding to a string primitive is discouraged, you could try ng-list.
<form name="graddiv" ng-controller="Ctrl">
List: <input name="namesInput" ng-list ng-model="vm.names"/>
<ul>
<input ng-repeat="name in vm.names track by $index" ng-model="name" ng-change="updateMe($index, name)"/>
</ul>
You'll need both track by $index and an ng-change handler because of the primitive string binding.
function Ctrl($scope) {
$scope.vm = {}; // objref so we can retain names ref binding
$scope.vm.names = ['Christian', 'Jason Miller', 'Judy Dobry', 'Bijal Shah', 'Duyun Chen', 'Marvin Plettner', 'Sio Cheang', 'Patrick McMahon', 'Chuen Wing Chan'];
$scope.updateMe = function($index, value){
// ng quirk - unfortunately we need to create a new array instance to get the formatters to run
// see http://stackoverflow.com/questions/15590140/ng-list-input-not-updating-when-adding-items-to-array
$scope.vm.names[$index] = value; // unfortunately, this will regenerate the input
$scope.vm.names = angular.copy($scope.vm.names); // create a new array instance to run the ng-list formatters
};
}
Here's your updated plunkr
By the nature of instances, new anObject({id: 1}) != new anObject({id: 1}).
This leads me to a problem regarding Knockout:
I have an array of possible options (all instances of an model with different property-values) and another model which helds a selection.
From a UI-perspective, I have a simple <select data-bind="options: [...]-binding, which works fine as long as I select an option.
Because my ViewModel can get stored and later recalled in a new applyBinding, I get into the problem of my data-bind not recognizing my selected value and consequentially removing the value.
Now my simplest solution is some sort of initialisation-function, which loops through the options and selects the right model-instance through an id-comparison. After I have the correct instance, I then can apply it to the "selectedValue"-property.
I didn't tried it out yet, but I don't see how it wouldn't work.
Because I don't think that this a strange requirment and a lot of people are using Knockout - I was hoping there was some nicer way of doing this?
Thanks!
Take a look at the Knockout.js documentation for "optionsValue": http://knockoutjs.com/documentation/options-binding.html
Typically you’d only want to use optionsValue as a way of ensuring
that KO can correctly retain selection when you update the set of
available options. For example, if you’re repeatedly getting a list of
“car” objects via Ajax calls and want to ensure that the selected car
is preserved, you might need to set optionsValue to "carId" or
whatever unique identifier each “car” object has, otherwise KO won’t
necessarily know which of the previous “car” objects corresponds to
which of the new ones.