-
-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Warning Validators #226
Changes from 3 commits
4a8e37f
198f493
8bc874b
41480d6
869f2ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,8 +80,8 @@ export default Ember.Object.extend({ | |
* @readOnly | ||
* @type {Ember.ComputedProperty | Boolean} | ||
*/ | ||
isValid: computed('content.@each.isValid', cycleBreaker(function () { | ||
return get(this, 'content').isEvery('isValid', true); | ||
isValid: computed('_errorContent.@each.isValid', cycleBreaker(function () { | ||
return get(this, '_errorContent').isEvery('isValid', true); | ||
}, true)), | ||
|
||
/** | ||
|
@@ -116,8 +116,8 @@ export default Ember.Object.extend({ | |
* @readOnly | ||
* @type {Ember.ComputedProperty | Boolean} | ||
*/ | ||
isTruelyValid: computed('content.@each.isTruelyValid', cycleBreaker(function () { | ||
return get(this, 'content').isEvery('isTruelyValid', true); | ||
isTruelyValid: computed('_errorContent.@each.isTruelyValid', cycleBreaker(function () { | ||
return get(this, '_errorContent').isEvery('isTruelyValid', true); | ||
}, true)), | ||
|
||
/** | ||
|
@@ -136,8 +136,8 @@ export default Ember.Object.extend({ | |
* @readOnly | ||
* @type {Ember.ComputedProperty | Boolean} | ||
*/ | ||
isDirty: computed('content.@each.isDirty', cycleBreaker(function () { | ||
return !get(this, 'content').isEvery('isDirty', false); | ||
isDirty: computed('_errorContent.@each.isDirty', cycleBreaker(function () { | ||
return !get(this, '_errorContent').isEvery('isDirty', false); | ||
}, false)), | ||
|
||
/** | ||
|
@@ -171,8 +171,8 @@ export default Ember.Object.extend({ | |
* @readOnly | ||
* @type {Ember.ComputedProperty | Array} | ||
*/ | ||
messages: computed('content.@each.messages', cycleBreaker(function () { | ||
const messages = flatten(get(this, 'content').getEach('messages')); | ||
messages: computed('_errorContent.@each.messages', cycleBreaker(function () { | ||
const messages = flatten(get(this, '_errorContent').getEach('messages')); | ||
|
||
return uniq(compact(messages)); | ||
})), | ||
|
@@ -194,6 +194,41 @@ export default Ember.Object.extend({ | |
return get(this, 'messages.0'); | ||
})), | ||
|
||
/** | ||
* A collection of all {{#crossLink "Error"}}Warnings{{/crossLink}} on the object in question. | ||
* Each warning object includes the warning message and it's associated attribute name. | ||
* | ||
* ```javascript | ||
* // Example | ||
* get(user, 'validations.warnings') | ||
* get(user, 'validations.attrs.username.warnings') | ||
* ``` | ||
* | ||
* @property warnings | ||
* @readOnly | ||
* @type {Ember.ComputedProperty | Array} | ||
*/ | ||
warnings: computed('attribute', '_warningContent.@each.errors', cycleBreaker(function () { | ||
return this._computeErrorCollection(get(this, '_warningContent')); | ||
})), | ||
|
||
/** | ||
* An alias to the first {{#crossLink "Warning"}}{{/crossLink}} in the warnings collection. | ||
* | ||
* ```javascript | ||
* // Example | ||
* get(user, 'validations.warning') | ||
* get(user, 'validations.attrs.username.warning') | ||
* ``` | ||
* | ||
* @property warning | ||
* @readOnly | ||
* @type {Ember.ComputedProperty | Error} | ||
*/ | ||
warning: computed('warnings.[]', cycleBreaker(function () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return get(this, 'warnings.0'); | ||
})), | ||
|
||
/** | ||
* A collection of all {{#crossLink "Error"}}Errors{{/crossLink}} on the object in question. | ||
* Each error object includes the error message and it's associated attribute name. | ||
|
@@ -208,18 +243,8 @@ export default Ember.Object.extend({ | |
* @readOnly | ||
* @type {Ember.ComputedProperty | Array} | ||
*/ | ||
errors: computed('attribute', 'content.@each.errors', cycleBreaker(function () { | ||
const attribute = get(this, 'attribute'); | ||
let errors = flatten(get(this, 'content').getEach('errors')); | ||
|
||
errors = uniq(compact(errors)); | ||
errors.forEach(e => { | ||
if(e.get('attribute') !== attribute) { | ||
e.set('parentAttribute', attribute); | ||
} | ||
}); | ||
|
||
return errors; | ||
errors: computed('attribute', '_errorContent.@each.errors', cycleBreaker(function () { | ||
return this._computeErrorCollection(get(this, '_errorContent')); | ||
})), | ||
|
||
/** | ||
|
@@ -303,7 +328,43 @@ export default Ember.Object.extend({ | |
* @type {Ember.ComputedProperty} | ||
* @private | ||
*/ | ||
_contentValidators: computed.mapBy('content', '_validator').readOnly(), | ||
_contentValidators: computed.mapBy('_errorContent', '_validator').readOnly(), | ||
|
||
/** | ||
* @property _errorContent | ||
* @type {Ember.ComputedProperty} | ||
* @private | ||
*/ | ||
_errorContent: computed.filter('content', function(result) { | ||
return get(result, 'isWarning') !== true; | ||
}).readOnly(), | ||
|
||
/** | ||
* @property _warningContent | ||
* @type {Ember.ComputedProperty} | ||
* @private | ||
*/ | ||
_warningContent: computed.filterBy('content', 'isWarning', true).readOnly(), | ||
|
||
|
||
/** | ||
* @method _computeErrorCollection | ||
* @return {Object} | ||
* @private | ||
*/ | ||
_computeErrorCollection(content) { | ||
const attribute = get(this, 'attribute'); | ||
let errors = flatten(content.getEach('errors')); | ||
|
||
errors = uniq(compact(errors)); | ||
errors.forEach(e => { | ||
if(e.get('attribute') !== attribute) { | ||
e.set('parentAttribute', attribute); | ||
} | ||
}); | ||
|
||
return errors; | ||
}, | ||
|
||
/** | ||
* Used by the `options` property to create a hash from the `content` that is grouped by validator type. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,10 +5,16 @@ | |
{{/if}} | ||
|
||
<div class="input-error"> | ||
{{#if showMessage}} | ||
{{#if showErrorMessage}} | ||
<div class="error"> | ||
{{v-get model valuePath 'message'}} | ||
{{get (v-get model valuePath 'error') 'message'}} | ||
</div> | ||
{{/if}} | ||
|
||
{{#if showWarningMessage}} | ||
<div class="warning"> | ||
{{get (v-get model valuePath 'warning') 'message'}} | ||
</div> | ||
{{/if}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we expose all the messages for an attribute somehow? Since this is new functionality, I wonder if {{#each (v-get model valuePath 'messages') as |msg|}}
<div class="message {{if msg.isWarning 'warning'}}">
{{msg}}
</div>
{{/each}} That way I don't have to have two blocks and reduce boilerplate templating. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My theory was:
We could release 3.0 with API changes and think of a better way to handle this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, of course - I blanked that messages are strings on the current API. Definitely don't want to break that. Though warnings are optional, it seems like anytime someone took the time to write a validation for a warning, they'd want to expose it to the user, likely just with another class to style differently. |
||
</div> | ||
</div> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is
.0
the right thing to use here? I've always usedfirstObject
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the same code that is the
errors
anderror
CPs. I believe I tried firstObject but it errored on me. Ill give it another shot though.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem - definitely not required, I just haven't seen this syntax before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
firstObject ideally