【问题标题】:Meteor Blaze order sub-documents by sub-document propertyMeteor Blaze 按子文档属性排序子文档
【发布时间】:2016-10-07 18:21:42
【问题描述】:

简介:

_id: Pe0t3K8GG8,
videos: [
   {id:'HdaZ8rDAmy', url:'VIDURL', rank: 2},
   {id:'22vZ8mj9my', url:'VIDURL2', rank: 0},
   {id:'8hyTlk8H^6', url:'VIDURL3', rank: 1},
]

个人资料与视频列表一起显示。我有一个拖放,它使用服务器方法更新视频排名。

1) 数据库在 Drop 时正确更新。

2) 对视频数组进行排序 - 我在 Profile Template 上声明了一个助手,并基于 SORT videos 数组声明了一个 >自定义比较功能

Template.Profile.helpers({
    'videosSorted': function(){
        let videos = (this.videos);
        let videosSorted = videos.sort(function(a, b) {
        return parseFloat(a.rank) - parseFloat(b.rank);
    });
    return videosSorted;
  }
});

问题:

A) 在 Blaze 中,{{#each videosSorted}} 不会被动更新。 如果我 F5 刷新然后我可以看到新订单。

我认为问题在于我提供的 videosSorted 不会更新数据库中文档的更改。

如何使 videosSorted 具有响应性?

更新:

所有相关代码: Iron Router Controller - 我订阅并设置布局的数据上下文

ProfileController = RouteController.extend({
 subscriptions: function() {
    this.subscribe('profile',this.params.slug).wait();
  },
  data: function () {
     //getting the data from the subscribed collection
     return Profiles.findOne({'slug':this.params.slug});
  },
})

出版:

Meteor.publish('profile', function (slug) {
  const profile = Profiles.find({"slug":slug});
  if(profile){
    return profile;
  }
  this.ready();
});

个人资料 HTML 模板:

<template name="Profile">
     <ul  class="sortlist">
        {{#each videosSorted}}
            {{> Video}}
        {{/each}}
     </ul>
</template>

我正在使用 mrt:jquery-ui - 可排序功能

Template.Profile.onRendered(function () {
  thisTemplate = this;

  this.$('.sortlist').sortable({
    stop: function(e, ui) {

      el = ui.item.get(0);
      before = ui.item.prev().get(0);
      after = ui.item.next().get(0);

      if(!before) {
        newRank = Blaze.getData(after).rank - 1
      } else if(!after) {
        newRank = Blaze.getData(before).rank + 1
      }
      else {
        newRank = (Blaze.getData(after).rank +
          Blaze.getData(before).rank) / 2
      }

      let queryData = {
          _id: thisTemplate.data._id,    //the id of the profile record
          videos_objId: Blaze.getData(el).objId,    //the id of the sub document to update
          new_rank: newRank  //the new rank to give it
      };

      //Update the sub document using a server side call for validation + security  
      Meteor.call("updateVideoPosition", queryData, function (error, result) {
          if(!result){
            console.log("Not updated");
          }
          else{
            console.log("successfully updated Individual's Video Position")
          }
      });
   }
 })
});

最后是进行更新的 Meteor 方法

'updateVideoPosition': function (queryData){
    let result = Individuals.update(
      {_id: queryData._id, 'videos.objId': queryData.videos_objId },
      { $set:{ 'videos.$.rank' : queryData.new_rank } }
    )
    return result;
  }

注意:

正如我所提到的 - 数据库正确更新 - 如果我有一个隐身窗口打开到同一页面 - 我会重新看到视频(神奇!)切换到新顺序。

架构

const ProfileSchema = new SimpleSchema({
  name:{
    type: String,
  }
  videos: {
    type: [Object],
    optional:true,
  },
  'videos.$.url':{
    type:String,
  },
  'videos.$.rank':{
    type:Number,
    decimal:true,
    optional:true,
    autoform: {
      type: "hidden",
    }
  },
  'videos.$.subCollectionName':{
    type:String,
    optional:true,
    autoform: {
      type: "hidden",
    }
  },
  'videos.$.objId':{
    type:String,
    optional:true,
    autoform: {
      type: "hidden",
    }
  } 
});

【问题讨论】:

  • 如果你这样做let videos = this.videos.slice(),它会起作用吗?
  • 您的收藏是如何定义的?
  • 所有助手都是响应式的并且依赖于currentData()。所以,你的助手应该被动地工作。可能您在以前的模板级别上失去了反应能力。能把视频文档相关的js/html代码全部贴出来吗?
  • @Tdm 我已经更新了问题,在 Update 标题下
  • 好的。诊断。在对 mongo 进行异步更新之前调用 videosSorted 助手。方法返回后如何调用该助手手动运行。

标签: javascript arrays sorting meteor reactive-programming


【解决方案1】:

我想出了非常粗略的解决方案,但我现在看不到其他选择。我能想到的最简单的解决方案是手动重新渲染模板:

Template.Profile.onRendered(function () {
  var self = this;
  var renderedListView;
  this.autorun(function () {
    var data = Template.currentData(); // depend on tmeplate data
    //rerender video list manually
    if (renderedListView) {
      Blaze.remove(renderedListView);
    }
    if (data) {
      renderedListView = Blaze.renderWithData(Template.VideoList, data, self.$('.videos-container')[0]);
    }
  });
});
Template.VideoList.onRendered(function () {
  var tmpl = this;
  tmpl.$('.sortlist').sortable({
    stop: function (e, ui) {
      var el = ui.item.get(0);
      var before = ui.item.prev().get(0);
      var after = ui.item.next().get(0);
      var newRank;
      if (!before) {
        newRank = Blaze.getData(after).rank - 1
      } else if (!after) {
        newRank = Blaze.getData(before).rank + 1
      }
      else {
        newRank = (Blaze.getData(after).rank +
          Blaze.getData(before).rank) / 2
      }
      let queryData = {
        _id: tmpl.data._id,    //the id of the profile record
        videos_objId: Blaze.getData(el).objId,    //the id of the sub document to update
        new_rank: newRank  //the new rank to give it
      };
      //Update the sub document using a server side call for validation + security
      Meteor.call("updateVideoPosition", queryData, function (error, result) {
        if (!result) {
          console.log("Not updated");
        }
        else {
          console.log("successfully updated Individual's Video Position")
        }
      });
    }
  });
});
Template.VideoList.helpers({
  videosSorted: function () {
    return this.videos.sort(function (a, b) {
      return a.rank - b.rank;
    });
  }
});

还有 HTML:

<template name="Profile">
  <div class="videos-container"></div>
</template>

<template name="VideoList">
  <ul class="sortlist">
    {{#each videosSorted}}
      <li>{{url}}</li>
    {{/each}}
  </ul>
</template>

由于 JQuery UI 可排序,在您的情况下失去了反应性。它对 Meteor 的反应性一无所知,只是阻止模板重新渲染。

也许您应该考虑使用更适合 Meteor 的东西,例如 this(我不确定它是否符合您的需求)。

【讨论】:

    猜你喜欢
    • 2017-10-07
    • 2014-06-23
    • 2014-08-14
    • 2013-10-21
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 2013-04-18
    相关资源
    最近更新 更多