如何回滚 EmberData 中的关系更改

2023-11-24

我有两种亲子关系模型:训练和锻炼:

App.Training = DS.Model.extend({
  exercises: DS.hasMany('App.Exercise')
})

App.Exercise = DS.Model.extend({
  training: DS.belongsTo('App.Training')
})

我想要一个页面,其中显示培训及其所有相关练习。如果用户按下Edit按钮,页面变得可编辑,并且可以添加新的练习。我也想拥有一个Cancel按钮将放弃所做的所有更改。

这是我的控制器:

App.TrainingsShowController = Em.ObjectController.extend({
  editing: false,

  edit: function() {
    this.set('editing', true);
    transaction = this.get('store').transaction();
    transaction.add(this.get('model'));
    this.get('model.exercises').forEach(function(x){
      transaction.add(x);
    });
  },

  cancel: function() {
    this.set('editing', false);
    this.get('model.transaction').rollback();
  },

  save: function() {
    this.set('editing', false);
    this.get('model.transaction').commit();
  },

  addExercise: function() {
    this.get('model.exercises').createRecord({});
  }
})

控制器中有四个事件处理程序:

  1. edit:用户按下Edit按钮:创建交易,页面进入“编辑”模式。
  2. cancel:用户按下Cancel按钮:事务回滚并返回“正常”模式。
  3. save:用户按下Save按钮:事务已提交并返回“正常”模式。
  4. addExercise:用户按下Add exercise按钮:创建一个新练习(在同一事务中)并将其添加到训练中。

除了新创建的记录之外,回滚功能工作正常:如果我推送Edit按钮,添加新练习并按下Cancel按钮,新创建的练习将保留在页面上。

摆脱丢弃的子记录的最佳方法是什么?

UPDATE:

我创建了一个 jsFiddle 来重现问题,但它有效。与我在这里使用的应用程序不同DS.FixtureAdapter: http://jsfiddle.net/tothda/LaXLG/13/

然后我使用创建了另一个DS.RESTAdapter问题出现了:http://jsfiddle.net/tothda/qwZc4/5/

在小提琴中尝试:编辑,添加新的,然后回滚。

我发现,在 RESTAdapter 的情况下,当我将新的子记录添加到hasMany关系,父记录不会变脏。这看起来不错,但是当我回滚事务时,新创建的子记录保留在父记录中ManyArray.

我仍然不知道处理这种情况的最佳方法是什么。


Ember 数据中非常缺乏对 hasMany 和 BelongsTo 关系进行适当的脏检查和回滚。它当前的行为方式经常被报告为错误。这对很多开发人员来说是一个很大的痛点,这里正在讨论如何解决这个问题:

https://github.com/emberjs/rfcs/pull/21

在找到适当的解决方案之前,您可以使用以下方法解决此问题。

首先,您需要重新打开 DS.Model 并扩展它。如果您使用全局变量,则可以将其(例如 DS.Model.reopen({}))放在任何地方,但如果您使用 Ember CLI,最好创建一个初始化程序(例如 ember g 初始化程序模型):

import DS from 'ember-data';

export function initialize(/* container, application */) {

    DS.Model.reopen({

        saveOriginalRelations: function() {

            this.originalRelations = {};
            this.constructor.eachRelationship(function(key, relationship) {

                if (relationship.kind === 'belongsTo')
                    this.originalRelations[key] = this.get(key);

                if (relationship.kind === 'hasMany')
                    this.originalRelations[key] = this.get(key).toArray();

            }, this);
        },

        onLoad: function() {

            this.saveOriginalRelations();

        }.on('didLoad', 'didCreate', 'didUpdate'),

        onReloading: function() {

            if (!this.get('isReloading'))
                this.saveOriginalRelations();

        }.observes('isReloading'),    

        rollback: function() {

            this._super();

            if (!this.originalRelations)
                return;

            Ember.keys(this.originalRelations).forEach(function(key) {

                // careful, as Ember.typeOf for ArrayProxy is 'instance'
                if (Ember.isArray(this.get(key))) {
                    this.get(key).setObjects(this.originalRelations[key]);
                    this.get(key).filterBy('isDirty').invoke('rollback');
                    return;
                }

                if (Ember.typeOf(this.get(key)) === 'instance') {
                    this.set(key, this.originalRelations[key]);
                    return;
                }

            }, this);
        },

        isDeepDirty: function() {
            if (this._super('isDirty'))
                return true;

            if (!this.originalRelations)
                return false;

            return Ember.keys(this.originalRelations).any(function(key) {

                if (Ember.isArray(this.get(key))) {
                    if (this.get(key).anyBy('isDirty'))
                        return true;

                    if (this.get(key).get('length') !== this.originalRelations[key].length)
                        return true;

                    var dirty = false;
                    this.get(key).forEach(function(item, index) {
                        if (item.get('id') !== this.originalRelations[key][index].get('id'))
                            dirty = true;
                    }, this);

                    return dirty;
                }

                return this.get(key).get('isDirty') || this.get(key).get('id') !== this.originalRelations[key].get('id');

            }, this);
        }
    });
};

