【问题标题】:Ember Data model's errors property (DS.Errors) not populatingEmber 数据模型的错误属性 (DS.Errors) 未填充
【发布时间】:2014-07-24 11:46:18
【问题描述】:

我正在使用 Ember Data,但我似乎无法让模型的“错误”属性填充来自我的 REST API 的错误消息。我非常关注本指南中的示例:

http://emberjs.com/api/data/classes/DS.Errors.html

我的应用如下所示:

    window.App = Ember.Application.create();

    App.User = DS.Model.extend({
        username: DS.attr('string'),
        email: DS.attr('string')
    });

    App.ApplicationRoute = Ember.Route.extend({
        model: function () {
            return this.store.createRecord('user', {
                username: 'mike',
                email: 'invalidEmail'
            });
        },

        actions: {
            save: function () {
                this.modelFor(this.routeName).save();
            }
        }
    });

我的 API 会返回这个:

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 125

{
  "errors": {
    "username": ["Username is taken!"],
    "email": ["Email is invalid."]
  }
}

在模型上调用 save() 后,我在用户模型上看到的内容如下:

user.get('isError') // true
user.get('errors.messages') // []

即使模型正确注册了 isError 属性,我似乎也无法获取要填充的错误消息。我怎样才能让它工作?我正在开发 Ember Data 版本 1.0.0-beta.8.2a68c63a 的最新 beta 版本

【问题讨论】:

  • 你在使用 ActiveModelAdapter 吗?
  • 不,我使用的是默认的 DS.RESTAdapter。至少那是我认为我正在使用的。我的所有 JavaScript 代码都列在我的问题中。如果我不指定任何内容,我认为 Ember Data 默认为 DS.RESTAdapter。

标签: ember.js ember-data


【解决方案1】:

这方面肯定缺少文档,除非您使用活动模型适配器,否则不会填充错误。

这是一个工作示例,也可以查看 Ember: error.messages does not show server errors on save 我说同样的话

http://jsbin.com/motuvaye/24/edit

您可以通过覆盖 ajaxError 并复制活动模型适配器的操作方式,在 RESTAdapter 上相当轻松地实现它。

App.ApplicationAdapter = DS.RESTAdapter.extend({

  ajaxError: function(jqXHR) {


    var error = this._super(jqXHR);

    if (jqXHR && jqXHR.status === 422) {
      var response = Ember.$.parseJSON(jqXHR.responseText),
          errors = {};

      if (response.errors !== undefined) {
        var jsonErrors = response.errors;

        Ember.EnumerableUtils.forEach(Ember.keys(jsonErrors), function(key) {

          errors[Ember.String.camelize(key)] = jsonErrors[key];
        });
      }
      return new DS.InvalidError(errors);
    } else {
      return error;
    }
  }
});

http://jsbin.com/motuvaye/27/edit

https://github.com/emberjs/data/blob/v1.0.0-beta.8/packages/activemodel-adapter/lib/system/active_model_adapter.js#L102

【讨论】:

  • 哇,我只是在这上面浪费了几个小时。感谢您澄清这个问题。你知道这是否只是 ActiveModelAdapter 设计的一个特性吗?还是他们计划将其移植到 RESTAdapter?
  • 它也可能是,我认为它最初是由于 Rails 活动模型适配器的内置特性(我不确定,我不使用它)至少它也可以记录在案,在其余适配器上实现相当简单。我将添加上面的代码。
  • 在您的 jsbin 示例中,您知道为什么 Ember Data 完全忽略了错误对象的“base”:“General error message here”部分吗?似乎它只关注属于模型一部分的属性,这真是一种耻辱。
  • 是的,目前它只允许与模型上定义的属性或关系同名的错误(超级奇怪),但 github.com/emberjs/data/pull/1984
  • 我在你的 jsbin 中添加了一个删除选项。你知道为什么当删除从服务器返回错误时 isSaving 仍然为真吗?您可以先单击“创建”,然后单击此处的“删除”来测试:jsbin.com/motuvaye/32/edit
【解决方案2】:

我在使用 Ember Data 的 errors.messages 属性方面有过一段漫长而令人沮丧的经历,所以我想我会在这里总结一下我的所有发现,以防其他人尝试使用此功能。

1) 文档已过期

正如@kingpin2k 在他的回答中提到的,http://emberjs.com/api/data/classes/DS.Errors.html 的文档已过时。他们在该页面上提供的示例仅在您使用 DS.ActiveModelAdapter 时才有效。如果您使用默认的 DS.RESTAdapter,那么您需要执行类似的操作。请注意,我更喜欢这种更简单的方法,而不是仅仅复制 ActiveModelAdapter 的 ajaxError 实现:

