문제

I've been playing around with the fantastic Ractive.js library (not to be confused with Facebook's Reactive.js). I worked out that you can render a two-dimensional using the following template code:

<div class="container">
{{ #frameContainer:i }}
    <div class="row">
    {{ #frameContainer[i] }}
        <div on-click="cell-click" class="cell {{ . ? 'on' : 'off' }}"></div>
    {{ /frameContainer[] }}
    </div>
{{ /frameContainer }}
</div>

This works exactly as I'd expect and the inner cell-click event returns the correct keypath (e.g. frameContainer.2.4).

I then wanted to turn this into a three-dimensional array (to add a "time" axis). However, the following did not work:

<div class="container">
{{ #frameContainer[time]:i }}
    <div class="row">
    {{ #frameContainer[time][i] }}
        <div on-click="cell-click" class="cell {{ . ? 'on' : 'off' }}"></div>
    {{ /frameContainer[][] }}
    </div>
{{ /frameContainer }}
</div>

Where time represents the current time value (it will only ever show one "time" at a time - seems obvious when you say it like that...).

This... sort of works. It displays the grid as it should, but the returned keypath for the cell-click event is no longer correct, returning something like ${frameContainer-time-8-}.2 - which has lost the time value (and has gone a bit weird).

Obviously I could just do it by having a currentFrame value which I render from and switch around with ractive.set(), but that seems less elegant. Is there a way to do it purely within the template? And, if not, what's the most efficient way of doing it otherwise?

Thanks!

도움이 되었습니까?

해결책

This is quite a tricky one. Basically, the ${frameContainer-time-8-} keypath is how Ractive uniquely identifies an expression, and ${frameContainer-time-8-}.2 means 'the third member of whatever that expression evaluates to'.

What's happening here is this: When Ractive's parser sees the {{ #frameContainer[time][i] }} section, it parses frameContainer[time][i] as a JavaScript expression and turns it into the following (you can try this yourself - Ractive.parse('{{#frameContainer[time][i]}}')):

{
  r: ['i','time','frameContainer'],
  s: '${2}[${1}][${0}]'
}

When the template is rendered, Ractive creates an evaluator for that expression, which has a function generated from the string (the s property), and which watches the time and frameContainer values (it doesn't need to watch i because it can't change). When either or both of those values change, the function is executed with them as arguments. If it returns a changed value, Ractive needs to update the view.

Since there's a good mechanism for propagating viewmodel changes to the view - keypaths - that's what the evaluator uses. In order to do so, it needs to create a unique keypath, hence ${frameContainer-time-8-} (it can't contain dots or square brackets, because Ractive would try to split on those).

So here's the thing: expression keypaths are one-way. You can't do ractive.set('${frameContainer-time-8-}.2', 'true') like you can with regular keypaths, because Ractive can't figure out what underlying property that corresponds to (or if there even is one - it might be a derivative value for all it knows).

Solution 1

The simplest workaround would probably be to do something like this:

<div on-click="cell-click:{{time}},{{i}}" class="cell {{ . ? 'on' : 'off' }}"></div>

You can then use the time and i values in your cell-click handler (they will be the second and third arguments, after event).

I've done a simplified demo here (assuming I understood you correctly): http://jsfiddle.net/rich_harris/LYEGX/

Solution 2

The other way would be to do away with keypaths altogether and use an adaptor. I won't go into all the details here as it may not be the answer you're looking for, but there's some documentation and an example (some of the links are currently out of date, sorry...). In short, this method assumes you're able to use non-POJOs in your app.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top