export default {
    name: 'model',
    initialize: initialize
};

上面的代码本质上存储了加载或更新时的原始关系,以便稍后可以用于回滚和脏检查。

model.rollback() 现在应该回滚所有内容,包括 hasMany 和 BelongsTo 关系。不过,我们还没有完全解决“isDirty”检查问题。为此,我们需要在模型的具体实现中重写 isDirty。我们需要在这里执行此操作而不能在 DS.Model 中通用执行此操作的原因是因为 DS.Model 不知道要监视哪些属性更改。以下是使用 Ember CLI 的示例。相同的方法将用于全局变量,只不过您将此类分配给 App.Book 之类的东西:

import DS from 'ember-data';

var Book = DS.Model.extend({

    publisher: DS.belongsTo('publisher'),

    authors: DS.hasMany('author'),

    isDirty: function() {
        return this.isDeepDirty();
    }.property('currentState', 'publisher', 'authors.[]', '[email protected]').readOnly()

});

export default Book;

对于 isDirty 的依赖参数,请确保包含所有 ownTo 关系,并且还包含“array.[]”和“[电子邮件受保护]' 对于每个 hasMany 关系。现在 isDirty 应该按预期工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何回滚 EmberData 中的关系更改 的相关文章

随机推荐

  • 如何在 Visual Studio 中提取默认控件模板?

    我想知道如何使用 Visual Studio 提取 获取副本 给定控件的默认模板 我知道这可以使用 Expression Blend 来完成 右键单击控件 编辑模板 gt 编辑副本 然后将默认控件模板复制到我的 Xaml 中 但这可以用 V
  • POST 和 PUT 的不同模型要求

    假设我有一个控制器CatController包括 GET POST 和 PUT 操作 他们都使用相同的Cat资源可能如下所示 public class CatDto public int Id get set Required public
  • 为什么 hazelcast 的默认分区数为 271?选择分区数的参数是什么?

    我刚刚浏览了 hazelcast 文档 它表明数据跨所有节点进行分区 默认情况下在集群 271 中创建的分区数 哪些参数控制正确分区计数值的选择 为什么默认分区数是 271 271 是质数 给定任何键 Hazelcast 都会对键进行哈希处
  • Jenkins:开始构建时发送电子邮件通知

    在 Jenkins 中 有没有办法在开始构建时发送邮件 我知道有一个构建后电子邮件通知 它会在构建成功或失败后向给定收件人发送邮件 同样 我期待类似预构建通知之类的东西 还有一个条件电子邮件 例如 if username null 发送至
  • 我的智能手机每秒可以执行多少次 FFT? (用于执行语音识别)

    我正在探索语音识别和 DSP 因此我想在我的智能手机上实现一个简单的声音频率分析仪 我有一部 iPhone 和一部运行 Android 的三星 Nexus S 我以前在Matlab中做过基本的DSP 根据我的理解 我需要执行 FFT 来获取
  • 如何使用 UWP MarkupExtension 类?

    Fall Creators 更新 SDK 添加了标记扩展类 非常棒 https learn microsoft com en us uwp api windows ui xaml markup markupextension 所以我创建了一
  • 创建交互式应用程序原型的最佳方法是什么?

    这个问题应该从一般的角度来解释 而不是仅仅针对网络应用程序或桌面应用程序 我一直在寻找一种简单易用的方法来为 Web 应用程序创建交互式原型 我想使用一种技术 允许简单的 UI 创建 特别是在进一步迭代中重新创建和修改 UI 用模型数据填充
  • android:TimePickerDialog 阻止用户选择过去的时间,并可以选择具有新日期的未来时间

    我正在使用这个链接Android TimePickerDialog 设置最大时间 我是安卓新手 借助此代码 我无法选择过去的时间 但我们无法选择未来的时间 当在时间选择器对话框模式中选择 12 时 根据第二天而不是过去的一天自动更改为 am
  • AngularJS:扩展输入指令

    我想知道是否可以扩展 Angular 的输入指令 我想将一些侦听器附加到页面上的所有输入字段 我认为你可以用以下方式装饰现有模块 provide decorate 但我不知道如何使用指令 更准确地说是输入指令 来做到这一点 那么 有人能把我
  • 如何将wav文件转换为浮动幅度

    所以我问了标题中的所有内容 我有一个 wav 文件 由 PyAudio 从输入音频编写 我想将其转换为与声级 振幅 相对应的浮点数据 以进行一些傅里叶变换等 有人有将 WAV 数据转换为 float 的想法吗 我已经找到了两种不错的方法来做
  • Java中的字符串和字符数组

    我是一名刚从C 转向Java的学生 在 Java 中 为 String 和 Char 数组定义单独的数据类型的主要原因是什么 两者有什么区别 由于我只学过C 到目前为止我的印象是它们是同一件事 如果可能的话请澄清 String是不可变的 C
  • 加载和存储是唯一需要重新排序的指令吗?

    我读过很多关于内存排序的文章 它们都只说 CPU 重新排序加载和存储 CPU 我对 x86 CPU 特别感兴趣 是否只重新排序加载和存储 而不重新排序它拥有的其余指令 乱序执行保留了按程序顺序运行的错觉对于单线程 核心 这就像C C as
  • 停止服务内的线程

    我在服务中有一个线程 我希望能够在按下时停止该线程buttonStop在我的主要活动课上 在我的主要活动课上 我有 public class MainActivity extends Activity implements OnClickL
  • C++中通过引用传递指针

    我有一个函数 将迭代器传递给 char 缓冲区 这也是一个 char 该函数需要递增迭代器 无论如何 我发现将迭代器传递到函数中的一个好方法是通过引用传递指针 bool myFunction unsigned char iter 但是 我听
  • 将 CSV 文件转换为 Lua 中定义键的表

    我正在学习 Lua 来构建飞行模拟器的脚本 我有一个 CSV 文件 如下所示 Poti city Poti red 295731 42857144 617222 85714285 Lanchhuti city Poti red 299217
  • 使用Xcode和SDK构建胖静态库(设备+模拟器)4+

    理论上 我们似乎可以构建一个包含模拟器以及 iPhone 和 iPad 的静态库 然而 Apple 没有我能找到的相关文档 并且 Xcode 的默认模板也没有配置为执行此操作 我正在寻找一种可以在 Xcode 内完成的简单 可移植 可重用的
  • 是否可以在共享内存中存储多态类?

    假设我有课Base and Derived public Base 我使用 boost interprocess 库构建了一个共享内存段 是否可以有类似这样的代码 Base b new Derived write b one app wri
  • 用于 Asp.net Mvc 的 Google Chart HtmlHelper

    是否有任何 HtmlHelper 扩展谷歌图表API 我喜欢使用一些基本图表 例如饼图 条形图 Soe Moe 谷歌说你插入一个像这样的图表 img src alt Sample chart 所以编写一个像这样的 HtmlHelper 应该
  • 在 Java 中,如何解析 xml 模式 (xsd) 以了解给定元素的有效内容?

    我希望能够读取 XML 模式 即 xsd 并在遍历它时从中知道什么是有效属性 子元素和值 例如 假设我有一个 xsd 此 xml 将根据它进行验证
  • 如何回滚 EmberData 中的关系更改

    我有两种亲子关系模型 训练和锻炼 App Training DS Model extend exercises DS hasMany App Exercise App Exercise DS Model extend training DS