App.ApplicationAdapter = DS.RESTAdapter.extend({
    ajaxError: function (jqXHR) {

        this._super(jqXHR);

        var response = Ember.$.parseJSON(jqXHR.responseText);
        if (response.errors)
            return new DS.InvalidError(response.errors);
        else
            return new DS.InvalidError({ summary: 'Error connecting to the server.' });
    }
});

2) 您必须提供拒绝回调

这很奇怪,但是当你在你的模型上调用 save() 时,你需要提供一个拒绝回调,否则,你会得到一个未捕获的“后端拒绝提交”异常,并且 JavaScript 将停止执行。我不知道为什么会这样。

没有拒绝回调的示例。这将导致异常:

    user.save().then(function (model) {
        // do something
    });

以拒绝回调为例。一切都会好起来的:

    user.save().then(function (model) {
        // do something
    }, function (error) {
        // must supply reject callback, otherwise Ember will throw a 'backend rejected the commit' error.
    });

3) 默认情况下,只有属于模型的错误属性才会在 errors.messages 中注册。 例如,如果这是您的模型:

App.User = DS.Model.extend({
    firstName: DS.attr('string'),
    lastName: DS.attr('string')
});

...如果这是您的错误负载:

{
    "errors": {
        "firstName":"is required",
        "summary":"something went wrong"
    }
}

那么summary就不会出现在user.get('errors.messages')中。这个问题的根源可以在 Ember Data 的 adapterDidInvalidate 方法中找到。它使用 this.eachAttribute 和 this.eachRelationship 将错误消息的注册限制在模型中。

  adapterDidInvalidate: function(errors) {
    var recordErrors = get(this, 'errors');
    function addError(name) {
      if (errors[name]) {
        recordErrors.add(name, errors[name]);
      }
    }

    this.eachAttribute(addError);
    this.eachRelationship(addError);
  }

这里有关于这个问题的讨论:https://github.com/emberjs/data/issues/1877

在 Ember 团队解决此问题之前,您可以通过创建自定义基础模型来解决此问题,该模型覆盖默认的 adapterDidInvalidate 实现,并且您的所有其他模型都继承自它:

基础模型:

App.Model = DS.Model.extend({
    adapterDidInvalidate: function (errors) {
        var recordErrors = this.get('errors');
        Ember.keys(errors).forEach(function (key) {
            recordErrors.add(key, errors[key]);
        });
    }
});

用户模型:

App.User = App.Model.extend({
    firstName: DS.attr('string'),
    lastName: DS.attr('string')
});

4) 如果您从适配器的 ajaxError(我们在上面覆盖的那个)返回 DS.InvalidError,那么您的模型将卡在“isSaving”状态,您将无法摆脱它。

如果您使用 DS.ActiveModelAdapter,也会出现此问题。

例如:

    user.deleteRecord();
    user.save().then(function (model) {
        // do something
    }, function (error) {

    });

当服务器响应错误时,模型的 isSaving 状态为真,我无法在不重新加载页面的情况下重置它。

更新:2014-10-30 对于任何与 DS.Errors 苦苦挣扎的人,这里有一篇很好的博客文章,很好地总结了这一点:http://alexspeller.com/server-side-validations-with-ember-data-and-ds-errors/

【讨论】:

【解决方案3】:

更新:Ember Data 2.x

上述响应仍然有些相关并且通常非常有用,但现在对于 Ember Data 2.x(在撰写本文时为 v2.5.1)已经过时。使用较新版本的 Ember Data 时需要注意以下几点:

  • DS.RESTAdapter 在 2.x 中不再具有 ajaxError 函数。现在由RESTAdapter.handleResponse() 处理。如果需要对错误进行任何特殊处理或格式化,您可以覆盖此方法。 RESTAdapter.handleResponse source code
  • DS.ErrorsDS.Model.errors(它是 DS.Errors 的一个实例)的文档目前有点误导。 仅当响应中的错误符合 JSON API 错误对象规范时才有效。这意味着如果您的 API 错误对象遵循任何其他格式,它将完全没有帮助或可用。不幸的是,此行为目前无法像 Ember Data 中的许多其他内容一样被很好地覆盖,因为此行为是在 DS.Model 中 Ember 的 InternalModel 类中的私有 API 中处理的。
  • DS.InvalidError 只会在响应状态码默认为422 时使用。如果您的 API 使用不同的状态代码来表示无效请求的错误,您可以覆盖 RESTAdapter.isInvalid() 以自定义要检查哪些状态代码(或错误响应的其他部分)代表 InvalidError
  • 作为替代方案,您可以覆盖 isInvalid() 以始终返回 false,以便 Ember Data 始终创建更通用的 DS.AdapterError instead。然后在DS.Model.adapterError 上设置此错误,并且可以从那里根据需要加以利用。
  • DS.AdapterError.errors 包含 API 响应的 errors 键上返回的任何内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 2014-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多