1

我有一个消息流对象,如下所示:

ractive.data.messages:

{
    stream_id1: {
        some_stream_metadata: "foo",
        stream: [  
            {id: "someid1", message: "message1"}, 
            {id: "someid2", message: "message2"}
        ]
    },
    stream_id2: {
        some_stream_metadata: "bar",
        stream: [
            {id: "someid3", message: "message3"},
            {id: "someid4", message: "message4"}
        ]
    }
}

主模板:

{{#messages[ current_stream_id ]}}
    {{>render_message_stream}}
{{/messages[ current_stream_id ]}}

渲染消息流:

{{#stream}}
    <div class="stream">
    ...someotherstuff...
        {{>render_message}}
    </div>
{{/stream}}

渲染消息:

<div class="message">
...someotherstuff...
    {{message}}
</div>

我更改“current_stream_id”以更改呈现的消息流。

在更新时,我像这样更改消息流的内容:

ractive.merge(
    "messages.stream_id1.stream",
    new_message_stream,
    {
        compare: function ( item ) { return item.id; }
    });

我还尝试了 compare: true 选项而不是函数,结果相同:

Ractive 总是认为这两条消息实际上属于同一个 DOM 元素,即使它们存在于完全不同的消息流中:

ractive.data.messages[ "stream_id1" ].stream[1].message
ractive.data.messages[ "stream_id2" ].stream[1].message

问题:

  • 当有介绍/结尾动画时,ractive 动画始终只是消息流的结尾,即使流中间的消息被删除,我也需要帮助让 ractive 了解哪些消息是相同的。

  • 当我更改 current_stream_id 时,ractive 不会重新渲染完整的 {{>render_message_stream}} 部分,而是进入现有 dom 并更改所有现有消息中的 {{message}} 字段,尽管这可能有利于 dom 元素重用,这会触发很多错误的动画。(例如,如果stream1 比stream2 多一条消息,它会触发流中最后一条消息的介绍/结尾动画)。

4

1 回答 1

2

其中一个问题有一个直截了当的答案;不幸的是,另一个没有。

我将从简单的开始 - 事实上

ractive.data.messages[ "stream_id1" ].stream[1].message
ractive.data.messages[ "stream_id2" ].stream[1].message

属于同一个 DOM 元素。您是正确的,Ractive 更新现有元素而不是删除它们并创建新元素 - 这是其设计的核心部分。在这种情况下,这是不受欢迎的行为,但您可以像这样解决它:

// instead of immediately switching to a new stream ID like this...
ractive.set( 'current_stream_id', 'stream_id2' );

// you can set it to a non-existent ID. That will cause the existing DOM
// to be removed. When you set it to an ID that *does* exist, new DOM
// will be created:
ractive.set( 'current_stream_id', null );
ractive.set( 'current_stream_id', 'stream_id2' );

// or, if you'd like the initial transitions to complete first...
ractive.set( 'current_stream_id', null ).then(function () {
  ractive.set( 'current_stream_id', 'stream_id2' );
});

另一个问题 -merge()不是合并,而是表现得好像你在做一样ractive.set('messages.stream_id1.stream', new_message_stream)- 更难。问题是,虽然你我都知道这{{#messages[ current_stream_id ]}}等于messages.stream_id1when current_stream_id === 'stream_id1但 Ractive 不知道

它所知道的是我们有一个表达式,其值由messages和确定current_stream_id。当这些引用中的任何一个的值发生变化时,就会重新评估表达式,如果值发生变化,则 DOM 会被更新——但使用标准的set(). 当你这样做时ractive.merge('messages.stream_id1.stream', ...),Ractive 会更新所有依赖于“上游”或“下游”的关键路径的东西messages.stream_id1.stream- 其中包括messages. 所以这就是表达式知道它需要重新评估的方式。

未来版本的 Ractive 可能会以更智能的方式处理这种情况。也许它可以记录受合并操作影响的数组,并检查评估结果以查看它们是否与这些数组之一相同,如果是,则使用merge()而不是set(). 也许它可以以某种方式分析该函数,以查看该部分是否应将自己注册为for as long as{{#messages[ current_stream_id ]}}的依赖项,而不是内部生成的keypath。messages.stream_id1current_stream_id === 'stream_id1'${messages-current_stream_id-}

在此期间,这些都对您没有帮助。在当前情况下使用的唯一方法merge()是拥有一个不使用表达式的单独引用,以及模式观察者的一点魔法:

主模板:

{{#current_messages}} <!-- rather than `messages[ current_stream_id ]` -->
    {{>render_message_stream}}
{{/current_messages}}

渲染消息流:

{{#current_message_stream}} <!-- rather than `stream` -->
    <div class="stream">
        {{>render_message}}
    </div>
{{/current_message_stream}}

代码:

ractive.observe( 'current_stream_id', function ( id ) {
    var current_messages = this.get( 'messages.' + id );

    this.set( 'current_messages', current_messages );

    // hide existing stream, then show new stream
    this.set( 'current_message_stream', null ).then(function () {
        this.set( 'current_message_stream', current_messages.stream );
    });
});

// when ANY message stream changes, we see if it's the current one - if so, we
// perform a merge on the top-level `current_message_stream` array
ractive.observe( 'messages.*.stream', function ( new_stream, old_stream, keypath, id ) {
    // the value of any * characters are passed in as extra arguments, hence `id`

    if ( id === this.get( 'current_stream_id' ) ) {
        this.merge( 'current_message_stream', new_stream, {
            compare: function ( item ) {
                return item.id;
            }
        });
    }
});

我已经建立了一个JSFiddle 来演示这一点。我希望这是有道理的,如果没有,请告诉我 - 抱歉,我没有早点回答这个问题。

于 2014-02-21T23:07:49.050 回答