Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rv-if does not update children when model changes #452

Open
diachedelic opened this issue Feb 22, 2015 · 19 comments
Open

rv-if does not update children when model changes #452

diachedelic opened this issue Feb 22, 2015 · 19 comments

Comments

@diachedelic
Copy link

Any binding deeper than one level within an rv-if statement is not updated when the object representing the deep binding is replaced, ala:

<div rv-if="foo">
    { foo.bar }
</div>
var model = {
    foo: {
        bar: 'original'
    }
}

model.foo = {
    bar: 'updated' // not refreshed in view
}

http://jsfiddle.net/v001txq3/

@AMTourky
Copy link

It seems that rivets can't handle when a bound object's property is totally replaced with a new one (the target property you are binding in the template)
in this case model.foo completely replaced with a new object {bar: 'updated'}

so, if you changed only a property of the target object, this will work just fine:
model.foo.bar = 'updated'

http://jsfiddle.net/h53tfahn/

@diachedelic
Copy link
Author

Yes I realise that, but I want to replace the whole object - you can do that anywhere except inside an 'rv-if' statement!

@diachedelic
Copy link
Author

This seems to be the case with arbitrary bindings (e.g. rv-text and rv-value: http://jsfiddle.net/v001txq3/4/)

@diachedelic
Copy link
Author

And the sub-elements of the rv-if element are updated otherwise: http://jsfiddle.net/v001txq3/6/

@diachedelic
Copy link
Author

Ah...the rv-if binding does not update its subelements at all: http://jsfiddle.net/v001txq3/8/

Is this how rv-if is supposed to work? Is there an alternative to rv-if that updates? I know there is rv-show but you can't do this

<div rv-show="foo">
    { foo.bar }
</div>
var model = {}

model.foo = {
    bar: 'hello'
}

because accessing model.foo.bar is an error when model.foo is undefined

@diachedelic diachedelic changed the title Deep binding within rv-if statement does not update when deep object replaced rv-if does not update children when model changes Feb 23, 2015
@tayfunyugruk
Copy link

@diachedelic rivets' default adapter is a defineProperty based adapter so it listens the object it is provided at first binding time (at the rivets.bind phase). If you want to override the current object with new one you need to inform the view about model change with view.update (view object is returned from rivets.bind).

You can see the result here : http://jsfiddle.net/opdow1sb/
please double check and be sure about my finding.

Best Regards.

@diachedelic
Copy link
Author

Oh wow, yeah that works. But why is this only an issue for rv-if?

On 8 May 2015, at 5:25 pm, Tayfun YÜĞRÜK [email protected] wrote:

@diachedelic https://github.com/diachedelic rivets's default adapter is a defineProperty based adapter so it listens the object it is provided at first binding time (rivets.bind phaes). If you want to override the current object with new one you need to inform the view about model change with view.update (view object is returned from rivets.bind).

You can see the result here : http://jsfiddle.net/opdow1sb/ http://jsfiddle.net/opdow1sb/
please double check and be sure about my finding.

Best Regards.


Reply to this email directly or view it on GitHub #452 (comment).

@tayfunyugruk
Copy link

i think this is because each binders gets the reference of the sub object and listens that reference so completely replacing sub object affects the root view but not rv-if because rv-if has old reference of sub object.

var root = {
   sub : {
     key : value
   }
};

in this object sub object has a reference to key-value and rv-if listens that reference not whole object. and if you set a new object instance you change the reference.

root.sub = {
   key2 : value2
}

if i did not miss anything this is how default binding of objects work.

@Namek
Copy link
Contributor

Namek commented Feb 27, 2017

#512 seems to be related

@diachedelic
Copy link
Author

It seems like rv-if just does not rebind, am I right?

http://jsfiddle.net/h53tfahn/

@Namek
Copy link
Contributor

Namek commented Apr 10, 2017

@diachedelic what can you see in output? I see both "updated" which seems OK.

@diachedelic
Copy link
Author

Oops I forgot to fork that fiddle.

I have made a new one which shows the exact problem I'm having: rv-src is never rebound if there is an rv-if on the same element

http://jsfiddle.net/bLwdbr2o/

@Namek
Copy link
Contributor

Namek commented Apr 10, 2017

@diachedelic by trying this, you can see that rv-if alone works but doesn't trigger update of other directives (in this case rv-src): http://jsfiddle.net/qobb51ed/

rivets.bind(document, model);
setTimeout(() => {model.foo = null;}) // image for a while disappears
setTimeout(() => {model.foo = friesURL;},1000) // image reappear but still with old URL

@diachedelic
Copy link
Author

diachedelic commented Apr 10, 2017

Look at this weirdness - neither rv-text, rv-src nor rv-title are being rebound

http://jsfiddle.net/ryL2zqt7/

@luikore
Copy link

luikore commented Jun 4, 2017

I have a simple fix for this problem by not copying model in rv-if's routine in src/binders.coffee

-        models = {}
-        models[key] = model for key, model of @view.models
+        models = @view.models

But don't know if it may cause other problems?

@Namek
Copy link
Contributor

Namek commented Jun 4, 2017

@luikore I'm still note sure what was the purpose of creating a new object because I didn't look into that too deeply. However, you might want to look here:
blikblum@7203c10#diff-72d0612bafa6e737e475d5bce8cbab4cL169 which commits to a branch https://github.com/blikblum/rivets/tree/svelte

@blikblum
Copy link
Contributor

blikblum commented Jun 4, 2017

In the svelte fork i changed how scope is created, basically by not shallow copying the models in the nested scopes. Instead, it uses prototype like inheritance. It searches the property to bind in the current scope, if not find search in parent scope until find the root scope.

Each scope has a $parent property pointing to parent scope.

This is similar how vue works.

It has some advantages. Fixes #486 #512 #417 and this bug

Also allows to bind to properties in parent scopes.

See https://codepen.io/blikblum/pen/eBQzJO . Try to edit one of the input fields that are bound to a property in the parent model

Now do the same with https://codepen.io/blikblum/pen/MKXXOX?editors=1010#0

svelte fork it behaves similar to vue: https://codepen.io/blikblum/pen/ZQRRmm

@arantius
Copy link

I don't know for sure if this is related or not. In our code, we have:

  • An object (gTplData), with a key (userscripts), which is an object with two keys (active, inactive), each of which is an array of objects.
  • A UI action can remove one of those leaf objects, so we .splice() its index out of the array containing it, to remove it from the display.

This works fine, unless it's the last item in the array. Because we also have, in the html, some rv-unless="..." which take these arrays as their inputs. If I remove all the rv-unless'es that point at these arrays, everything works.

For now instead I'm working around the issue, by not calling splice() if the length is 1.

@blikblum
Copy link
Contributor

blikblum commented May 31, 2018

@arantius
You can try tinybind, a fork of rivets with many bug fixes, like this one, and other improvements

https://github.com/blikblum/tinybind

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants