knockout / knockout

Knockout makes it easier to create rich, responsive UIs with JavaScript
http://knockoutjs.com/
Other
10.44k stars 1.52k forks source link

please fix the data, event object #2532

Closed julientype closed 4 years ago

julientype commented 4 years ago

The main view holds one array self.Tables = ko.observableArray();

in the array i give it a name and attach a nested array with more variables.... var self = this; self.name = ko.observable(iname); self.items = ko.observableArray();

I attach create a click function self.removeTable = function(data,event) self.Tables.remove(data); ====this works no problem on the array but not the nested one==== self.getFieldName = function(data,event) =====problem is in the nested array==== the data object needs to be smarter..... ko is passing reference to it like var a = data.parent,index; or var a = data.item;

you have nothing to play with without running mass loop to find the object index

i may need to run other types of action on the selected data object....

should work as a pointer object for the array nested elements and parent. function(data,event) self.Tables[data.parentID][dada.id]

no loop your passing index location of the item. why loop 1000 entry when you can call it in one shot with data object values

karimayachi commented 4 years ago

Short answer

You can pass the parent in your binding like so:

<button data-bind="click: getFieldName.bind(null, $parent)">Show Name</button>

And use it in your Viewmodel:

self.getFieldName = function(parent, data, event) {
    console.log(parent.name());
}

Long answer

Object instances in JavaScript, or indeed any language, don't have a hierarchy. They can have any number of references to it. Pushing an object to an array doesn't create a hierarchy, it just adds another reference to it. Hierarchy is something the developer implies within the context of the object.

That said: Knockout does create a hierarchy, because it adds context to an object, once this object is used as a viewmodel and that viewmodel is used to bind against the DOM. Now, the viewmodel object is part of a hierarchical context: the DOM.

Knockout creates a binding-context for each level of nested foreach/with/etc -binding. These binding contexts are hierarchical and have a parent, based on their position in the DOM.

It doesn't however impose this hierarchy on the underlying object, because that object can be used simultaneously in many contexts (each with it's own hierarchical structure) or even completely outside of the binding context.

So the view is the only place where the object 'lives in context' and the place to get the parent. As in the above example.

I agree though that in many instances it would be very helpful to access the binding context from the viewmodel in callback-functions as per your question, because in callback functions you could actually know in what context the object lives. I have been in that situation many times, but every time I ask myself if I could rethink my problem, because it usually is not necessary to rely on a hierarchy that isn't actually there.

Finally (and I have done so many times before) if it really is necessary and you are certain that the object will always be used in a hierarchical context, you could always introduce the parent-child relation yourself:

class ParentTable {
   constructor() {
       this.name = ko.observable('MyName');
       this.items = ko.observableArray();

       let newObject = new ChildObject(this);
       this.items.push(newObject); 
   }
}

class ChildObject {
   constructor(parent) {
       this.parent = parent;
       this.name = ko.observable('ChildName');
   }
}

Regards

julientype commented 4 years ago

Yes that worked grate......
why the null? click: getFieldName.bind(null, $parent)

What i like about this is i can keep the DOM elements small. and all my custom actions are held in the view model by a nested array not the DOM tree i self ..... like filter ascend or descend Click: FieldsFilter.bind(null, $parent)
no array loops this way.... That take space in the View Model

karimayachi commented 4 years ago

why the null? click: getFieldName.bind(null, $parent)

See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

.bind() returns a new function that, when called, calls the original function and sets the object passed as the first argument to .bind as the this-context of that function. It also prepends the rest of the arguments to the argumentlist of the original function.

In this case you don't use this and you don't have to set anything.

You could also do something like:

<button data-bind="click: getFieldName.bind($context)">Show Name</button>
self.getFieldName = function(data, event) {
    console.log(this.$parent.name());
}

Personally I don't like this, because I always have TypeScript capture this to be the ViewModel.

More info about the binding-context, $context and $parent here: https://knockoutjs.com/documentation/binding-context.html

julientype commented 4 years ago

i,m just currently testing different binders to suit the task.......

tinybinder 20.0 KB dont think it can get smaller then that.... basically all i want it to do is pickup the jsonarray from the server..... I now have a data island that i can bind to elements..... regardless of the view.... I remove server calls this way...... this is from Tinybind at 20k I would simply load tinybinder in the iframe and transfer the modal data to it..... it can then do what it likes to it..... client server..... i can clean up memory leaks this way without affecting the main page memory allocation for models and get them to talk to each models ....... check iframe location<---- then send model in.... i let dom do style binding its faster its in dom memory..the even object will let me change or append class if i need to but css will do it for you ..... where model space is better used as relational data to present. JsonArray to elements..... even smaller would be better ... i may have 3 iframes with each its own copy serving json-data.... tinybind.bind(document.getElementById("test"), model);

function getItems() { return [{ name: 'x', value: 2 }, { name: 'y', value: 1 }, { name: 'z', value: 3 }] } var model = { items: getItems(), additem(event,data) { alert(data.$index); alert(data.item.name); alert(data.item.value); model.items.push({ name: 'pushed' }) }

On Mon, Jun 8, 2020 at 4:12 AM Karim Ayachi notifications@github.com wrote:

why the null? click: getFieldName.bind(null, $parent)

See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

.bind() returns a new function that, when called, calls the original function and sets the object passed as the first argument to .bind as the this-context of that function. It also prepends the rest of the arguments to the argumentlist of the original function.

In this case you don't use this and you don't have to set anything.

You could also do something like:

self.getFieldName = function(data, event) { console.log(this.$parent.name());}

Personally I don't like this, because I always have TypeScript capture this to be the ViewModel.

More info about the binding-context, $context and $parent here: https://knockoutjs.com/documentation/binding-context.html

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/knockout/knockout/issues/2532#issuecomment-640442842, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFUMRZOWDRJV6ZOVHPKFOQTRVSMOXANCNFSM4NUL3ZYQ .