如何强制Meteor重新加载订阅?

6
在我的应用程序中,sections 是一个集合,通过名为 course.sectionIds 的属性与 courses 相关联。初始加载正常,但是当在管理面板中添加一个部分时,我遇到了非响应式联接问题。
以下是路由:
@route 'adminCourse',
    path: 'admin/course/:course'
    waitOn: -> Meteor.subscribe 'course', @params.course
    data: -> Course.first()

并且这些章节已经包含在课程出版物中:

Meteor.publish 'course', ( courseId ) ->
    return null if not this.userId

    # [some permission checks]

    courses = Course.find courseId
    sections = Section.find _id: $in: _.flatten courses.map ( course ) -> course.sectionIds

    [ courses, sections ]

我知道关于反应式连接的知识,但是我无法使用方法#1或#4(过度发布和客户端连接),因为这里涉及权限检查(您只能查看自己课程的章节)。另外,我知道数据何时更改,所以它不需要是反应式的。
当用户提交新增章节的表单时,我只想让Meteor重新加载数据(我目前通过在添加章节后执行window.location.reload()来解决这个问题)。有没有一种在Meteor中实现这样做的方法?
3个回答

8
原来这很简单,现在我感觉有点傻:)
您只需再次调用即可重新加载订阅。当我尝试使用Iron Router导航到不同的URL以重新加载订阅时,我发现了这一点。
因此,在我的提交侦听器中,您可以简单地执行以下操作,而不是执行:
Template.sectionForm.events
    'submit form': ( e ) ->
        e.preventDefault()
        data = SimpleForm.processForm( event.target )

        section = Section.create( data )
        this.course.push( sectionIds: section._id )

        # Reload the subscription to pull in the new section
        params = Router.current().params
        Meteor.subscribe 'course', params.producer, params.course

它将拉取新数据。耶!


严格来说,这是我的问题的答案。但是Mitar建议将主项目的ID放在所有子项目中(预连接,可以这么说)更好,我最终采用了这种方法。 - Rijk

7

如果有人感兴趣,我通过在发布函数中添加observeChanges来修复了它:

Meteor.publish 'course', ( courseId ) ->
    return null if not this.userId

    # [some permission checks]

    courses = Course.find courseId
    sectionIds = _.flatten courses.map ( course ) -> course.sectionIds

    handle = courses.observeChanges
        changed: ( id, fields ) =>
            if _.has fields, 'sectionIds'
                addedIds = _.difference fields.sectionIds, sectionIds
                removedIds = _.difference sectionIds, fields.sectionIds
                sectionIds = fields.sectionIds

                _.each addedIds, ( id ) => @added 'sections', id, Section.first id
                _.each removedIds, ( id ) => @removed 'sections', id

    @onStop -> handle.stop()

    sections = Section.find _id: $in: sectionIds

    [ courses, sections ]

观察者检查sectionIds属性的更改,当发生更改时,在订阅上调用addedremoved方法。这使得连接变得反应灵敏;现在将ID添加到courses.sectionIds属性中,新的部分文档会自动推送到客户端。

2
很好。我仍然觉得这是一项艰苦的工作,而这个功能应该在核心中。我希望它能够进入1.0版本。 - Benjamin Crouzier
3
我知道...Meteor很棒,直到你遇到它无法做到的事情。;) - Rijk
@DanDascalescu,你确定链接是正确的吗?似乎没有指向任何倡议。 - Rijk
@pinouchon: 如果您想在核心中实现此功能,您可能希望加入 fork Meteor 这个倡议 - Dan Dascalescu

3
您可以使用meteor-related 包来完成此操作:
Meteor.publish 'course', (courseId) ->
  # verify that courseId is really an ID and not {} or something else

  return unless @userId

  @related (course) ->
    # [some permission checks]

    Section.find
      _id:
        $in: course.sectionIds
  ,
    Course.find courseId

我假设在这里courseId只匹配一个课程。因此,与Meteor相关的内容仅适用于一对多关系。在您上面的代码中,您执行了一些多对多操作。
另一种方法是,在所有这些文档(问题、选项、步骤等)中,您都有一个主ID,即课程ID。然后,您只需基于课程ID筛选所有这些文档,并将其发布到客户端,然后在客户端以任何所需的层次结构显示它们。因此,您不是在读取时间计算关系,而是在写入时间计算关系,并将该关系的ID存储在您感兴趣的所有文档中。
免责声明:我是此软件包的作者。

你也可以看一下PeerDB包。 - Mitar
也许我还没有测试过嵌套关系。我认为你应该重新考虑这种方法。即使你让它工作,这意味着通过创建一个订阅所做的查询数量是巨大的,如果你有n个部分,那么你将为步骤进行n次查询,除非有一些额外的连接魔法和查询优化。但问题是:如果你想使用连接,为什么要使用MongoDB?或者说:为什么要在MongoDB中使用这样的模式?我认为你正在使用关系模式。尝试简单地使用子文档和数组。MongoDB的整个理念就是让它执行一个查询。 - Mitar
我知道,那是我开始的方式,但它完全失败了 :/ 更新步骤和部分非常困难(请参见http://stackoverflow.com/questions/25548548/how-to-query-and-update-nested-arrays)。然后我查看了crowducate.me的源代码并复制了他们的方法(为每个对象类型分别使用集合 - 确实很像RDB)。如果我漏掉了什么,请告诉我 :) - Rijk
正如您在我的评论中链接的问题中所读到的那样,子文档数组在更新其中一个子文档时出现了问题。例如:在具有ID“y”的部分中设置具有ID“x”的步骤的标题。我无法弄清楚如何做到这一点,因为您只能使用$运算符一次。由于该问题仍然没有答案,显然SO的其他人也无法解决这个问题。 - Rijk
2
Mitar,你是我的英雄。它完全可行,并且是响应式的。非常好的建议,再次感谢! - Rijk
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接