MobX装饰类会导致函数引发TypeError

时间:2019-04-25 03:01:44

标签: mobx

我有两个要在商店中使用的数据类(我没有写的业务逻辑)。我通过装饰来解决该商店中的问题。它们定义为:

// data classes; // these would be in separate file. 
class PayRate {
  /**
   * @param {number} storeId The store id of the pay rate. (-1 for all)
   * @param {number} empId The id of the employee. (-1 for all)
   * @param {number} taskId The id of the task. (-1 for all)
   * @param {Date} effectiveDate The date the payrate goes in effect.
   * @param {number} rate The rate of pay.
   */
  constructor(storeId, empId, taskId, effectiveDate, rate) {
    this.StoreId = storeId || -1;
    this.EmpId = empId || -1;
    this.TaskId = taskId || -1;
    this.EffectiveDate = effectiveDate ? new Date(effectiveDate) : new Date();
    this.Rate = rate || 0.00;
    this.OriginalObject = $.extend(true, {}, this);
  }

  /**
   * Gets a readable version of the effective date.
   * @returns {string} A -m/dd/yyyy representation of the effective date.
   */
  GetReadableDate() {
    return this.EffectiveDate.toLocaleDateString("en-US");
  }
  /**
   * Gets a one line description of the pay rate.
   * @returns {string} A string in the form of (date) - payrate.
   */
  GetDescription() {
    return `(${this.GetReadableDate()}) - $${this.Rate.toFixed(2)}`;
  }
  /**
   * Gets a one line description of the pay rate.
   * Does the exact same as GetDescription(), but is overload of Object.prototype.toString, which allows for stringification of Objects
   * @returns {string} A string in the form of (date) - payrate.
   */
  toString() {
    return `(${this.GetReadableDate()}) - $${this.Rate.toFixed(2)}`;
  }
  /**
   * Tells whether a pay rate was changed or not.
   * @returns {boolean} A boolean saying whether or not the pay rate was changed.
   */
  IsChanged() {
    if (this.EffectiveDate.getTime() !== this.OriginalObject.EffectiveDate.getTime()) {
      return true;
    }
    if (this.Rate != this.OriginalObject.Rate) {
      return true;
    }
    if (this._deleted) {
      return true;
    }
    return false;
  }
  /**
   * Reverts the changes back to the original.
   */
  RevertChanges() {
    $.extend(true, this, this.OriginalObject);
  }
}

mobx.decorate(PayRate, {
  StoreId : mobx.observable,
  EmpId : mobx.observable,
  TaskId : mobx.observable,
  EffectiveDate : mobx.observable,
  Rate : mobx.observable,
})

class TaskObject {
  /**
   * @param {Task} task The task that is being kept track of.
   * @param {PermissionLink[]} permissionLinks A list of permission links that are being kept track of.
   * @param {PayRate[]} payrates A list of pay rates that are being kept track of.
   */
  constructor(task, permissionLinks = [], payrates = []) {
    this.Model = $.extend(true, {}, task);
    this.Model.Type = 25;
    this.PermissionLinks = $.extend(true, [], permissionLinks);
    this.Payrates = $.extend(true, [], payrates);
    this.OriginalObject = $.extend(true, {}, this);
  }
  /**
   * Gives the dirty status of the task object.
   * @returns {boolean} Tells whether or not the TaskObject has been changed.
   */
  IsChanged() {
    if (this.Model.Id == -1) {
      return true;
    }
    if (this.Model.Name != this.OriginalObject.Model.Name) {
      return true;
    }
    if (this.Model.GroupName != this.OriginalObject.Model.GroupName) {
      return true;
    }
    if (this.Model.DescStr != this.OriginalObject.Model.DescStr) {
      return true;
    }
    if (this.Model.IsActive != this.OriginalObject.Model.IsActive) {
      return true;
    }
    if (this.PermissionLinks.length != this.OriginalObject.PermissionLinks.length) {
      return true;
    }
    for (let i = 0; i < this.PermissionLinks.length; i++) {
      const element = this.PermissionLinks[i];
      const compElement = this.OriginalObject.PermissionLinks[i];
      if (JSON.stringify(element) !== JSON.stringify(compElement)) {
        return true;
      }
    }
    for (let i = 0; i < this.Payrates.length; i++) {
      const payrate = this.Payrates[i];
      if (payrate.IsChanged()) {
        return true;
      }
    }
    return false
  }
  /**
   * Reverts the changes that are on the task object.
   * @returns {TaskObject} The TaskObject with its changes discarded.
   */
  RevertChanges() {
    this.Model = $.extend(true, {}, this.OriginalObject.Model);
    this.PermissionLinks = $.extend(true, [], this.OriginalObject.PermissionLinks);
    for (let i = 0; i < this.Payrates.length; i++) {
      this.Payrates[i].RevertChanges()
    }
    return this;
  }

  /**
   * This is here for debugging purposes (i.e. with data stores that use it) and may be overwritten with business logic at any time
   */
  toString() { 
    return JSON.stringify(this, null, '\t')
  }
}

// marking all the properties of TaskObject observable
mobx.decorate(TaskObject, {
  Model : mobx.observable,
  PermissionLinks : mobx.observable,
  Payrates : mobx.observable,
  RevertChanges : mobx.action,
  IsChanged : mobx.computed
})

现在,执行new PayRate().IsChanged()可以按预期工作,但是new TaskObject.IsChanged()会导致:

Uncaught TypeError: Cannot read property 'call' of undefined
    at e.computeValue (mobx.umd.min.js:1)
    at e.get (mobx.umd.min.js:1)
    at e.read (mobx.umd.min.js:1)
    at TaskObject.get (mobx.umd.min.js:1)
    at pen.js:163
e.computeValue @ mobx.umd.min.js:1
e.get @ mobx.umd.min.js:1
e.read @ mobx.umd.min.js:1
get @ mobx.umd.min.js:1
(anonymous) @ pen.js:163

Here is demo

为什么会这样,我该如何解决?

1 个答案:

答案 0 :(得分:0)

您将IsChanged声明为函数,但将其修饰为computed属性,然后将其调用为函数。如果希望将其作为计算属性,则需要:

  • 将其转换为吸气剂:IsChanged() -> get IsChanged()
  • 不带括号将其调用:new TaskObject.IsChanged()