ember-changeset-validations icon indicating copy to clipboard operation
ember-changeset-validations copied to clipboard

How to validate nested keys?

Open olivierlesnicki opened this issue 8 years ago • 18 comments

Given the following Ember.Object:

Ember.Object.create({
  title: 'Hello',
  contactNumber: {
    type: '07594378348',
    number: ''
  }
})

How do I define validations to validate the contactNumber.number field?

{
  title: validatePresence(true),
  contactNumber: ... ?
}

olivierlesnicki avatar Dec 01 '16 12:12 olivierlesnicki

👍

EstesE avatar Dec 09 '16 14:12 EstesE

This is currently a bug that I'm going to fix - when it lands, it should be:

{
  title: validatePresence(true),
  'contactNumber.number': validatePresence(true)
}

Tracking the work here - https://github.com/DockYard/ember-changeset/pull/140

poteto avatar Dec 09 '16 18:12 poteto

@poteto We ran into this exact same problem this morning! :-) Excited for your update!

dpigera avatar Dec 09 '16 20:12 dpigera

Is there any update on when a fix for this might land?

I tried working around it by getting my top-level validator to return an object with keys containing the nested errors, but that then changes the error structure to be non-standard, and so I'd rather avoid doing that if possible.

BillyRayPreachersSon avatar Nov 13 '17 11:11 BillyRayPreachersSon

Hey @BillyRayPreachersSon, I'll be reviewing this sometime this week – see https://github.com/poteto/ember-changeset/pull/140#issuecomment-343172398. It's high on my todo list.

nucleartide avatar Nov 13 '17 11:11 nucleartide

Will have time for this on Thursday and Friday.

nucleartide avatar Nov 14 '17 04:11 nucleartide

Hola, I just ran into this exact issue and coincidence you guys are talking about it...

import CreditCardValidations from '.../validations/credit-card';
import { inject as service } from '@ember/service';

export default Component.extend(CreditCardValidations, {
    model: {
        address: {
            address_line1: null,
            address_line2: null,
            city: null,
            country: null,
            phone_number: null,
            postal_code: null,
            state_province: null
        },
        cc: {
            ccv: null,
            expiration_month: null,
            expiration_year: null,
            number: null
        },
        name: {
            first_name: null,
            last_name: null
        }
    },

This doesn't work so far...

import {
    validatePresence
} from 'ember-changeset-validations/validators';

export default {
    'name.first_name': [
        validatePresence(true)
    ],
};

erichaus avatar Nov 19 '17 00:11 erichaus

Was this issue fixed by the 3.0 release? I'm trying to validate a nested key as follows, and it does not appear to be working at this time (the other non-nested validations on this model work as expected):

// Data structure
{
  amount: {
    value: 0,
    currency: 'USD'
  }
}
// Validations
import {
  validateNumber,
  validatePresence,
} from 'ember-changeset-validations/validators';

export default {
  'amount.value': [
    validatePresence(true),
    validateNumber({ integer: true }),
  ]
};

kpfefferle avatar May 26 '20 15:05 kpfefferle

@kpfefferle Prior to the 3.0 release, there were some hard to deal with bugs relating to nested keys. This was mainly due to dot separated keys. With 3.0 (and specifically 3.4.0), we have finally reached a solid solution for allowing users to retrieve CHANGES/CONTENT and set at any level in the object tree. One consequence of this is that we deal with JS objects as they are (in "expanded" form). So this should work!

export default {
  amount: {
    value: [
      validatePresence(true),
      validateNumber({ integer: true }),
    ]
  }
};

https://github.com/validated-changeset/validated-changeset/pull/45

snewcomer avatar May 26 '20 20:05 snewcomer

@snewcomer Great, I actually like that syntax better. I'll give that a try, thanks!

kpfefferle avatar May 26 '20 20:05 kpfefferle

The validation seems to be working as described, but I'm seeing the top-level object get replaced with only the nested properties from the changeset rather than preserving any properties that have not been edited.

To build on my example above, I have the following data structure:

{
  amount: {
    value: 0,
    currency: 'USD'
  }
}

I then have a changeset validating amount.value (which is editable via a form field):

// Validations
export default {
  amount: {
    value': [
      validatePresence(true),
      validateNumber({ integer: true }),
    ]
  }
};

When I apply the changeset to the underlying model, I lose the amount.currency (which was not modified by the changeset. Instead, I just get the amount.value property from the changeset:

{
  amount: {
    value: '1000'
    // currency: 'USD' has been wiped out by the changeset
  }
}

I would expect the changeset to only update amount.value here and leave amount.currency untouched.

If this should be filed as a new issue or on a different repo, please let me know.

kpfefferle avatar Jun 03 '20 19:06 kpfefferle

Just as a quick check, what version are you on?

snewcomer avatar Jun 03 '20 19:06 snewcomer

@snewcomer I just updated this morning to [email protected] (which did fix a previous issue from 3.4.0!)

kpfefferle avatar Jun 03 '20 20:06 kpfefferle

You used the never ever never value key in an object ;). Just kidding. A Change instance has a value key and that is what we were checking (obj.value). However, we should just check obj instanceof Change. Thanks for the issue and lmk if it works or not!

snewcomer avatar Jun 03 '20 23:06 snewcomer

@snewcomer With 3.5.1, I'm still getting my amount object squashed. I end up with something that looks like:

{
  amount: {
    value: {
      value: '1000' // nested value.value
    }
    // currency: 'USD' is still wiped out
  }
}

where I'd hope to see

{
  amount: {
    value: '1000',
    currency: 'USD'
  }
}

kpfefferle avatar Jun 04 '20 15:06 kpfefferle

@kpfefferle With the latest e-c-v (3.5.2), is it still failing? How different is your case from this test case?

https://github.com/validated-changeset/validated-changeset/blob/e1dd9a65d19fd1e269b48049855421d32a041cfd/test/index.test.ts#L1431

snewcomer avatar Jun 05 '20 13:06 snewcomer

Might still not work with Ember Data Models. I defined a model with an attribute which contains an object:

import Model, { attr } from '@ember-data/model';

export default class TermModel extends Model {
  @attr('string') title;
  @attr({
    defaultValue() {
      return {};
    }
  }) content;
}

And created a corresponding validator with a rule for the nested object:

import {
  validatePresence
} from 'ember-changeset-validations/validators';

export default {
  title: validatePresence(true),
  content: {
    address: validatePresence(true)
  }
};

Finally in my template I followed the suggested implementation:

<div>
  Title:<br>
  <Input @value={{@changeset.title}} />
</div>

<div>
  Address:<br>
  <Input @value={{@changeset.content.address}} />
</div>

<button {{on 'click' (fn @submit @changeset)}}>
  Save
</button>

If I call the validation method, only the first level of attributes is recognized. I also tried it with a pure Javascript object and unfortunately I got the same result.

tmthn avatar Jul 14 '20 21:07 tmthn

@snewcomer Sorry for the delay on this, but I recently refactored away from the pattern that was requiring me to validate nested keys. Thanks for your responsiveness, and hopefully someone else running into the same issue can help out moving forward.

kpfefferle avatar Aug 05 '20 18:08 kpfefferle