BorisMoore / jsviews

Interactive data-driven views, MVVM and MVP, built on top of JsRender templates
http://www.jsviews.com/#jsviews
MIT License
855 stars 130 forks source link

LinkTo not refreshing DOM on empty value #457

Closed alnico2001 closed 9 months ago

alnico2001 commented 10 months ago

https://www.jsviews.com/#link2way@linkto https://www.jsviews.com/#tagoptions@bindfrom

In these two similar samples listed above, if you change the value for field Name and press Apply the underlying value updates, pressing Cancel returns the previous value. If you don't press Apply, pressing Cancel still returns the previous value. All good.

If you Apply an empty value "", if too updates the underlying data correctly. But now type a value into the field (don't press Apply) but press Cancel...the underlying data updates correctly, but the DOM does not (Name value remains, instead of empty)

I discovered this when building a search field with an x button at the end for clearing the search field (regardless if it is applied or not). The button x observably updates the LinkTo value.

BorisMoore commented 10 months ago

Yes you are right.

The first sample should work correctly if you modify the cancel: function(), and replace

$.observable(this.current).setProperty({title: "", color: ""});

with

$.observable(this.current).setProperty({title: null, color: null});

The first sample also works if you set the values to undefined, rather than null, but the second sample does not. Not sure why.

Similarly, the first sample also works if instead you instead write

$.observable(this.current).removeProperty("title"); $.observable(this.current).removeProperty("color");

but again, this does not work with the second sample. I have not been able to investigate further.

Let me know if these suggestions help in your scenario.

BTW the behavior of these samples is partly related to the fact that the controls (buttons, inputs) are within a <form>. But they may give you some ideas for your scenario.

alnico2001 commented 9 months ago

Your suggestions work, however, in my scenario I cannot alter the current value (without Go/Apply) as it is observed and will fire an ajax call.

What does work is to use jquery to set the input val(""), empty.

Here is a working example https://jsfiddle.net/alnico/rf7pb9ke/ When searching... x resets the modified value to previous value or if current and modified are equal sets them both to empty "". If you comment out line 52, $(ev.currentTarget)... and then with current empty, an entered input value does not update/clear when x is pressed even though modified is updated.

This isn't a critical issue, but thought I would bring it to your attention as I was expecting an observable update to the LinkTo modified value to refresh the DOM (even with an empty value).

BorisMoore commented 9 months ago

Yes, I agree with your approach for Cancel - of reverting the DOM values directly, and hence avoiding triggering additional observable events. Incidentally, I also like the fact that you moved your buttons out of the <form>. In my samples, the Cancel button is in the form, and is therefore treated as a second "submit" button. Clicking on my Cancel button triggered first cancel() and then apply() (since I didn't return false, or call preventDefault(), or set type="button").

The behavior of <input data-link="current.search linkTo=modified.search" /> is that the value will be updated if current.search changes observably, but not when modified.search changes observably. With linkTo specified, the two-way binding is split into an outgoing binding to modfied and an incoming binding from current

About refreshing the DOM, one possible source of 'unexpected behavior' could be this scenario: Suppose this.current.search has the value "A", and the <input> is on some other value "B", and now you call $.observable(this.current).setProperty("search", "C");. This will trigger an update, and the input will have value "C". However, suppose instead you call $.observable(this.current).setProperty("search", "A");. This will be a no-op, (since this.current.search already has the value "A"), and as a result the <input> will remain with value "B".

alnico2001 commented 9 months ago

Yeah, there really is no 'perfect' solution here. linkTo binding split between two targets works fine with a minor issue. It's quit the conundrum actually.

About refreshing the DOM...

Yes, that could be a problem, but I don't see this as an issue though. If a person uses linkTo, then the expectation is the current value will get updated from the modified value (which is in sync with the <input>) and not some 'other' value. At least that's how I see it...linkTo has a special use case.

Theoretically what (I think ;-) would be ideal is that the modified value would have two-way binding while current has one-way binding. But how could this be achieved... Here is a link to experimental code where linkTo is not used, modified is two-way data bound and the current value is copied into modified on initialization (one-way binding--not possible). This approach isolates changes both ways to modified and doesn't affect current raising a change event.

https://jsfiddle.net/alnico/t657m2rc/1/

Anyway, I am not suggesting any changes here. linkTo works with a caveat (one that wasn't clear to me initially). I am completely fine updating the DOM value directly; and I would likely not use the experimental code either, as linkTo is built-in and intuitive.

BorisMoore commented 9 months ago

Thanks. That is useful thinking. But as you say, for now, the current linkTo behavior is probably best kept as is. For most scenarios at least, it is useful and intuitive.

Thanks for your investigation and comments!

I'll resolve this issue.