Skip to content

Instantly share code, notes, and snippets.

@Keeo
Last active October 19, 2016 21:37
Show Gist options
  • Save Keeo/8f1d49b5e7f937caa9f291c4e846557a to your computer and use it in GitHub Desktop.
Save Keeo/8f1d49b5e7f937caa9f291c4e846557a to your computer and use it in GitHub Desktop.
Custom form skeleton for ember
import Ember from 'ember';
const {assert, get, computed, isArray, RSVP} = Ember;
const {keys} = Object;
const {service} = Ember.inject;
const {later} = Ember.run;
export default Ember.Component.extend({
validation: service(),
fields: undefined,
errors: undefined,
isValid: true,
submitOnFocusOut: false,
init() {
this._super(...arguments);
assert('Model property must be set.', this.get('model'));
this.set('fields', {});
this.set('errors', {});
this.resetForm();
},
resetForm() {
this.get('modelProperties').forEach(name => {
const item = this.get('model.' + name);
this.set('fields.' + name, isArray(item) ? item.slice(0) : item);
});
this.set('errors', {});
},
validateField(fieldName) {
const constraint = this.get('model.constraints.' + fieldName);
const value = this.get('fields.' + fieldName);
const error = this.get('validation').validateField(fieldName, value, constraint);
this.set('errors.' + fieldName, error ? error[fieldName] : undefined);
this._setIsValid();
},
validate() {
const errors = this.get('validation').validate(this.get('fields'), this.get('model.constraints'));
this.set('errors', errors || {});
return this._setIsValid();
},
updateModel() {
const model = this.get('model');
this.get('modelProperties').forEach(name => {
const newValue = this.get('fields.' + name);
model.set(name, newValue);
});
},
focusOut() {
later(() => {
if (this.get('isDestroyed') || this.get('isDestroying')) {
return;
}
const focusedElement = document.activeElement;
const isFocusedOut = this.$().has(focusedElement).length === 0 && !this.$().is(focusedElement);
if (isFocusedOut) {
this.send('focusOut');
}
});
},
actions: {
focusOut() {
if (this.get('submitOnFocusOut')) {
this.send('submit');
}
},
propertyDidChange(property, value) {
this.set('fields.' + property, value);
this.validateField(property);
},
reset() {
this.resetForm();
},
validate(fieldName) {
if (fieldName) {
this.validateField(fieldName);
} else {
this.validate();
}
},
submit() {
const model = this.get('model');
if (!this.validate()) {
return RSVP.reject();
}
if (!this.isModified()) {
this.sendAction('didSubmit', model);
return RSVP.resolve();
}
this.updateModel();
return model.save().then(() => {
this.resetForm();
this.sendAction('didSubmit', model);
}, err => {
this.sendAction('didSubmit', model, err);
});
},
close() {
this.sendAction("close");
},
},
isModified() {
const model = this.get('model');
const fields = this.get('fields');
const singleModelProperties = this.get('modelRelationshipsBelongsTo').concat(this.get('modelAttributes'));
if (singleModelProperties.any(name => model.get(name) !== fields[name])) {
return true;
}
const manyModelProperties = this.get('modelRelationshipsHasMany');
return manyModelProperties.any(name => {
if (model.get(name + '.length') === 0 && fields[name].length === 0) {
return false;
}
if (model.get(name + '.length') !== fields[name].length) {
return true;
}
return model.get(name).any(record => !fields[name].includes(record));
});
},
_setIsValid() {
const errors = this.get('errors');
const isValid = !keys(errors).any(key => errors[key]);
return this.set('isValid', isValid);
},
modelProperties: computed('modelRelationshipsHasMany', 'modelRelationshipsBelongsTo', 'modelAttributes', function() {
const hasMany = this.get('modelRelationshipsHasMany');
const belongsTo = this.get('modelRelationshipsBelongsTo');
const attributes = this.get('modelAttributes');
return [].concat(hasMany, belongsTo, attributes);
}),
modelRelationships: computed('modelRelationshipsHasMany', 'modelRelationshipsBelongsTo', function() {
return [].concat(this.get('modelRelationshipsHasMany'), this.get('modelRelationshipsBelongsTo'));
}),
modelRelationshipsHasMany: computed('model', function() {
return get(this.get('model').constructor, 'relationshipNames').hasMany;
}),
modelRelationshipsBelongsTo: computed('model', function() {
return get(this.get('model').constructor, 'relationshipNames').belongsTo;
}),
modelAttributes: computed('model', function() {
const attributes = Ember.A();
get(this.get('model').constructor, 'attributes').forEach(attr => attributes.push(attr.name));
return attributes;
}),
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment