【问题标题】:EmberJS 2.7 = has_many configuration for Ember-Data and Active Model Serializers, using Ember-Power-Select (and side loaded, not embedded data)EmberJS 2.7 = 用于 Ember-Data 和 Active Model Serializers 的 has_many 配置,使用 Ember-Power-Select(以及侧面加载,而不是嵌入数据)
【发布时间】:2016-09-15 08:27:09
【问题描述】:

这是一个与 this one 类似的问题,但这是针对最新版本的 Ember 和 Active Model Serializers (0.10.2)。

我有一个简单的父子关系。

app/models/trail.js

import Ember from 'ember';
import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr(),

  // relationships
  employees: DS.hasMany('employee', { async: true }),

});

app/models/employee.js

import DS from 'ember-data';

import Person from '../models/person';

export default Person.extend({
  status: DS.attr(),
  statusCode: DS.attr(),
});

app/models/person.js

import Ember from 'ember';
import DS from 'ember-data';

export default DS.Model.extend({
  avatarUrl: DS.attr(),
  firstName: DS.attr(),
  lastName: DS.attr(),

  fullName: Ember.computed('firstName', 'lastName', function() {
    return `${this.get('lastName')}, ${this.get('firstName')}`;
  }),
});

当我创建一个新的 Trail 并为“hasMany”选择两个员工时,以下 json 到达服务器(来自 Rails 日志):

{"data":
  {"attributes":
    {"name":"TEST3", 
      "gpx-file-url":"a url", 
      "distance-value":"5"}, 
      "relationships":
         {"employees":{"data":[]}}, "type":"trails"}}

我的问题是,员工发生了什么事?员工的 ID 在哪里(它们已经存在于数据库和 Ember 商店中 - 即,我没有尝试在此请求中创建子记录)。

编辑

我刚刚发现this 问题,它解释了 hasMany 关系的 id 不是由 Ember 的 JSONAPISerializer 发送到 API - 因为这里的外键实际上必须保留在每个子记录中。所以基本上通过“选择”员工,你需要保存他们现在有父母的事实。所以选择的员工记录需要被持久化。

但我的理解是,这一切都是“开箱即用”的,Ember 会自动触发 POST 请求来执行此操作,但情况似乎并非如此。

这就是真正的问题 - 我如何更新这些孩子?


更新 - 随着问题的发展,增加了赏金


经过进一步分析,很明显需要一个新模型 - 作业。所以现在问题更复杂了。

模型结构现在是这样的:


轨迹

有很多任务


员工

有很多任务


作业

属于小径

属于员工


在我的“new Trail”路线中,我使用了奇妙的ember-power-select 让用户选择员工。单击“保存”后,我计划遍历选定的员工,然后创建分配记录(显然,在保存 Trail 本身之前或之后保存它们,不确定哪个最好)。

但问题仍然是,我不知道该怎么做——如何找到“选定”的员工,然后遍历他们以创建任务。

所以,这是我的模板中相关的 EPS 用法:

在 /app/templates/trails/new.hbs 中

  {{#power-select-multiple options=model.currentEmployees 
    searchPlaceholder="Type a name to search"
    searchField="fullName"
    selected=staff placeholder="Select team member(s)"
    onchange=(route-action 'staffSelected') as |employee| 
  }}
  <block here template to display various employee data, not just 'fullName'/>
  {{/power-select-multiple}}

(route-action 是 Dockyard 的一个助手,它只是自动将动作发送到我的路线,效果很好)

这是我的模型:

  model: function () {
    let myFilter = {};
    myFilter.data = { filter: {status: [2,3] } }; // current employees
    return Ember.RSVP.hash({
      trail: this.store.createRecord('trail'),
      currentEmployees: this.store.query('employee', myFilter).then(function(data) {return data}),
    });
  },

  actions: {
    staffSelected (employee) {
      this.controller.get('staff').pushObject(employee);      
      console.log(this.controller.get('staff').length);
    },
  }

我今天才发现我们仍然需要控制器,所以这可能是我的问题!这里是:

import Ember from 'ember';

export default Ember.Controller.extend({
  staff: [] <- I guess this needs to be something more complicated
});

这可行,我看到一个对象被添加到控制台中的数组中。但是后来 EPS 拒绝工作,因为我在控制台中收到此错误:

trekclient.js:91 Uncaught TypeError: Cannot read property 'toString' of undefined(anonymous function) @ trekclient.js:91ComputedPropertyPrototype.get @ vendor.js:29285get @ 

etc....

紧随其后的是:

vendor.js:16695 DEPRECATION: You modified (-join-classes (-normalize-class "concatenatedTriggerClasses" concatenatedTriggerClasses) "ember-view" "ember-basic-dropdown-trigger" (-normalize-class "inPlaceClass" inPlaceClass activeClass=undefined inactiveClass=undefined) (-normalize-class "hPositionClass" hPositionClass activeClass=undefined inactiveClass=undefined) (-normalize-class "vPositionClass" vPositionClass activeClass=undefined inactiveClass=undefined)) twice in a single render. This was unreliable in Ember 1.x and will be removed in Ember 3.0 [deprecation id: ember-views.render-double-modify]

所以我想这是因为文档中的示例仅使用包含字符串的数组,而不是实际的 Ember.Objects。但我不知道如何解决这个问题。

所以,我决定扔掉控制器(哈哈)并发挥创意。

