简体   繁体   English

Ember.Object实例中的必需属性(构造函数args)

[英]Required properties (constructor args) in Ember.Object instances

In Ember, let's say I have an object called FoodStuff that has a few properties: 在Ember中,假设我有一个名为FoodStuff的对象,它有一些属性:

export default Ember.Object.extend({
    name: null,     // REQUIRED: 'Slice of Apple Pie'
    calories: null, // OPTIONAL: int: eg. 250
    category: null, // REQUIRED: 'Pastry'
    rating: null    // OPTIONAL: int: 1-5
});

How can I write a 'constructor' in Ember, requiring that the 'name' and 'category' properties be provided at instantiation-time? 如何在Ember中编写“构造函数”,要求在实例化时提供“名称”和“类别”属性?

Angular seems to approach this with fairly straightforward syntax: Angular似乎用相当简单的语法来解决这个问题:

.factory('User', function (Organisation) {

  /**
   * Constructor, with class name
   */
  function User(firstName, lastName, role, organisation) {
    // Public properties, assigned to the instance ('this')
    this.firstName = firstName;
    ...

Angular model objects with JavaScript classes 具有JavaScript类的Angular模型对象

Does Ember have something similar? Ember有类似的东西吗? Currently all my classes are as seen at the top, with a bunch of initially null properties that might or might not be properly set by the caller. 目前我的所有类都在顶部看到,有一堆初始为null的属性,调用者可能正确设置也可能没有 At build time (I'm using ember-cli ) I would like for changes in constructor requirements to be caught downstream by the ember build phase with JSHint. 在构建时(我正在使用ember-cli )我希望通过ember build阶段将构造函数需求的更改捕获到下游。

As far as I know, there is no native way to do this in Ember. 据我所知,在Ember中没有本地方法可以做到这一点。 But there's nothing impossible! 但没有什么是不可能的! You can tweak Ember a bit to handle the case. 你可以调整Ember来处理这个案子。 Just add an initializer: 只需添加初始化程序:

/initializers/extend-ember.js : /initialsizers/extend-ember.js

import Ember from 'ember';

export function initialize() {

  Ember.Object.reopen({

    /**
     * @prop {Array} - array of required properties
     */
    requiredAttrs: [],

    /**
     * Validates existance of required properties
     *
     * @param {String} attr - attribute name
     * @param {*} value - value of the property
     * @throws {Error} in case when required property is not set
     */
    _validateExistance(attr, value) {
      if (this.requiredAttrs.contains(attr) && typeof value === "undefined") {
        throw new Error("Attribute " + attr + " can't be empty!");
      }
    },

    /**
     * Sets value of a property and validates existance of required properties
     *
     * @override 
     */
    set(key, value) {
      this._validateExistance(key, value);
      return this._super(key, value);
    }

  });

  Ember.Object.reopenClass({

    /**
     * Creates object instance and validates existance of required properties
     *
     * @override
     */
    create(attrs) {
      let obj = this._super(attrs);
      if (attrs) {
        obj.requiredAttrs.forEach((key) => {
          obj._validateExistance(key, attrs[key]);
        });
      }
      return obj;
    }

  });

}

export default {
  name: 'extend-ember',
  initialize: initialize
};

Then you can use requiredAttrs property on any class to define which properties are required. 然后,您可以在任何类上使用requiredAttrs属性来定义所需的属性。 It will throw an exception if you try to create an instance with empty required properties or if you try to set an empty value to required property. 如果您尝试创建具有空必需属性的实例,或者尝试将空值设置为required属性,则会抛出异常。

let MyModel = Ember.Object.extend({
  prop1: null,
  prop2: null,
  requiredAttrs: ['prop2']
});

let ChildModel = MyModel.extend({
  prop3: null,
  requiredAttrs: ['prop2', 'prop3']
});

// throws exception
let obj1 = MyModel.create({
  prop1: 'val1'
});

// no exception
let obj2 = MyModel.create({
  prop2: 'val2'
});

// throws exception
obj2.set('prop2', undefined);

// throws exception
let obj3 = ChildModel.create({
  prop3: 'val3'
});

// no exception
let obj4 = ChildModel.create({
  prop2: 'val2',
  prop3: 'val3'
});

It will also work on DS.Model and other Ember entities out of the box, since they all extend Ember.Object . 它也适用于DS.ModelDS.Model其他Ember实体,因为它们都扩展了Ember.Object

To have instantiation-time (runtime) checks, you want to override the init method: 要进行实例化时间(运行时)检查,您需要覆盖init方法:

init() {
    this._super();
    Ember.assert('Name cannot be empty', name);
    // ...
}

For them to be caught at build time, I'm not sure of a good way to do that. 为了让他们在建设时被抓住,我不确定这样做的好方法。 You can use some kind of tool to add static typing to Javascript (Typescript and Flow come to mind), which might be able to force those properties when instantiating those objects. 您可以使用某种工具向Javascript(Typescript和Flow想到)添加静态类型,这可能会在实例化这些对象时强制执行这些属性。 You could also write a custom tool (like an ESLint plugin) to check for that, but that's probably more difficultly than what it's worth. 您也可以编写一个自定义工具(如ESLint插件)来检查它,但这可能比它的价值更难。 You're probably best off with a runtime solution and decent test coverage. 您最好使用运行时解决方案和体面的测试覆盖率。


Here's an example of having your own create() method. 这是一个拥有自己的create()方法的例子。

SomeClass.reopenClass({
    create(name, category, calories = null, rating = null) {
        // Validate your properties
        Ember.assert('Name cannot be empty', name);
        // ...

        // Create the object
        var obj = this._super({ name, category, calories, rating });

        // Do any other setup/validation

        // Return the object
        return obj;
    }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM