This project is read-only.
1

Closed

Indexes are wrong if I use observableArray.splice

description

I am using Html.ClientBlockRepeater.
However, in javascript, when I modify the Knockout observableArray that the ClientBlockRepeater is bound to, the indexes start to be wrong.

For example, the HTML may start out as something like this:

<div data-name-prefix="Items[0]">
<input name="Items[0].Value" />
</div>
<div data-name-prefix="Items[1]">
<input name="Items[1].Value" />
</div>

Then I use "splice()" in order to replace an item in the array. For example:

window.theModel.Items.splice(0,1,newItem);

Then the HTML looks like this:

<div data-name-prefix="Items[2]">
<input name="Items[2].Value" />
</div>
<div data-name-prefix="Items[1]">
<input name="Items[1].Value" />
</div>

The indexes in HTML should be "0", not "2". Incidentally, this no longer posts to my page since the MVC mapper expects arrays to start at "0".


I did some initial investigation, and it look like there is a "_tag" property that MvcControlToolkit.Bindings-3.0.0.js appends to things. This "_tag" property is used as the index. This property is generated using mvcct.utils.uniqueTags.newTag(). I don't think this is correct, though.
Closed Nov 19, 2014 at 5:05 PM by frankabbruzzese

comments

frankabbruzzese wrote Nov 19, 2014 at 8:26 AM

No, it is correct! It is the only way to ensure name uniqueness! Knockout.js renders only the items that changed someway. Thus, suppose, that instead of substituting the first item of the array you just put a new item on top of the array. If the toolkit gives a [0] index to this new item, its name would be the same of old first element of the array that now is in the second place since this last is not rendered again and accordingly it preserve its old name, that is [0] based.

I might change the way knockout.js acts, but this would force the re-rendering of the whole array each time the array changes...Not a good idea, expecialyy for big arrays!

You should not rely on input field names when submitting a page implemented with client side techniques, but you should submit the client side ViewModel instead. Client Blocks do this jobs for you since they take care of serializing and putting the client side viemodel into an hidden field as soon as the form is submitted.


Input fields names are handled in such a way that unobtrusive validation works properly. Incidentally, also when using server side techniques the same name problem arise, due to new elements added and deleted to an array rendered with a grid. In the server side case Mvc Controls Toolkit custom model binder takes care of all names issues.

joshmouch wrote Nov 19, 2014 at 3:01 PM

Makes sense. I am already in a Client Block, though, so the portion that serializes the client side model into an input must not be operating... since MVC is using the inputs to create the server-side view model instead of using a serialized client side view model. I'll look into it a bit.

frankabbruzzese wrote Nov 19, 2014 at 5:04 PM

If you need just to replace an item, often it is better to copy all its fields from another similar item with mvcct.utils.restoreEntity (x, y, [visitRelation=false]) (see here). This way you change all properties values as needed without changing the pointer to the original item. I prefer having an unique copy of each entity in Javascript, thus implementing uniqueness of principal key with uniqueness of pointers.

wrote Nov 19, 2014 at 5:05 PM

wrote Nov 19, 2014 at 5:05 PM

joshmouch wrote Nov 19, 2014 at 5:22 PM

OK, I was confused by the fact that your Grid examples (using ClientBlockRepeater) all put a dummy form at the top of the View so that the ClientBlock functionality is mostly missing from the rest of the View. So instead of using the client-side view model, you use regular inputs in order to post back filtering, sorting, and paging information.