将模板绑定到Meteor中的ReactiveVar数组

时间:2018-10-09 04:54:08

标签: rest http meteor callback reactive

我有以下情况:

我有以下模板:

<ul>
  {{#each persons}}
     {{Name}}
  {{/each}}
</ul>

模板.js文件中的persons = ReactiveVar([])。 我正在更新HTTP Rest API的回调中的persons变量:

var instance = Template.instance();
API(url, (error, result) = instance.persons.set(result)) //result is an array

UI上没有任何反应。我怎样才能解决这个问题? (我也愿意使用简单的数组,但是条件是从API回调中填充数组)。

1 个答案:

答案 0 :(得分:0)

将外部API绑定到模板可以通过经典的Template实例的ReactiveVar / ReactiveDict(让我们将它们称为反应性源)来解决。请注意,您不应在帮助程序中对事件源进行这些调用或更新,而应在事件内或onCreated内进行。

让我们来看看您的模板:

<ul>
  {{#each persons}}
     {{Name}}
  {{/each}}
</ul>

然后我们在onCreated函数中进行调用:

Template.myTemplate.onCreated(function () {
  const instance = this
  instance.state = new ReactiveDict()
  instance.state.set('persons', [])

  // Template's internal tracker
  instance.autorun(() => {

    API(url, (error, result) => instance.state.set('persons', result)) //result is an array
  })
})

并且仅通过助手中的反应性源返回数据:

Template.myTemplate.helpers({
  persons() {
    return Template.instance().state.get('persons')
  }
})

现在,这带来了另一个问题:外部API通常不响应,如果外部API中的数据已更改,则autorun会再次触发。如果源是Mongo集合,则模板的内部跟踪器将自动重新运行并更新您的persons状态。

如果您只想获取一次外部数据,那很好。但是,为了scan进行更改的外部api,您有一些不同的选择:

简便方法:使用计时器(setInterval):

let timerId

Template.myTemplate.onCreated(function () {
  const instance = this
  instance.state = new ReactiveDict()
  instance.state.set('persons', [])

  timerId = setInterval(() => {
    API(url, (error, result) => instance.state.set('persons', result)) //result is an array
  }, 5000) // scans each 5 seconds for updates
})

Template.myTemplate.onDestroyed(function () {
  if (timerId) {
    clearInterval(timerId)
    timerId = null
  }
})

专业人士

  • 易于实现
  • 精细的定时调整以获得流畅的体验

缺点

  • setInterval是sink
  • 您必须清理它以防止内存泄漏(在onDestroyed中)

方法:让外部API的服务呼叫您!

如果您可以选择让外部服务connect and call your app via ddp,则可以让外部服务决定何时更改数据并准备启动数据,以便您当前的应用可以自动更新。

您需要一个方法和一个集合:

服务器和客户端:

export const ExternalData = new Mongo.Collection('externalData')

服务器:

import ExternalData from 'path/to/externalData'

Meteor.methods({
  'myApp.updateExternalData'(args) {
     // check permissions...
     // check data integrity...
     const {url} = args
     const {data} = args
     ExternalData.update({url}, {$set: data})
   }
})

Meteor.publish({
 'myApp.externalData'(url) {
   return ExternalData.find({url})
 }
})

现在,在客户端上,您只需要订阅数据并自动更新反应式var:

客户端:

import ExternalData from 'path/to/externalData'

Template.myTemplate.onCreated(function () {
  const instance = this

  // subscribe to changes
  instance.autorun(() => {

    const subscription = this.subscribe('myApp.externalData', url)
    if (subscription.ready()) {
       console.log('myApp.externalData is ready')
    }
  })
})

Template.myTemplate.helpers({
  persons() {
    return ExternalData.find({})
  }
})

外部服务/ APP:

// if the external app is a meteor app you are lucky and can go with:
// https://docs.meteor.com/api/connections.html#DDP-connect
// Otherwise you can use the npm package:
// https://www.npmjs.com/package/ddp
// For authentication you can use:
// https://github.com/reactioncommerce/meteor-ddp-login
// or
// https://www.npmjs.com/package/ddp-login
const connection = // create a ddp connection

function onDataChanged () {
  const data = //... get data from the backend of your ext. servie
  const url  = //... and the url for which the data is relevant

  // call the app to update the data:
  connection.call('myApp.updateExternalData', {url, data})
}

优点:

  • 模板在集合更新时自动更新
  • 没有计时器=没有下沉!
  • 不需要其他反应变量
  • 您可以使用集合使外部数据持久化,对其进行缓存或创建修订/历史记录
  • 您可以插入/拔出外部服务(扩展性更好,依赖性更少)

缺点:

  • 学习曲线高(但值得努力)
  • 仅在您可以控制外部服务时有效
  • 更多代码=更多潜在错误,因此需要编写更多测试