mercredi 2 mai 2018

Validate hasMany relationship against another hasMany

I have 3 models: type, restriction and item.

A type is simple and just has an id:

app/models/type.js:

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

A restriction can have many type describing the allowable types for an item with this restriction:

app/models/restriction.js:

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

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

An item can have many type but also can have many restriction and the type must only be a subset of the intersection of the allowed types for all the restrictions (and if there is at least one restriction then it must have at least one type).

I've implemented a validation for this using a computed property:

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 ) );
    }
  )
});

This uses the DS.Model.hasMany( attributeName ) to synchronously get the HasManyReference for the relationships which relies on the referenced models being loaded.

How can I change the computed property to use this.get() to asynchronously get both attributes (and the child attributes) rather than using this.hasMany() synchronously?




Aucun commentaire:

Enregistrer un commentaire