【问题标题】:Validate hasMany relationship against another hasMany验证 hasMany 与另一个 hasMany 的关系
【发布时间】:2018-05-02 21:10:00
【问题描述】:

我有 3 个模型:type、restriction 和 item。

type 很简单,只有一个id:

app/models/type.js:

import Model from 'ember-data/model';
export default Model.extend({});

一个 restriction 可以有多个 type 描述具有此限制的项目的允许类型:

app/models/restriction.js:

import Model from 'ember-data/model';
import { hasMany } from 'ember-data/relationships';

export default Model.extend({
  allowedTypes: hasMany( "type" )
});

一个item可以有多个type也可以有多个restriction和type 只能是所有限制的允许类型的交集的子集(如果至少有一个限制,那么它必须至少有一种类型)。

我已经使用计算属性对此进行了验证:

app/models/item.js:

import Model from 'ember-data/model';
import { computed } from '@ember/object';
import { hasMany } from 'ember-data/relationships';
import { isEmpty } from '@ember/utils';

const peekHasMany    = attr => ( item => item.hasMany( attr ).ids() );
const hasItems       = array => !isEmpty( array );
const includedIn     = array => ( item => array.indexOf( item ) >= 0 );
const intersectionOf = ( array1, array2, index ) => index >= 0 ? array1.filter( includedIn( array2 ) ) : array2;

export default Model.extend({
  types:        hasMany( "type" ),
  restrictions: hasMany( "restriction" ),
  isValidTypes: computed(
    "types.[]",
    "restrictions.@each.allowedTypes",
    function(){
      let restrictions = this.hasMany( "restrictions" ).value();

      if ( isEmpty( restrictions ) )
      {
        return true;
      }

      let allowed = restrictions
                      .map( peekHasMany( "allowedTypes" ) )
                      .filter( hasItems );

      if ( isEmpty( allowed ) )
      {
        return true;
      }

      let types = this.hasMany( "types" ).ids();
      if ( isEmpty( types ) )
      {
        return false;
      }

      let allowedTypes = allowed.reduce( intersectionOf );
      return types.every( includedIn( allowedTypes ) );
    }
  )
});

这使用DS.Model.hasMany( attributeName ) 来同步获取HasManyReference 用于依赖于正在加载的引用模型的关系。

如何更改计算属性以使用this.get() 异步获取两个属性(和子属性)而不是同步使用this.hasMany()?

【问题讨论】:

  • 您是否考虑过为此使用库?类似 ember-cp-validations 的东西? github.com/offirgolan/ember-cp-validations
  • @JoeHartzell 如果您想整理一个答案,展示如何使用该库异步重新实现我的代码。但是,通过查看文档,自定义验证器只是 JavaScript 函数,因此,我最初的看法是,可以在有或没有库的情况下编写类似的答案(但我很可能遗漏了一些使其特别容易使用的功能图书馆)。
  • 你最初的想法确实是这样。但是没有理由重新发明轮子。他们的库在验证方面提供了很多。仅仅能够在整个组件、路由或模型上调用“isValid”就非常值得实现
  • @JoeHartzell 您似乎忽略了这一点-该库没有提供一种方法来验证一个 hasMany 与另一个 hasMany 因此即使使用该库,也需要编写自定义验证器-我不知道至于验证器是用什么框架编写的 - 问题是如何在验证器中编写异步代码。

标签: ember.js ember-data


【解决方案1】:
let value = this.hasMany( attributeName ).value();
/* following code */

可以替换为

this.get( attributeName ).then( value => { /* following code */ } );

复杂之处在于:

const peekHasMany = attr => ( item => item.hasMany( attr ).ids() );
let allowed = restrictions.map( peekHasMany( "allowedTypes" ) )
/* following code */

当改变时会产生一系列的承诺。这可以使用Promise.all( arrayOfPromises )

封装在一个promise 中
const getAll = attr => ( item => item.get( attr ) );
Promise.all( restrictions.map( getAll( "allowedTypes" ) ) )
  .then( allowed => {
    /* following code */
  } );

然后代码变成:

import Model from 'ember-data/model';
import { computed } from '@ember/object';
import { hasMany } from 'ember-data/relationships';
import { isEmpty } from '@ember/utils';

const getAll         = attr => ( item => item.get( attr ) );
const hasItems       = array => !isEmpty( array );
const includedIn     = array => ( item => array.indexOf( item ) >= 0 );
const intersectionOf = ( array1, array2, index ) => index >= 0 ? array1.filter( includedIn( array2 ) ) : array2;

export default Model.extend({
  types:        hasMany( "type" ),
  restrictions: hasMany( "restriction" ),
  isValidTypes: computed(
    "types.[]",
    "restrictions.@each.allowedTypes",
    function(){
      return this.get( "restrictions" )
        .then( restrictions => {
          if ( isEmpty( restrictions ) )
          {
            return true;
          }

          return Promise.all( restrictions.map( getAll( "allowedTypes" ) ) )
            .then( allowed => {
              allowed = allowed.filter( hasItems );
              if ( isEmpty( allowed ) )
              {
                return true;
              }

              return this.get( "types" )
                .then( types => {
                  if ( isEmpty( types ) )
                  {
                    return false;
                  }

                  let allowedTypes = allowed.reduce( intersectionOf );
                  return types.every( includedIn( allowedTypes ) );
                } );
            } );
        } );
    }
  )
});

或者使用async 和await 语法:

import Model from 'ember-data/model';
import { computed } from '@ember/object';
import { hasMany } from 'ember-data/relationships';
import { isEmpty } from '@ember/utils';

const getAll         = attr => ( item => item.get( attr ) );
const hasItems       = array => !isEmpty( array );
const includedIn     = array => ( item => array.indexOf( item ) >= 0 );
const intersectionOf = ( array1, array2, index ) => index >= 0 ? array1.filter( includedIn( array2 ) ) : array2;

export default Model.extend({
  types:        hasMany( "type" ),
  restrictions: hasMany( "restriction" ),
  isValidTypes: computed(
    "types.[]",
    "restrictions.@each.allowedTypes",
    async function(){
      let restrictions = await this.get( "restrictions" );
      if ( isEmpty( restrictions ) )
      {
        return true;
      }

      let allowed = ( await Promise.all( restrictions.map( getAll( "allowedTypes" ) ) ) )
                      .filter( hasItems );
      if ( isEmpty( allowed ) )
      {
        return true;
      }

      let types = await this.get( "types" );
      if ( isEmpty( types ) )
      {
        return false;
      }

      let allowedTypes = allowed.reduce( intersectionOf );
      return types.every( includedIn( allowedTypes ) );
    }
  )
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 2016-07-24
    • 1970-01-01
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    相关资源
    最近更新 更多