dalgard / meteor-viewmodel

Minimalist VM for Meteor
24 stars 2 forks source link

Updating textarea value with jQuery does not trigger viewmodel value update #19

Closed KoenLav closed 8 years ago

KoenLav commented 8 years ago

When I have a textarea and bind it using {{bind "value: text"}} and then use jQuery (in another viewmodel) to modify the .val() of the element that is bound this does not trigger an update of the viewmodel property (text) even though the value of the element has changed.

KoenLav commented 8 years ago

Ah, I see I can fix this using jQuery .change() after updating the value...

dalgard commented 8 years ago

The really big advantage of using the viewmodel pattern, is that you never have to update the DOM manually – all you do is update the viewmodel, and the bindings take care of the rest.

You can say, that if you ever use jQuery outside of a custom binding definition, then you are doing it wrong ;)

Instead of updating the textarea with val(), just set the new value on the textarea's viewmodel. This viewmodel can be retrieved, preferably, by selecting it with one of the traversal methods (this.child()), or, alternatively, with one of the static methods ( ViewModel.findOne()), in case it is in a totally different part of the view hierarchy.

KoenLav commented 8 years ago

Let me pick your brain here a little:

We have a jQuery on-screen keyboard which changes the value of the currently focussed element, so there is (as far as I know) no easy way of determining which viewmodel we are updating.

Would you then still say that this is 'wrong'?

dalgard commented 8 years ago

As long as you have the actual DOM element of the textarea, then you can get to Blaze world using Blaze.getView(element).

From there, you can traverse the view hierarchy until you get the template instance, which has a viewmodel property. If you simply need to update the property bound to the element in question, do this (ES6 syntax):

const dom_elem = $(".my-textarea")[0];
const blaze_view = Blaze.getView(dom_elem);
const binding_nexus = blaze_view.nexuses.findOne(nexus => nexus.elem === dom_elem && nexus.name() === "value");
const viewmodel_prop = binding_nexus.getProp();

// Set new value
viewmodel_prop("New text in textarea");

// Get value
viewmodel_prop();  // "New text in textarea"

EDIT: The above can be achieved in 1.0.0 with:

const dom_elem = document.querySelector(".my-textarea");

// Set new value
ViewModel.Nexus.findOne(dom_elem, "value").prop("New text in textarea");
KoenLav commented 8 years ago

Hi Kristian,

I expected this was possible, but do you think that this is the 'correct' solution in this particular situation? It seems like this approach is more likely to break under different circumstances than using .val().change() in the keyboard.

dalgard commented 8 years ago

I have to agree that it's probably a cleaner solution to keep your jQuery plugin in jQuery land. Library ignorance is bliss ;)

KoenLav commented 8 years ago

Hehe, was just wondering whether you might have some insight which could convince me otherwise :)

Thanks!

dalgard commented 8 years ago

Np, don't hesitate to ask again.

P.S. I would consider adding a couple of static convenience methods to ViewModel:

ViewModel.getViewmodel(dom_element) ViewModel.getProp(dom_element, binding_name)

dalgard commented 8 years ago

I've made some API changes along the lines of my previous comment. You can check out the part most relevant to your original question in the third code example in the Static methods section. I've also updated my previous answer in this thread.

After a long time with no issues, I've also become ready to release version 1.0.0 of the package.

The new version should be compatible with the previous version 0.9.4, except that I've removed jQuery as a dependency, which means that elements and events are no longer wrapped by jQuery.

See the History section.

KoenLav commented 8 years ago

Nice! :+1: