Don't share errors and hasValidated references between validator instances

no issue
- ensure that each validator instance gets it's own `errors` and `hasValidated` objects
- updates some uses of `ValidationEngine` that were relying on side-effects of the unintended reference sharing
- fixes issue with add subscriber modal displaying an error state after opening if it previously had errors when closing
This commit is contained in:
Kevin Ansfield 2016-04-25 10:54:36 +01:00
parent 4a5c7d953e
commit 983c708ece
6 changed files with 20 additions and 21 deletions

View File

@ -92,10 +92,6 @@ export default Controller.extend(SettingsSaveMixin, {
},
actions: {
validate(property) {
this.get('model').validate({property});
},
checkPostsPerPage() {
let postsPerPage = this.get('model.postsPerPage');

View File

@ -1,7 +1,6 @@
import Ember from 'ember';
import isNumber from 'ghost/utils/isNumber';
import boundOneWay from 'ghost/utils/bound-one-way';
import ValidationEngine from 'ghost/mixins/validation-engine';
const {
Controller,
@ -12,9 +11,7 @@ const {
} = Ember;
const {alias, and, not, or, readOnly} = computed;
export default Controller.extend(ValidationEngine, {
// ValidationEngine settings
validationType: 'user',
export default Controller.extend({
submitting: false,
lastPromise: null,
showDeleteUserModal: false,

View File

@ -49,11 +49,17 @@ export default Mixin.create({
// This adds the Errors object to the validation engine, and shouldn't affect
// ember-data models because they essentially use the same thing
errors: Errors.create(),
errors: null,
// Store whether a property has been validated yet, so that we know whether or not
// to show error / success validation for a field
hasValidated: emberA(),
hasValidated: null,
init() {
this._super(...arguments);
this.set('errors', Errors.create());
this.set('hasValidated', emberA());
},
/**
* Passes the model to the validator specified by validationType.

View File

@ -12,14 +12,14 @@
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="title"}}
<label for="blog-title">Blog Title</label>
{{gh-input id="blog-title" class="gh-input" name="general[title]" type="text" value=model.title focusOut=(action "validate" "title")}}
{{gh-input id="blog-title" class="gh-input" name="general[title]" type="text" value=model.title focusOut=(action "validate" "title" target=model)}}
{{gh-error-message errors=model.errors property="title"}}
<p>The name of your blog</p>
{{/gh-form-group}}
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="description" class="description-container"}}
<label for="blog-description">Blog Description</label>
{{gh-textarea id="blog-description" class="gh-input" name="general[description]" value=model.description focusOut=(action "validate" "description")}}
{{gh-textarea id="blog-description" class="gh-input" name="general[description]" value=model.description focusOut=(action "validate" "description" target=model)}}
{{gh-error-message errors=model.errors property="description"}}
<p>
Describe what your blog is about
@ -108,7 +108,7 @@
{{#if model.isPrivate}}
{{#gh-form-group errors=model.errors hasValidated=model.hasValidated property="password"}}
{{gh-input name="general[password]" type="text" value=model.password focusOut=(action "validate" "password")}}
{{gh-input name="general[password]" type="text" value=model.password focusOut=(action "validate" "password" target=model)}}
{{gh-error-message errors=model.errors property="password"}}
<p>This password will be needed to access your blog. All search engine optimization and social features are now disabled. This password is stored in plaintext.</p>
{{/gh-form-group}}

View File

@ -79,7 +79,7 @@
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="name" class="first-form-group"}}
<label for="user-name">Full Name</label>
{{input value=user.name id="user-name" class="gh-input user-name" placeholder="Full Name" autocorrect="off" focusOut=(action "validate" "name")}}
{{input value=user.name id="user-name" class="gh-input user-name" placeholder="Full Name" autocorrect="off" focusOut=(action "validate" "name" target=user)}}
{{#if user.errors.name}}
{{gh-error-message errors=user.errors property="name"}}
{{else}}
@ -102,7 +102,7 @@
<label for="user-email">Email</label>
{{!-- Administrators only see text of Owner's email address but not input --}}
{{#unless isAdminUserOnOwnerProfile}}
{{input type="email" value=user.email id="user-email" name="email" class="gh-input" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "email")}}
{{input type="email" value=user.email id="user-email" name="email" class="gh-input" placeholder="Email Address" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "email" target=user)}}
{{gh-error-message errors=user.errors property="email"}}
{{else}}
<span>{{user.email}}</span>
@ -128,21 +128,21 @@
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="location"}}
<label for="user-location">Location</label>
{{input type="text" value=user.location id="user-location" class="gh-input" focusOut=(action "validate" "location")}}
{{input type="text" value=user.location id="user-location" class="gh-input" focusOut=(action "validate" "location" target=user)}}
{{gh-error-message errors=user.errors property="location"}}
<p>Where in the world do you live?</p>
{{/gh-form-group}}
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="website"}}
<label for="user-website">Website</label>
{{input type="url" value=user.website id="user-website" class="gh-input" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "website")}}
{{input type="url" value=user.website id="user-website" class="gh-input" autocapitalize="off" autocorrect="off" autocomplete="off" focusOut=(action "validate" "website" target=user)}}
{{gh-error-message errors=user.errors property="website"}}
<p>Have a website or blog other than this one? Link it!</p>
{{/gh-form-group}}
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="bio" class="bio-container"}}
<label for="user-bio">Bio</label>
{{textarea id="user-bio" class="gh-input" value=user.bio focusOut=(action "validate" "bio")}}
{{textarea id="user-bio" class="gh-input" value=user.bio focusOut=(action "validate" "bio" target=user)}}
{{gh-error-message errors=user.errors property="bio"}}
<p>
Write about you, in 200 characters or less.

View File

@ -256,7 +256,7 @@ describe('Acceptance: Team', function () {
triggerEvent('.user-details-top .first-form-group input.user-name', 'blur');
andThen(() => {
expect(find('.user-details-top .first-form-group').hasClass('error'), 'input is in error state').to.be.true;
expect(find('.user-details-top .first-form-group').hasClass('error'), 'username input is in error state with blank input').to.be.true;
});
// test too long user name
@ -264,7 +264,7 @@ describe('Acceptance: Team', function () {
triggerEvent('.user-details-top .first-form-group input.user-name', 'blur');
andThen(() => {
expect(find('.user-details-top .first-form-group').hasClass('error'), 'input is in error state').to.be.true;
expect(find('.user-details-top .first-form-group').hasClass('error'), 'username input is in error state with too long input').to.be.true;
});
// reset name field
@ -292,7 +292,7 @@ describe('Acceptance: Team', function () {
triggerEvent('.user-details-bottom input[name="email"]', 'blur');
andThen(() => {
expect(find('.user-details-bottom .form-group:nth-of-type(2)').hasClass('error'), 'email input should be in error state').to.be.true;
expect(find('.user-details-bottom .form-group:nth-of-type(2)').hasClass('error'), 'email input should be in error state with invalid email').to.be.true;
});
fillIn('.user-details-bottom input[name="email"]', 'test@example.com');