Knockout: Propper way of Option-Binding with Model-Instances - javascript

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.

Related

Should I use PUT or POST to add a new subitem to an existing item?

Suppose I have an item (model) which has fields that are arrays, for example:
const item = {
name: "",
prices: [{somePriceSpecificObject}],
}
When I want to add a new price to that item prices array, which method is better to be used (in terms of quality I guess), PUT or POST?
Right now I am using PUT because by creating a new price for prices array I am editing the item, but I am now making functionality that will let me edit the existing entries, and it naturally goes into PUTaswell, which got me thinking about this issue.
Because, it also kinda makes sense to use POST, because price relative to the item is a new thing, but the item relative to me with the new price is still the item, just with an edited field (prices).
Now one solution to my dilemma might be to make a separate model for prices? But I never did that, because, a given item in my specific conditions, will never have more than a handful of prices, and it will make me make more queries to the database (I am not concerned about performance, but still, just looking for best practices).
So which route do you think I should go?
EDIT: My api endpoints look like this at the moment:
post("/one", POST.oneItem);
put("/addprice", PUT.addPriceToItem);
put("/editone/:id", PUT.EDIT.item);
put("/editone/:id/price/:priceId", PUT.EDIT.price);
Notice I had to make a nested EDIT object in my PUT object (that provides the handling functions), to be able to easily distinguish between adding a price and editing a price, so that got me into thinking about all of that.
I have seen in most cases of production code there is never a delete query being run as the schema has a soft delete key which gets turned when wanting to delete a particular row/document.
Hence it is always using PUT as a best practice even for delete. Any changes to an existing document need to be a PUT because it will help you in improving performance unless the document is completely different from the one before. It even becomes easier while caching as one needs to update the cache and not add another one in and remove some other one (in case the cache becomes full at that point). In the end, if you intend to keep prices as an array then I guess PUT is a better choice.
"PUT /uri" creates/updates the thing at "/uri". Your example where the URI contains "/addprice" is IMHO a misuse of PUT.
If you have a collection X and want to add an item Y, use "PUT /X/Y". If you want the server to name the item, use "POST /x".

Using ko.observableArrays without viewModel

I want to use knockout observables without the viewmodel. I simply want to use one observableArray as a data source for a DevExtreme data grid. So for now, my idea was fairly simple: I just declared a variable (shuttleList) as empty ko.observableArray. Later, I fill that up through an ajax request. My grid is set to that variable as data source.
However, nothing happens, as I change the array. Still, I have to manually replace the dataSource of the grid using its option method. What am I doing wrong?
shuttleList = ko.observableArray([]);
$.getJSON('http://someCall?ID=' + id, function (e) {
shuttleList(e.tourenList.find(x => x.title == 'Base').shuttleList);
});
var grid = $("#gridContainer").dxDataGrid({
dataSource: shuttleList,
…
});
I know that this is not the way knockout is supposed to be used, but can I somehow make this work automatically – without doing some manual grid refreshing in the shuttleList.subscribe event?
By the way, just calling the grid's refresh()/repaint() methods doesn't help either. I haven't found a way around resetting its dataSource option yet.
grid.option('dataSource', shuttleList);
Thank you very much in advance for your help!
I think all you're missing is to initialize the knockout bindings with ko.applyBindings, and you can use a specific element when calling that function. The "view-model" can be as simple as object-brackets with your variable inside:
ko.applyBindings({shuttleList}, document.getElementById("gridContainer"));
var shuttleList = ko.observableArray([]);
setTimeout(function(){shuttleList.push("success!")}, 1000);
ko.applyBindings({shuttleList}, document.getElementById("gridContainer"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<span>waiting for update...</span><br/>
<span id="gridContainer" data-bind="text: shuttleList"></span>
I know that this is not the way knockout is supposed to be used, but
can I somehow make this work automatically – without doing some manual
grid refreshing in the shuttleList.subscribe event?
The only way to make KO to update something automagically is creating dependency. So in your case you can create anonymous computed that will depend on shuttleList observable array:
ko.computed(function(){
grid.option('dataSource', shuttleList());
});
As you can see the observable array is called there. That's what creates dependency. Now any change made within shuttleList array will re-evaluate the function passed to computed and grid's dataSource will be updated.
However it doesn't seem optimal solution because pushing even single value will cause whole grid to render. But if you data doesn't contain too many rows then this approach will work good.
Furthermore there is another drawback - since the computed creates one-way dependency you have no way to reflect changes on the shuttleList array by using grid's API methods.

Ember select is not showing the selected item

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.

Editing a delimited string using multiple input fields in AngularJS

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

bind complex json object to knockout dropdown menu

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)

Categories

Resources