adopted-ember-addons / ember-cp-validations

Ember computed property based validations
https://adopted-ember-addons.github.io/ember-cp-validations/
BSD 3-Clause "New" or "Revised" License
442 stars 174 forks source link

Validate array of objects #560

Open rossjha opened 6 years ago

rossjha commented 6 years ago

Environment

How would I go about validating an array of objects from the model? Validating profile.address nested attrs works fine. Not sure how to go about validating name and email for each friend?

I have the following example:

Data:

  profile: {
    friends: [{
      name: 'bob',
      email: 'bob@email.com'
    }, {
      name: 'sue',
      email: 'sue@email.com'
    }],
    address: {
      street: '10 Bash St',
      town: 'Dundee'
    }
  }
import Model from 'ember-data/model';
import attr from 'ember-data/attr';

import { computed } from '@ember/object';

import { validator, buildValidations } from 'ember-cp-validations';

const Validations = buildValidations({
  name: [
    validator('presence', true),
    validator('length', {
      min: 4
    })
  ],
  'profile.address.street': [
    validator('presence', true),
    validator('length', {
      min: 4
    })
  ],
  'profile.address.town': [
    validator('presence', true),
    validator('length', {
      min: 4
    })
  ]
});

export default Model.extend(Validations, {

  // Attributes

  name: attr('string'),
  // custom transform
  profile: attr('object', {
    defaultValue() {
      return {
        friends: [],
        address: {}
      }
    }
  }),

  // Associations

  // Computed Properties

  isFormValid: computed.alias('validations.isValid')

});
rossjha commented 6 years ago

Managed to validated friends from a component, based on the help provided in #431.

I think I need to deal with nested objects in the serializer, but I have the following working in my sample component.

Two issues, I couldn't convert the Ember objects to POJO for saving and I couldn't get the template helper working for showing a message.

If anyone has any suggestions on how to improve this, it would be much appreciated.

import Component from '@ember/component';

import { get, set } from '@ember/object';
import Object from '@ember/object';
import { getOwner } from '@ember/application';
import { A } from '@ember/array';

import { validator, buildValidations } from 'ember-cp-validations';

const friendsValidations = buildValidations({
  'name': [
    validator('presence', true)
  ],
  'email': [
    validator('presence', true),
    validator('format', { type: 'email' })
  ]
});

const Friend = Object.extend(friendsValidations);

const profileValidations = buildValidations({
  'friends': validator('has-many')
});

export default Component.extend(profileValidations, {

    // Defaults

    // Single line CP

    // Multiline CP

    // Private

  createFriends() {
    let owner = getOwner(this);
    let friends = get(this, 'user.profile.friends') || A();

    friends = friends.map(a => Friend.create(owner.ownerInjection(), a));
    set(this, 'friends', friends);
  },

  createNewFriend() {
    let owner = getOwner(this);
    let friend = Friend.create(owner.ownerInjection());
    set(this, 'friend', friend);
  },

    // Lifecycle hooks

  init() {
    this._super(...arguments);

    this.createFriends();
    this.createNewFriend();
    set(this, 'toggleFriendErrorMessage', false);

  },

  actions: {

    addFriend() {
      if (get(this, 'friend.validations.isValid')) {

        // TODO Workaround, can't create POJO from friend, getting 'Converting circular structure to JSON'
        let friend = { name: get(this, 'friend.name'), email: get(this, 'friend.email') };
        get(this, 'friends').unshiftObject(get(this, 'friend'));
        get(this, 'user.profile.friends').unshiftObject(friend);

        // reset, create new friend
        this.createNewFriend();
        // TODO Can't use v-get helper to show error message
        this.toggleProperty('toggleFriendErrorMessage');

      } else {

        this.toggleProperty('toggleFriendErrorMessage');
        console.error('Friend is invalid');
        return false;

      }
    },

    removeFriend(friend) {
      get(this, 'friends').removeObject(friend);
      get(this, 'user.profile.friends').removeObject(friend);
      this.send('save');
    },

    save() {
      let user = get(this, 'user');

      user.validate().then(({ validations }) => {
        if (get(validations, 'isValid')) {
          this.attrs.save();
        }
      });
    },

    cancel() {
      this.attrs.cancel(get(this, 'user'));
    }

  }

});