poteto / ember-changeset Goto Github PK
View Code? Open in Web Editor NEWEmber.js flavored changesets, inspired by Ecto
Home Page: http://bit.ly/ember-changeset-demo
License: MIT License
Ember.js flavored changesets, inspired by Ecto
Home Page: http://bit.ly/ember-changeset-demo
License: MIT License
I am totally in love with ember-changeset! Bravo!
I have this finding:
Given an empty changeset field in a template, e.g. {input type="text" value=changeset.familyName}}.
Via controller.set('changeset', new Changeset(model))
this changeset is bound to a newly created person
record.
Problem: ember-changeset registers a change after entering this empty field and leaving the field untouched.
Enhancement request: Do not do that!
This request probably could be replaced by a more generic enhancement request: Do not make or keep a change registration (in changeset.changes), when current "model.value" equals current "changeset.value".
The errors
array is useful for iterating through validation messages, but sometimes you want an easy way to find a validation directly. For example,
I want to use ember-changeset for an object containing an array. When I create a changeset based on the following model:
{ "someField": "someValue", "someArray": [ "value1", "value2" ] }
And I do a changeset.set('someArray.0', 'newValue1')
, it does not work. Debugging shows that the new value is not registered as a change, but it is applied directly on the original model.
What happens here is that Ember first does a changeset.get('someArray')
and then sets the property on the resulting object. On the first get, the unknownProperty
handler of ember-changeset returns the original array (as it has not been changed yet). The set is then applied on the original array and not on the changeset.
Can this be fixed? This is probably related to the issues dealing with complex objects #127 and #133.
I would expect that when writing
changeset.save().then((result) => { ... })
the result would be the saved underlying entity, but instead it resolves with the changeset itself, which is not very useful, because you already have the changeset in scope.
If a cset value is changed from a valid value to an invalid one an error is set within addError
but this is not considered a CHANGE
. Running rollback()
on a cset should revert all changes, even the invalid ones.
Something that would be a nice-to-have for Ghost's usage (currently refactoring the validations to use ember-changeset) would be methods (computed properties perhaps?) on the changeset object that allow checking of individual properties for valid/invalid/dirty states.
Based on the current structure this shouldn't be too terribly difficult, but wanted to make sure this is a desired feature before going ahead and creating a PR for it ๐
Hi,
I am using "ember-changeset" along with "ember-changeset-validation" for client side validation.
Going through the docs, I couldn't find any information regarding how changeset works with ember model association (belongsTo, hasMany).
Currently, I require validation of objects having hasMany relation to my model, Am I supposed to create changeset for every objects in the hasMany association ? This could be a tedious process when the objects are being added/removed dynamically.
Would really like to know the opinion of "ember-changset" regarding this case.
Thanks
To reproduce:
In the method signature for changeset(obj, validateFn = defaultValidatorFn, validationMap = {})
, what is the purpose of validationMap
and why is it not documented?
Hi there!
Love this addon. Actually I love all of your addons, @poteto. Thank you for always providing such elegant solutions to common problems!
One of the main reasons I am using ember-changeset
now is to handle rolling back ember data relationships. Currently an ember data model can rollback attributes but not relationships, so this is a nice solution.
However, I have a use case with ember data that I am finding challenging.
save()
.The only solution I see today is to reset the changeset on Route.deactivate
, Controller.resetController
, or Component.willDestroyElement
, but that is quite far from the source of the issue. Is there a better solution? How do others handle this today?
I believe an elegant solution would be to have a changeset.reverse()
method, which is basically the opposite of execute()
. Just as execute()
applies the changes to the underlying model without clearing the list of changes, reverse()
would reverse the changes to the underlying model without clearing the list of changes. This way, a save
action may look something like this:
changeset.save()
.then(() => { /* ... */ })
.catch(() => {
get(this, 'model.errors').forEach(({ attribute, message }) => {
changeset.addError(attribute, { validation: message });
});
changeset.reverse();
});
This way, the user would not have lost their changes, and the underlying model would be made pristine again. The changeset can be safely discarded without needing to perform a final rollback.
(Ultimately I would love to see a more DDAU approach to Ember Data, where the models are read only from the server (data down), you create a changeset and submit it to the server (actions up), and a successful result will update the model from the returning payload (data down). Let me know if you'd be interested in collaborating on that! ๐ )
Hi,
Is there a way to check if a changeset has changed since it was last validated?
Ideally setting any prop: changeset.set('prop', 1)
will turn changeset.get('isValid')
false, until all validators (async here is the issue) resolve.
This would allow us to disable UI until we know for certain that everything is resolved and valid.
Currently, if there is a slow async validator, the changeset will be in isValid
true, but in reality, it is still unresolved, and therefor, the user may proceed to submit and then the world falls apart.
Let me know what you think.
Thanks
While working on my project I noticed that isPristine returns true when isValid is false. This is definitely not expected. Is there any story behind this? Here is full, working example.
Currently, when calling validate
on a changeset, all changes will also be set on the changeset. validate
should only set errors.
Hi,
Love this addon, thanks again for putting it together.
Question regarding isValid when you first create the changeset. Is there a way to have the changeset run it's validate function when it is first created?
I would like to be able to create a changeset and be able to test the isValid prop right away in the template.
Notice in this twiddle even though the model is invalid, it shows as valid on init: Twiddle
Thanks
Hi,
It's more if a question rather that the issue but I would like to know if it's (or will be) possible to skip the validation on property set?
If we hook ember-changest with form-for add-on we will get instant feedback on input change. However sometimes there is a case where you want to wait with validation and trigger it on submit. Is this currently doable?
Is this intended behavior? If so we should mention in the docs.
Also, as an aside, addError is given an array for validation
message from ember-changeset-validations, but is given a string in the docs and test. Is this something we care about and/or will care about in future development?
Needs some kind of server interaction and possible adapter implementation
When calling validate with multiple validations on a property, changeset.error.propertName.validations
is an array of messages. However, if you call changeset.addError('propertyName', 'message')
, then changeset.error.propertName.validations
will be a string.
This is inconsistent and makes it unclear how to handle validation messages for a property in the UI.
When using with ember-cli-materialize, e.g.:
The input value is constantly reset to empty. If the errors binding is removed everything works. I dug into both repos and not able to figure out where this is stemming from.
So after building out all of Ghost's new validation functionality using this, I ran into an issue with this library that forced me to re-write a private method (which is bad practice ๐ ). Essentially, I found out that my changeset didn't contain the most recent changes, because only valid changes were actually persisted to the changeset.
This isn't a bug, because it says in the readme:
The idea behind a changeset is simple: it represents a set of valid changes to be applied onto any Object
Here's the specific use-case for why this broke my refactor: the validation flow in Ghost works by validating when the particular input is de-focused, and then all inputs are validated when the form is submitted. After all changes are validated (using the changeset.validate
) method, then the changes are persisted to the underlying model.
The problem arises because if the user de-focuses an input without the content of said input being valid, the actual input value doesn't exist in the changeset, resulting in an incorrect error message displayed (e.g. the please enter a value
error message other than the invalid format for value
message).
After reading through the Ecto Changeset documentation, it seems that the changes are set on the changeset regardless of validation status (it differentiates "casting" from "validation"). I could be reading this wrong though.
I understand that not everyone would necessarily want to use ember-changeset this way, but I think there could be an ability to do both at the same time, based on how the changeset is created initially (maybe a third parameter passed to the changeset constructor that enables/disables this new method of behavior?)
In terms of code changes, it's pretty minimal.
Thoughts?
/cc @poteto
A handy optional feature would be the ability to apply a changeset and have it return a new object, with the changes, (assuming they're valid). This way, we'd have the option to proxy over to the original model, plus the ability to apply changesets immutably and curry the result of changeset executions with other actions/functions.
If you want to point me in the right direction, I'd be happy to work on this myself and open a PR.
In documentation for change at https://github.com/DockYard/ember-changeset#change it is written:
You can use this property to locate a single change:
{{#if changeset.change.firstName}}
<p>You changed {{changeset.firstName}} to {{changeset.change.firstName}}!</p>
{{/if}}
But, I seems like {{changeset.change.firstName}}
always contains exactly the same value as {{changeset.firstName}}
.
I am not surprised to find the new value of firstName
in {{changeset.firstName}}
. I have no expectations about what to find in {{changeset.change.firstName}}
.
Anyway, where do I find the original/old value of a property of the changeset object ?
I am getting the bug fixed in #47 when i pass a validator (either as an action or an ember-changeset-validators validation map).
With [email protected] the behavior of Ember.set
was changed to not modify the host object if the value is already there.
That means if one does a = {};Ember.set(a, 'foo', undefined);Object.keys(a)
, a
won't have the foo
key. ( see emberjs/ember.js#14270 ).
In context of changeset that means that a change that tries to set undefined
to the changeset CHANGES
, it won't be "tracked" and not set on the CONTENT
object on execute()
.
I'm not sure if this is a problem for changeset. If it is and you think this is a bug/regression in ember, please comment on emberjs/ember.js#14270 .
A possible use-case would be a one-way-select
(with promptIsSelectable
) that represents a country selector, where blank means all countries
.
If the user wants to change an item to all countries
, where the country was previously set to a specific country, undefined
would be set, which in turn wouldn't be set on the CHANGES
object, because previously get(obj, 'country')
was already undefined
.
Make sure to assign both changesets to an empty object
Hi there!
I have been working on an addon to integrate ember-cp-validations with ember-changeset. Unfortunately my lack of deep understanding of this library holds me back from releasing it into the wild. I was hoping someone here can do a quick review to make sure Im not doing anything too stupid or something that can be easily broken further down the line. Any help at all is appreciated ๐
I'm Chris from zipcar, this is in reference to the comments made on ZC-416.
In when using a form-for textarea bound to a changeset object, if you completely cut the text out, a presence error appears as expected. However, pasting that same text back in does not clear the error. Any other text can be pasted in as long as it is not the original text.
I'm using ember-changeset with more complex objects. Let's assume:
this.properties = Ember.A();
this.properties.pushObject({name: 'name', level: 'advanced', type: 'text', title: 'My Name', value: '123'});
this.properties.pushObject({name: 'city', type: 'select', title: 'City', value: 'Brno', options: ['Brno', 'Praha', 'New York']});
this.properties.pushObject({name: 'author', level: 'advanced', type: 'text', title: 'Author', value: null, defaultValue: 'Hugo Hugo'});
what differs from expected
this.properties = { name: 'My Name', city: 'Brno', author: null }
My question is what is the best practise to use more complex objects?
Currently, my solution is to use computed properties to generate expected format from my complex one. It works but I believe there might be a better option.
Spent a while debugging my API failure-case tests today (returning a 500 using mirage) as they were getting unhandled rejections, it eventually seems to be caused by save
chaining a then
call internally but returning the 'pre-then
' promise. This might be due to how RSVP
works, or could be something else, but modifying save
to return the 'full' promise like:
return savePromise.then((result) => {
this.rollback();
return result;
});
Stops the uncaught rejections and gets the tests working again.
It looks like someone else has already had this issue and put in a PR to fix it (#135), wanted to put up a corresponding issue as supporting info for the PR. ๐
Hello.
This my code:
const changeset = this.get('changeset');
const model = this.get('model');
const validation = this.get('validation');
return changeset
.cast(keys(validation))
.validate()
.then(() => {
if (changeset.get('isValid')) {
return changeset.execute();
}
});
I catched an error - You must use Ember.set() to set the
_changesproperty
.
This is cast function
I use:
DEBUG: -------------------------------
DEBUG: Ember : 2.7.1
DEBUG: Ember Data : 2.7.0
DEBUG: jQuery : 2.2.4
DEBUG: Ember Simple Auth : 1.1.0
DEBUG: -------------------------------
Should return undefined
since it won't be set on the changeset's internal changes
Is it possible to use ember-changeset
without a component?
Trying to use prepare, I get an error: vendor.js:29215 Uncaught Error: Assertion Failed: You must use Ember.set() to set the _changes
property (of changeset:<project-name@model:load::ember522:1>) to [object Object]
.
My code below:
var changeset = this.get('changeset');
changeset.prepare((changes) => {
let modified = JSON.parse(JSON.stringify(changes));
modified.signature = this.get('currentUser.user.fullName');
modified.timeIn = moment.utc(modified.date + ' ' + modified.timeIn, 'MMMM DD, YYYY h:mm a').valueOf();
modified.timeOut = moment.utc(modified.date + ' ' + modified.timeOut, 'MMMM DD, YYYY h:mm a').valueOf();
return modified;
});
Chaining execute().save()
is not good ergonomics. Calling save
should execute the changeset and commit the change.
Let's say you have a model A
that hasMany
some other model B
, and your form has a dynamic list of B
that are editable all at once. How would you wire that up using ember-changeset? It seems like you'd want to have a changeset per B
record as well and have the validation on the list of B
be the aggregate of the child validation state, butโฆ that sounds like lots of manual wiring, if it's even possible at all.
Have you met someone with a problem when using multiple forms? I have a signup and login page and switch between them using the {{link-to}}, if one form generate validation errors (signup-form) and I switch to another (login-form I get validations errors from the previous form after changeset.validation() is executed.
login.hbs
{{#with (changeset model LoginValidations) as |changeset|}}
<form {{action 'submit' changeset on='submit'}} >
{{input id="email" type="email" value=changeset.email }}
{{input id="password" type="password" value=changeset.password }}
<button type="submit" >Log in</button>
</form>
{{/with}}
Not a member? {{#link-to 'signup'}}Sign up{{/link-to}}
signup.hbs:
{{#with (changeset model SignupValidations) as |changeset|}}
<form {{action 'submit' changeset on='submit'}}>
{{input id="first_name" type="text" value=changeset.first_name}}
{{input id="last_name" type="text" value=changeset.last_name }}
{{input id="email" type="email" value=changeset.email}}
{{input id="password" type="password" value=changeset.password}}
{{input id="password_again" type="password" value=changeset.password_again}}
<button type="submit"}}>Sign up</button>
</form>
{{/with}}
Already have an account? {{#link-to 'login'}}Log in{{/link-to}}
route for login:
model(){
return Ember.Object.create({
email: null,
password: null
});
}
route for signup:
model(){
return Ember.Object.create({
first_name: null,
last_name: null,
email: null,
password: null,
password_again: null
});
}
example of "submit" action for login (signup is similar):
submit(changeset) {
return changeset.validate().then(
() => {
if (get(changeset, 'isInvalid')) {
console.log(changeset.get('errors') --> I get errors from signup changeset, how it is possible?
return;
}
},
Hi,
Wondering how I can handle validators that return promises.
Trying to create a validator that checks if a name is available by calling some api/name_check
service.
If you type anything in firstName
you will notice [object Object]
instead of a passing validation.
I noticed there was work done on handling validators that return promises, but I can't see how it actually works.
Thank you
I have a use case where some form fields are only required if a certain value is selected somewhere else in the form (in this case a checkbox). Upon investigating how validations and changesets work, I can see it's not so trivial to add support for this because not only do you need to not disable the validations, but you also need to ensure that field doesn't get propagated to the model on execute.
Any thoughts on this? Am I missing some not so obvious way to achieve this? Otherwise, I'll have a crack at implementing it soon.
let snapshot = changeset.snapshot(); // snapshot of changes and errors
localStorage.setItem('user-edit-form', JSON.stringify(snapshot));
// when user returns
changeset.restore(localStorage.getItem('user-edit-form')); // parses if string
Hi,
I'd like to know if this is posible. I want to create a changeset from a belongs-to association. I have a User model associated to one Location. So, in my template I wrote this:
{{location-form changeset=(changeset model.location)}}
but that doesn't build the changeset properly.
Thanks.
Hi there, thanks for this awesome library.
I may have found a bug with tracking changes to hasMany
associations. For example, if I have a Foo model with a users
hasMany association, and I update the users
prop, there is no change logged in changeset.changes()
. In my use case, I noticed this while trying to rollback changes on a Foo
model.
Ember Twiddle reproducing the issue: https://ember-twiddle.com/3bb755fcdf978eadfaa498acb5e777ca?openFiles=templates.application.hbs%2C
Please let me know if you need more info or if I'm doing something dumb. Thanks!
Here the code for it https://ember-twiddle.com/7e70cc65a019ce2fa1897daa0d3c13b3
It doesn't work if its .validate('first_name', 'password', 'passwordAgain')
and its works if .validate()
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.