如果我在 Trail 模型中添加一个属性会怎样?该属性基本上可以是收集选定员工的“虚拟”属性。

在 /app/models/trail.js 中

selectedEmps: DS.hasMany('employee', async {false}) 

我将 async 设置为 false,因为我们不会持久化它们,并且在保存新 Trail 之前,我可以再次将其设置为 null。

在 /app/templates/trails/new.js 中

  {{#power-select-multiple options=model.currentEmployees 
    searchPlaceholder="Type a name to search"
    searchField="fullName"
    selected=model.selectedEmps placeholder="Select team member(s)"
    onchange=(action (mut model.selectedEmps)) as |employee| 
  }}
  <block here again/>
  {{/power-select-multiple}}

这行得通,它不会在选择第一个员工后“炸毁”。我可以选择多个并将它们从模板中删除。该控件似乎工作正常,因为它直接改变了“model.selectedEmps”。

现在,我认为这是一个 hack,因为我有两个问题:

  1. 如果我将 'mut' 更改为一个动作,我可以添加更多逻辑,我 无法弄清楚如何访问实际存储在 属性'model.selectedEmps'
  2. 即使我能弄清楚 (1) 我也必须始终确保 'selectedEmps' 在离开这条路线时被清空,否则 下次输入这条路线时,它会记住 之前选择过(因为它们现在在 Ember.Store 中)

基本问题是我可以接受“mut”,但仍然存在这样的问题,即当用户点击“保存”时,我必须弄清楚选择了哪些员工,以便为他们创建分配。

但我不知道如何访问所选内容。也许是这个意大利面怪物可怕的混乱:

    save: function (newObj) {
      console.log(newObj.get('selectedEmps'));
      if (newObj.get('isValid')) {
        let emp = this.get('store').createRecord('assignment', {
          trail: newObj,
          person: newObj.get('selectedEmps')[0]
        })
        newObj.save().then( function (newTrail) {
            emp.save();
            //newTrail.get('selectedEmps')
 //         this.transitionTo('trails');
          console.log('DONE');
        }); 
      }
      else {
        alert("Not valid - please provide a name and a GPX file.");
      }
    },

所以有两个问题需要解决:

  1. 如何获取选定的员工、迭代和创建 作业。
  2. 然后如何将结果保存到 API(使用 Rails 的 JSON-API)。一世 假设 newObj.saveeach assignment.save 会照顾 。

更新


EPS 的开发者好心地指出,动作处理程序接收一个数组,因为我改为使用多选,而不是之前的单选。因此,该操作正在接收当前所选内容的完整数组。哦!

因此,我能够如下更新操作处理程序,它现在成功地将当前选择的员工存储在控制器的人员属性中。更近一步。

  staffSelected(newList) {
      existing.forEach(function(me){
        if (!newList.includes(me)) {
          existing.removeObject(me); // if I exist but the newList doesn't have me, remove me
        }
      });
      newList.forEach(function(me){
        if (!existing.includes(me)) {
          existing.pushObject(me); // if I don't exist but the newList has me, add me
        }          
      });
  }

也许不是交叉 2 个阵列的最佳方式,但这是我在星期六晚上凌晨 4 点最不担心的问题。 :(


最终问题更新 - 如何保存数据?


好的,现在我可以获取选定的员工,我可以创建分配,但仍然无法弄清楚 Ember 需要我保存它们的条件,此保存操作会引发错误:

save: function (newObject) {
      if (newObject.get('isValid')) {
        let theChosenOnes = this.controller.get('theChosenOnes');
        let _store = this.get('store');
        theChosenOnes.forEach(function (aChosenOne) {
          _store.createRecord('assignment', {
            trail: newObject,
            person: aChosenOne,
          });
        });
        newObject.save().then(function (newTrail) {
          newTrail.get('assignments').save().then(function() {
            console.log('DONE');
          });
        });
      }

get(...).save is not a function

【问题讨论】:

  • employee 模型是否与trailbelongsTo 关系?如果是这样,我相信默认情况下记录只存储在belongsTo 端,因为它是 ORM 的规范。
  • 不,没有定义 belongsTo。我应该添加吗?
  • @MikkoPaderes 我添加了“belongsTo”,这导致员工的关系数组从发送到 api 的 json 中删除。这是有道理的,因为您通常不会期望“跟踪”创建端点接收此数组并更新所有这些员工的外键。虽然我可以想象你可能想要的场景。
  • 是的,在某些情况下您会想要这样。对于这些情况,您应该覆盖序列化程序或创建自己的序列化程序。如果您对自定义序列化程序感兴趣,请参阅this,但我建议您尽可能避免使用它,因为这会增加维护方面的负担。
  • 您能否在选择要添加到跟踪中的两名员工的位置添加代码?我认为问题出在那儿,但在我看到代码之前我不能确定。

标签: ember.js ember-data active-model-serializers


【解决方案1】:

您最终更新的问题在于,在 Ember Data 2.x 中,默认情况下关系是异步的,因此从 newTrail.get('assignments') 返回的不是DS.ManyArray,它有一个.save,而是一个PromiseArray ,没有那个。

您需要稍作调整才能执行此操作,因此您在已解决的关系上调用 .save

newObject.save().then(function (newTrail) {
    newTrail.get('assignments').then(assignments => assignments.save()).then(function() {
        console.log('DONE');
    });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多