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

Unable to update ~prefix in for template when adding/removing items #313

Closed MrJoe closed 9 years ago

MrJoe commented 9 years ago

Maybe this is related to #169, but I am using a ~prefix to pass on the prefix for id / name generation within my view.

<h1>Items</h1>
<ul id="list"></ul>

<script id="listTmpl" type="text/x-jsrender">
    <span data-link="{:SomeProp}"></span>
    <button class="delete">Delete</button>

    {^{for Items ~prefix='SomeObject[' + #index + ']' tmpl='#childTmpl' }}
    {{/for}}
    <hr/>
</script>

<script id="childTmpl" type="text/x-jsrender">
        <li>
            <span data-link={:~prefix}></span>
            <input data-link="{:FirstName} name{:~prefix + '.FirstName'}" />
        </li>    
</script>

However when I delete the first item using $.observable(data).remove($.view(this).index); the ~prefix isn't updated. I have created a jsfiddle here: https://jsfiddle.net/Lasteo0p/4/ Try to delete the first or second item and you'll see the rendered #index isn't updated. This is a somewhat simplified example, but I want to use this to generate unique name and id fields such as generated by asp.net mvc so I can just post my form if needed.

BorisMoore commented 9 years ago

Yes, as mentioned in #169 ~prefix will not behave as an observable property when you delete items.

I updated your fiddle here https://jsfiddle.net/Lasteo0p/5/. (BTW I changed the jsviews to come from jsviews.com/download, since loading direct from GitHub it does not have the correct mime type for running securely in jsfiddle.)

Two options:

1) write ^~prefix=...#index.... This tells JsViews to make the {^{for...}} tag listen to any observable changes in the expression, and to re-render when anything (in this case #index) changes. So that is a simple one character fix, but it has a hidden side-effect - when you delete an item the whole output of the {^{for ...}} tag is refreshed. So it re-renders the remaining items.

2) set ~outer=#view, so then in the content you can get the index as ~outer.index. That will behave observably, so when you delete an item it will update wherever it is used - without re-rendering all of the content under {^{for ...}}

Does that make sense?

BorisMoore commented 9 years ago

@MrJoe: I didn't hear back from you... Closing this issue.

MrJoe commented 9 years ago

Sorry, didn't have the time to test it out in my scenario, the ^~prefix= ... seems to work fine for now in my case. The ~outer=#view solution is somewhat impractical because that would tie the sub templates to the master templates knowledge of knowing what its complete id / name attribute should be.

One question though, if ~outer=#view is something that works, would it be possible to do something like #view.prefix='some #index prefix' so when I use ~outer=#view i can actually use ~outer.prefix.

BorisMoore commented 9 years ago

Well no, I don't think I would add new observable properties on views. But you can create a helper that returns either an object with a prefix property that is tracking changes in #index, or an observable computed function which depends on #index.

Here are those two approaches: https://jsfiddle.net/Lasteo0p/6/

These are more advanced uses of observability, so I hope you can get a sense of how they work../

BTW you had a