From 42ce5ddb2adeee49e3a050853270c06a9ad897dc Mon Sep 17 00:00:00 2001 From: Dan Monroe Date: Fri, 4 Sep 2015 16:25:23 -0400 Subject: [PATCH 1/5] Added remaining input helper attributes as passthroughs --- app/templates/components/paper-input.hbs | 29 +++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/app/templates/components/paper-input.hbs b/app/templates/components/paper-input.hbs index 4cdf60988..37c727b28 100644 --- a/app/templates/components/paper-input.hbs +++ b/app/templates/components/paper-input.hbs @@ -9,7 +9,34 @@ {{#if textarea}} {{textarea class="md-input" id=inputElementId placeholder=placeholder value=value focus-in="focusIn" key-down="keyDown" focus-out="focusOut" disabled=disabled required=required autofocus=autofocus}} {{else}} - {{input class="md-input" id=inputElementId placeholder=placeholder type=type value=value focus-in="focusIn" key-down="keyDown" focus-out="focusOut" disabled=disabled required=required autofocus=autofocus}} + {{input class="md-input" id=inputElementId placeholder=placeholder type=type value=value focus-in="focusIn" key-down="keyDown" focus-out="focusOut" disabled=disabled required=required autofocus=autofocus + accept=attr-accept + autocomplete=attr-autocomplete + autosave=attr-autosave + form=attr-form + formaction=attr-formaction + formenctype=attr-formenctype + formmethod=attr-formmethod + formnovalidate=attr-formnovalidate + formtarget=attr-formtarget + height=attr-height + inputmode=attr-inputmode + min=attr-min + maxlength=attr-maxlength + max=attr-max + multiple=attr-multiple + name=attr-name + pattern=attr-pattern + readonly=attr-readonly + selectionDirection=attr-selectionDirection + size=attr-size + spellcheck=attr-spellcheck + step=attr-step + tabindex=attr-tabindex + width=attr-width + + enter=event-enter + }} {{/if}} {{#unless hideAllMessages}} From af8a0bf34667aa4982e2701f61c2e81624dfa864 Mon Sep 17 00:00:00 2001 From: Dan Monroe Date: Sun, 6 Sep 2015 23:36:35 -0400 Subject: [PATCH 2/5] Added integration tests for paper-input --- .../components/paper-input-test.js | 285 ++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 tests/integration/components/paper-input-test.js diff --git a/tests/integration/components/paper-input-test.js b/tests/integration/components/paper-input-test.js new file mode 100644 index 000000000..b83218466 --- /dev/null +++ b/tests/integration/components/paper-input-test.js @@ -0,0 +1,285 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('paper-input', 'Integration | Component | paper input', { + integration: true +}); + +test('renders default theme on wrapper', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input}}`); + + var actual = this.$('md-input-container').attr('class'); + var expected = 'ember-view md-default-theme'; + assert.equal(actual, expected); +}); + +test('renders with label', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input label="Name"}}`); + + var actual = this.$('md-input-container label').text(); + var expected = 'Name'; + assert.equal(actual, expected); +}); + +test('renders with icon', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input icon="person" icon-class="name"}}`); + + var actual = this.$('md-input-container md-icon').attr('class'); + var expected = 'name ember-view paper-icon md-font material-icons md-default-theme person'; + assert.equal(actual, expected); +}); + +test('renders input', function (assert) { + assert.expect(2); + + this.render(hbs`{{paper-input icon="person" icon-class="name"}}`); + + var actual = this.$('md-input-container input'); + var expected = 'md-input ember-view ember-text-field'; + assert.equal(actual.attr('class'), expected); + assert.equal(actual.attr('type'), 'text'); +}); + +test('renders input with id', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input inputElementId="testId"}}`); + + var actual = this.$('md-input-container input').attr('id'); + var expected = 'testId'; + assert.equal(actual, expected); +}); + +test('renders input with placeholder', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input placeholder="Enter value here"}}`); + + var actual = this.$('md-input-container input').attr('placeholder'); + var expected = 'Enter value here'; + assert.equal(actual, expected); +}); + +test('renders input with value', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input value="current value"}}`); + + var actual = this.$('md-input-container input').val(); + var expected = 'current value'; + assert.equal(actual, expected); +}); + +test('renders input as disabled', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input disabled=true}}`); + + var actual = this.$('md-input-container input').attr('disabled'); + var expected = 'disabled'; + assert.equal(actual, expected); +}); + +test('renders input as required', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input required=true}}`); + + var actual = this.$('md-input-container input').attr('required'); + var expected = 'required'; + assert.equal(actual, expected); +}); + +test('renders input as autofocus', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input autofocus=true}}`); + + var actual = this.$('md-input-container input').attr('autofocus'); + var expected = 'autofocus'; + assert.equal(actual, expected); +}); + +test('renders input with accept types of files', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input attr-accept="audio/*|video/*|image/*"}}`); + + var actual = this.$('md-input-container input').attr('accept'); + var expected = 'audio/*|video/*|image/*'; + assert.equal(actual, expected); +}); + +test('renders input with attribute autocomplete', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input attr-autocomplete=true}}`); + + var actual = this.$('md-input-container input').attr('autocomplete'); + var expected = 'true'; + + assert.equal(actual, expected); +}); + +test('renders input with attribute form', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input attr-form="myform"}}`); + + var actual = this.$('md-input-container input').attr('form'); + var expected = 'myform'; + + assert.equal(actual, expected); +}); + +test('renders input with attribute formnovalidate', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input attr-formnovalidate="formnovalidate"}}`); + + var actual = this.$('md-input-container input').attr('formnovalidate'); + var expected = 'formnovalidate'; + + assert.equal(actual, expected); +}); + +test('renders input with attribute formtarget', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input attr-formtarget="_blank"}}`); + + var actual = this.$('md-input-container input').attr('formtarget'); + var expected = '_blank'; + + assert.equal(actual, expected); +}); + +test('renders input with attribute formenctype', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input attr-formenctype="multipart/form-data"}}`); + + var actual = this.$('md-input-container input').attr('formenctype'); + var expected = 'multipart/form-data'; + + assert.equal(actual, expected); +}); + +test('renders input with multiple form attributes', function (assert) { + assert.expect(5); + + this.render(hbs`{{paper-input type="submit" attr-form="myform" attr-formnovalidate="formnovalidate" attr-formtarget="_blank" attr-formenctype="multipart/form-data"}}`); + + var $input = this.$('md-input-container input'); + + var actual = $input.attr('type'); + var expected = 'submit'; + assert.equal(actual, expected); + + actual = $input.attr('form'); + expected = 'myform'; + assert.equal(actual, expected); + + actual = $input.attr('formnovalidate'); + expected = 'formnovalidate'; + assert.equal(actual, expected); + + actual = $input.attr('formtarget'); + expected = '_blank'; + assert.equal(actual, expected); + + actual = $input.attr('formenctype'); + expected = 'multipart/form-data'; + assert.equal(actual, expected); + +}); + +test('renders input with input mode attribute', function (assert) { + assert.expect(1); + + this.render(hbs`{{paper-input attr-inputmode="numeric"}}`); + + var $input = this.$('md-input-container input'); + + var actual = $input.attr('inputmode'); + var expected = 'numeric'; + assert.equal(actual, expected); +}); + +test('renders input with multiple attributes', function (assert) { + assert.expect(12); + + this.render(hbs`{{paper-input type="submit" + attr-min="2" + attr-maxlength="20" + attr-max="42" + attr-multiple="true" + attr-name="elementname" + attr-pattern="(999)999-9999" + attr-readonly="true" + attr-size="30" + attr-spellcheck="true" + attr-step="2" + attr-tabindex="1138" + }}`); + + var $input = this.$('md-input-container input'); + + var actual = $input.attr('type'); + var expected = 'submit'; + assert.equal(actual, expected); + + actual = $input.attr('min'); + expected = '2'; + assert.equal(actual, expected); + + actual = $input.attr('maxlength'); + expected = '20'; + assert.equal(actual, expected); + + actual = $input.attr('max'); + expected = '42'; + assert.equal(actual, expected); + + actual = $input.attr('multiple'); + expected = 'multiple'; + assert.equal(actual, expected); + + actual = $input.attr('name'); + expected = 'elementname'; + assert.equal(actual, expected); + + actual = $input.attr('pattern'); + expected = '(999)999-9999'; + assert.equal(actual, expected); + + actual = $input.attr('readonly'); + expected = 'readonly'; + assert.equal(actual, expected); + + actual = $input.attr('size'); + expected = '30'; + assert.equal(actual, expected); + + actual = $input.attr('spellcheck'); + expected = 'true'; + assert.equal(actual, expected); + + actual = $input.attr('step'); + expected = '2'; + assert.equal(actual, expected); + + actual = $input.attr('tabindex'); + expected = '1138'; + assert.equal(actual, expected); + +}); + + + From 3639fd37ed4520c3f0e9824df013c840d9f70916 Mon Sep 17 00:00:00 2001 From: Dan Monroe Date: Mon, 7 Sep 2015 23:33:50 -0400 Subject: [PATCH 3/5] Added input helper attributes for textareas as well. --- app/templates/components/paper-input.hbs | 17 ++++- tests/dummy/app/templates/input.hbs | 83 +++++++++++++++++++++++- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/app/templates/components/paper-input.hbs b/app/templates/components/paper-input.hbs index 37c727b28..f98bab8e6 100644 --- a/app/templates/components/paper-input.hbs +++ b/app/templates/components/paper-input.hbs @@ -7,7 +7,22 @@ {{/if}} {{#if textarea}} - {{textarea class="md-input" id=inputElementId placeholder=placeholder value=value focus-in="focusIn" key-down="keyDown" focus-out="focusOut" disabled=disabled required=required autofocus=autofocus}} + {{textarea class="md-input" id=inputElementId placeholder=placeholder value=value focus-in="focusIn" key-down="keyDown" focus-out="focusOut" disabled=disabled required=required autofocus=autofocus + name=attr-name + rows=attr-rows + cols=attr-cols + maxlength=attr-maxlength + tabindex=attr-tabindex + selectionEnd=attr-selectionEnd + selectionStart=attr-selectionStart + selectionDirection=attr-selectionDirection + wrap=attr-wrap + readonly=attr-readonly + form=attr-form + spellcheck=attr-spellcheck + + enter=event-enter + }} {{else}} {{input class="md-input" id=inputElementId placeholder=placeholder type=type value=value focus-in="focusIn" key-down="keyDown" focus-out="focusOut" disabled=disabled required=required autofocus=autofocus accept=attr-accept diff --git a/tests/dummy/app/templates/input.hbs b/tests/dummy/app/templates/input.hbs index a74d464c8..e1c333c6b 100644 --- a/tests/dummy/app/templates/input.hbs +++ b/tests/dummy/app/templates/input.hbs @@ -71,9 +71,88 @@ \{{paper-input placeholder="Phone Number" type="text" value=user.phone icon="phone" md-no-float=''}} \{{paper-input label="Email (required)" type="email" value=user.email icon="email" icon-class="email" required=true hideAllMessages=true}} \{{paper-input placeholder="Address" type="text" value=user.address icon="place"}} -</p> +</p>{{/code-block}} +

Input Attributes

+

+ These additional attributes will be passed through to the input helper inside of paper-input. + See the Ember Input Helpers for more information. + This is an example of using one of the attributes. +

+

+{{#code-block language="handlebars"}} + \{{paper-input attr-tabindex="2"}}{{/code-block}} +

+

Text Fields

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Ember PaperMatching Ember Input Helper Attribute
attr-acceptaccept
attr-autocompleteautocomplete
attr-autosaveautosave
attr-formform
attr-formactionformaction
attr-formenctypeformenctype
attr-formmethodformmethod
attr-formnovalidateformnovalidate
attr-formtargetformtarget
attr-heightheight
attr-inputmodeinputmode
attr-minmin
attr-maxlengthmaxlength
attr-maxmax
attr-multiplemultiple
attr-namename
attr-patternpattern
attr-readonlyreadonly
attr-selectionDirectionselectionDirection
attr-sizesize
attr-spellcheckspellcheck
attr-stepstep
attr-tabindextabindex
attr-widthwidth
+

+

Text Areas

+

+ + + + + + + + + + + + + + + + + + + + + +
Ember PaperMatching Ember Input Helper Attribute
attr-colscols
attr-formform
attr-maxlengthmaxlength
attr-namename
attr-readonlyreadonly
attr-rowsrows
attr-selectionDirectionselectionDirection
attr-selectionEndselectionEnd
attr-selectionStartselectionStart
attr-spellcheckspellcheck
attr-tabindextabindex
attr-wrapwrap
+

+

Events

+

+ You may also pass through an action when the user presses the enter key while on the input field or textarea. +

+

+ {{#code-block language="handlebars"}} + \{{paper-input event-enter="myEnterActionFunctionName"}}{{/code-block}} +

-{{/code-block}} {{/paper-content}} From f79864453634123d7dc44471b1c3bcecc1ae4396 Mon Sep 17 00:00:00 2001 From: Dan Monroe Date: Wed, 9 Sep 2015 10:13:35 -0400 Subject: [PATCH 4/5] Add support for custom validations in paper-input component --- CHANGELOG.md | 3 + addon/components/paper-input.js | 50 +++++-- tests/dummy/app/controllers/input.js | 34 +++++ tests/dummy/app/templates/input.hbs | 49 ++++++ tests/unit/components/paper-input-test.js | 174 ++++++++++++++++++++++ 5 files changed, 301 insertions(+), 9 deletions(-) create mode 100644 tests/dummy/app/controllers/input.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c37ad2b5..dd2252b0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Ember Paper Changelog +- [#171](https://github.com/miguelcobain/ember-paper/pull/171) Add support for custom validations in paper-input component. + + ### 0.2.8 (Aug 19, 2015) - [#154](https://github.com/miguelcobain/ember-paper/pull/154) Add support for inline paper-icon in paper-input component - [#152](https://github.com/miguelcobain/ember-paper/pull/152) Add support for .md-actions to {{paper-card}} diff --git a/addon/components/paper-input.js b/addon/components/paper-input.js index 096879836..330bda282 100644 --- a/addon/components/paper-input.js +++ b/addon/components/paper-input.js @@ -24,13 +24,15 @@ export default BaseFocusable.extend(ColorMixin, FlexMixin, { }), iconFloat: Ember.computed.and('icon', 'label'), + customValidation: null, + validate() { if (!this.get('isTouched')) { return false; } - var returnValue = false; + var valueIsInvalid = false; var currentValue = this.get('value'); var constraints = [ { @@ -51,24 +53,54 @@ export default BaseFocusable.extend(ColorMixin, FlexMixin, { { attr: 'maxlength', defaultError: 'Must not exceed ' + this.get('maxlength') + ' characters.', - isError: () => currentValue && currentValue.length > + this.get('maxlength') + isError: () => currentValue && currentValue.length > +this.get('maxlength') } ]; constraints.some(thisConstraint => { - if(thisConstraint.isError()) { + if (thisConstraint.isError()) { this.setError(thisConstraint); - returnValue = true; + valueIsInvalid = true; return true; } }); - return returnValue; + if (valueIsInvalid === true) { + return true; + } + + if (!Ember.isEmpty(this.get('customValidation'))) { + var validationObjects = Ember.A(); + var self = this; + + try { + if (!Ember.isArray(this.get('customValidation'))) { + validationObjects.addObject(this.get('customValidation')); + } else { + validationObjects = this.get('customValidation'); + } + + for (var thisValidationObj of validationObjects) { + if (typeof thisValidationObj.isError === 'function') { + if (thisValidationObj.isError.apply(null, [currentValue])) { + self.setError(thisValidationObj); + valueIsInvalid = true; + break; + } + } + } + } catch (error) { + Ember.Logger.error('Exception with custom validation: ', error); + } + + } + + return valueIsInvalid; }, setError(constraint) { - this.set('ng-message', constraint.attr); - this.set('errortext', this.get(constraint.attr + '-errortext') || constraint.defaultError); + this.set('ng-message', constraint.attr || 'custom'); + this.set('errortext', this.get(constraint.attr + '-errortext') || constraint.defaultError || constraint.errorMessage); }, actions: { @@ -76,11 +108,11 @@ export default BaseFocusable.extend(ColorMixin, FlexMixin, { // We resend action so other components can take use of the actions also ( if they want ). // Actions must be sent before focusing. this.sendAction('focus-in', value); - this.set('focus',true); + this.set('focus', true); }, focusOut(value) { this.sendAction('focus-out', value); - this.set('focus',false); + this.set('focus', false); this.set('isTouched', true); }, keyDown(value, event) { diff --git a/tests/dummy/app/controllers/input.js b/tests/dummy/app/controllers/input.js new file mode 100644 index 000000000..5b400a9c8 --- /dev/null +++ b/tests/dummy/app/controllers/input.js @@ -0,0 +1,34 @@ +import Ember from 'ember'; + +export default Ember.Controller.extend({ + + multipleConstraints: [ + { + 'errorMessage': 'Value is not even', + 'isError': (inputValue) => { + return (+inputValue % 2) === 1; + } + }, + { + 'errorMessage': 'Value does not equal 4', + 'isError': (inputValue) => { + return +inputValue !== 4; + } + } + ], + + singleContraint: { + 'errorMessage': 'Value does not equal 16', + 'isError': (inputValue) => { + return +inputValue !== 16; + } + }, + + emailValidation: { + 'errorMessage': 'Please provide email in a valid format', + 'isError': (inputValue) => { + var emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; + return !emailPattern.test(inputValue); + } + } +}); diff --git a/tests/dummy/app/templates/input.hbs b/tests/dummy/app/templates/input.hbs index e1c333c6b..ce03c47d2 100644 --- a/tests/dummy/app/templates/input.hbs +++ b/tests/dummy/app/templates/input.hbs @@ -154,5 +154,54 @@ \{{paper-input event-enter="myEnterActionFunctionName"}}{{/code-block}}

+

Custom Validations

+

+ In addition to {{#code-inline}}required{{/code-inline}}, {{#code-inline}}min{{/code-inline}}, {{#code-inline}}max{{/code-inline}}, and {{#code-inline}}maxlength{{/code-inline}}, you may define your own custom validations. + What you need to do is to define an object with two attributes, {{#code-inline}}errorMessage{{/code-inline}} and {{#code-inline}}isError{{/code-inline}}. + +

    +
  • errorMessage This is the text you want to display to the user when there is an error.
  • +
  • isError This is a function that takes one parameter, which is the input's value, and returns true when the value is invalid or false when the value is valid.
  • +
+

+

+ Here is an example of validating the input value matches typical email formats. +

+

Template

+ {{#code-block language="handlebars"}} + \{{paper-input label="E-mail" type="email" value=email customValidation=emailValidation}}{{/code-block}} +

+ Define your {{#code-inline}}emailValidation{{/code-inline}} object in your controller. +{{#code-block language="handlebars"}} +emailValidation: { + 'errorMessage': 'Please provide email in a valid format', + 'isError': (inputValue) => { + var emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; + return !emailPattern.test(inputValue); + } +}{{/code-block}} + {{paper-input label="E-mail" type="email" value=customemail customValidation=emailValidation}} +

+

+ You may also define multiple custom constraints by using an array of validation objects. +

+ {{#code-block language="handlebars"}} + \{{paper-input label="E-mail" type="email" value=email customValidation=multipleConstraints}}{{/code-block}} +{{#code-block language="handlebars"}} +multipleConstraints: [ + { + 'errorMessage': 'Value is not even', + 'isError': (inputValue) => { + return (+inputValue % 2) === 1; + } + }, + { + 'errorMessage': 'Value does not equal 4', + 'isError': (inputValue) => { + return +inputValue !== 4; + } + } +];}{{/code-block}} + {{paper-input label="Value should be even and equal 4." type="email" value=customMultiple customValidation=multipleConstraints}} {{/paper-content}} diff --git a/tests/unit/components/paper-input-test.js b/tests/unit/components/paper-input-test.js index cd4a50c31..e8ff60645 100644 --- a/tests/unit/components/paper-input-test.js +++ b/tests/unit/components/paper-input-test.js @@ -311,3 +311,177 @@ test('validates maxlength with no error', function(assert) { assert.equal(charCounterDiv[0].innerHTML, '10/10'); }); + +test('custom validations with one object and error', function(assert) { + var inputGroup, + errorDiv, + customValidationConstraint, + component = this.subject(), + expectedError = 'Value does not equal 4'; + + assert.expect(2); + + customValidationConstraint = { + 'errorMessage': 'Value does not equal 4', + 'isError': function (inputValue) { + return +inputValue !== 4; + } + }; + + component.set('customValidation', customValidationConstraint); + component.set('value', '5'); + component.set('isTouched', true); + + this.render(); + + inputGroup = document.getElementsByTagName('md-input-container'); + assert.equal(inputGroup.length === 1, true); + + errorDiv = document.getElementById('error-input-'+inputGroup[0].id); + assert.equal(errorDiv.innerHTML, expectedError, 'Error text does not equal ' + expectedError); + +}); + +test('custom validations with one object and no error', function(assert) { + var inputGroup, + errorDiv, + customValidationConstraint, + component = this.subject(); + + assert.expect(2); + + customValidationConstraint = { + 'errorMessage': 'Value does not equal 4', + 'isError': function (inputValue) { + return +inputValue !== 4; + } + }; + + component.set('customValidation', customValidationConstraint); + component.set('value', '4'); + component.set('isTouched', true); + + this.render(); + + inputGroup = document.getElementsByTagName('md-input-container'); + assert.equal(inputGroup.length === 1, true); + + errorDiv = document.getElementById('error-input-' + inputGroup[0].id); + assert.equal(errorDiv.innerText, ''); + +}); + +test('custom validations with multiple constraints and error caught for first constraint', function(assert) { + var inputGroup, + errorDiv, + customValidationArray, + component = this.subject(), + expectedError = 'Value is not even'; + + assert.expect(2); + + customValidationArray = [ + { + 'errorMessage': 'Value is not even', + 'isError': function isInputisNotEven(inputValue) { + return (+inputValue % 2) === 1; + } + }, + { + 'errorMessage': 'Value does not equal 4', + 'isError': function isInputFour(inputValue) { + return +inputValue !== 4; + } + } + ]; + + component.set('customValidation', customValidationArray); + component.set('value', '5'); + component.set('isTouched', true); + + this.render(); + + inputGroup = document.getElementsByTagName('md-input-container'); + assert.equal(inputGroup.length === 1, true); + + errorDiv = document.getElementById('error-input-'+inputGroup[0].id); + assert.equal(errorDiv.innerHTML, expectedError, 'Error text does not equal ' + expectedError); + +}); + + +test('custom validations with multiple constraints and error caught for second constraint', function(assert) { + var inputGroup, + errorDiv, + customValidationArray, + component = this.subject(), + expectedError = 'Value does not equal 4'; + + assert.expect(2); + + customValidationArray = [ + { + 'errorMessage': 'Value is not even', + 'isError': function isInputisNotEven(inputValue) { + return (+inputValue % 2) === 1; + } + }, + { + 'errorMessage': 'Value does not equal 4', + 'isError': function isInputFour(inputValue) { + return +inputValue !== 4; + } + } + ]; + + component.set('customValidation', customValidationArray); + component.set('value', '6'); + component.set('isTouched', true); + + this.render(); + + inputGroup = document.getElementsByTagName('md-input-container'); + assert.equal(inputGroup.length === 1, true); + + errorDiv = document.getElementById('error-input-'+inputGroup[0].id); + assert.equal(errorDiv.innerHTML, expectedError, 'Error text does not equal ' + expectedError); + +}); + +test('custom validations with multiple constraints and no error', function(assert) { + var inputGroup, + errorDiv, + customValidationArray, + component = this.subject(); + + assert.expect(2); + + customValidationArray = [ + { + 'errorMessage': 'Value is not even', + 'isError': function isInputisNotEven(inputValue) { + return (+inputValue % 2) === 1; + } + }, + { + 'errorMessage': 'Value does not equal 4', + 'isError': function isInputFour(inputValue) { + return +inputValue !== 4; + } + } + ]; + + component.set('customValidation', customValidationArray); + component.set('value', '4'); + component.set('isTouched', true); + + this.render(); + + inputGroup = document.getElementsByTagName('md-input-container'); + assert.equal(inputGroup.length === 1, true); + + errorDiv = document.getElementById('error-input-' + inputGroup[0].id); + assert.equal(errorDiv.innerText, ''); + +}); + From 77b3b60111d275053053f3138316fa56573ea9e9 Mon Sep 17 00:00:00 2001 From: Dan Monroe Date: Wed, 9 Sep 2015 10:20:50 -0400 Subject: [PATCH 5/5] Revert "Add support for custom validations in paper-input component" This reverts commit f79864453634123d7dc44471b1c3bcecc1ae4396. --- CHANGELOG.md | 3 - addon/components/paper-input.js | 50 ++----- tests/dummy/app/controllers/input.js | 34 ----- tests/dummy/app/templates/input.hbs | 49 ------ tests/unit/components/paper-input-test.js | 174 ---------------------- 5 files changed, 9 insertions(+), 301 deletions(-) delete mode 100644 tests/dummy/app/controllers/input.js diff --git a/CHANGELOG.md b/CHANGELOG.md index dd2252b0c..8c37ad2b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,5 @@ # Ember Paper Changelog -- [#171](https://github.com/miguelcobain/ember-paper/pull/171) Add support for custom validations in paper-input component. - - ### 0.2.8 (Aug 19, 2015) - [#154](https://github.com/miguelcobain/ember-paper/pull/154) Add support for inline paper-icon in paper-input component - [#152](https://github.com/miguelcobain/ember-paper/pull/152) Add support for .md-actions to {{paper-card}} diff --git a/addon/components/paper-input.js b/addon/components/paper-input.js index 330bda282..096879836 100644 --- a/addon/components/paper-input.js +++ b/addon/components/paper-input.js @@ -24,15 +24,13 @@ export default BaseFocusable.extend(ColorMixin, FlexMixin, { }), iconFloat: Ember.computed.and('icon', 'label'), - customValidation: null, - validate() { if (!this.get('isTouched')) { return false; } - var valueIsInvalid = false; + var returnValue = false; var currentValue = this.get('value'); var constraints = [ { @@ -53,54 +51,24 @@ export default BaseFocusable.extend(ColorMixin, FlexMixin, { { attr: 'maxlength', defaultError: 'Must not exceed ' + this.get('maxlength') + ' characters.', - isError: () => currentValue && currentValue.length > +this.get('maxlength') + isError: () => currentValue && currentValue.length > + this.get('maxlength') } ]; constraints.some(thisConstraint => { - if (thisConstraint.isError()) { + if(thisConstraint.isError()) { this.setError(thisConstraint); - valueIsInvalid = true; + returnValue = true; return true; } }); - if (valueIsInvalid === true) { - return true; - } - - if (!Ember.isEmpty(this.get('customValidation'))) { - var validationObjects = Ember.A(); - var self = this; - - try { - if (!Ember.isArray(this.get('customValidation'))) { - validationObjects.addObject(this.get('customValidation')); - } else { - validationObjects = this.get('customValidation'); - } - - for (var thisValidationObj of validationObjects) { - if (typeof thisValidationObj.isError === 'function') { - if (thisValidationObj.isError.apply(null, [currentValue])) { - self.setError(thisValidationObj); - valueIsInvalid = true; - break; - } - } - } - } catch (error) { - Ember.Logger.error('Exception with custom validation: ', error); - } - - } - - return valueIsInvalid; + return returnValue; }, setError(constraint) { - this.set('ng-message', constraint.attr || 'custom'); - this.set('errortext', this.get(constraint.attr + '-errortext') || constraint.defaultError || constraint.errorMessage); + this.set('ng-message', constraint.attr); + this.set('errortext', this.get(constraint.attr + '-errortext') || constraint.defaultError); }, actions: { @@ -108,11 +76,11 @@ export default BaseFocusable.extend(ColorMixin, FlexMixin, { // We resend action so other components can take use of the actions also ( if they want ). // Actions must be sent before focusing. this.sendAction('focus-in', value); - this.set('focus', true); + this.set('focus',true); }, focusOut(value) { this.sendAction('focus-out', value); - this.set('focus', false); + this.set('focus',false); this.set('isTouched', true); }, keyDown(value, event) { diff --git a/tests/dummy/app/controllers/input.js b/tests/dummy/app/controllers/input.js deleted file mode 100644 index 5b400a9c8..000000000 --- a/tests/dummy/app/controllers/input.js +++ /dev/null @@ -1,34 +0,0 @@ -import Ember from 'ember'; - -export default Ember.Controller.extend({ - - multipleConstraints: [ - { - 'errorMessage': 'Value is not even', - 'isError': (inputValue) => { - return (+inputValue % 2) === 1; - } - }, - { - 'errorMessage': 'Value does not equal 4', - 'isError': (inputValue) => { - return +inputValue !== 4; - } - } - ], - - singleContraint: { - 'errorMessage': 'Value does not equal 16', - 'isError': (inputValue) => { - return +inputValue !== 16; - } - }, - - emailValidation: { - 'errorMessage': 'Please provide email in a valid format', - 'isError': (inputValue) => { - var emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; - return !emailPattern.test(inputValue); - } - } -}); diff --git a/tests/dummy/app/templates/input.hbs b/tests/dummy/app/templates/input.hbs index ce03c47d2..e1c333c6b 100644 --- a/tests/dummy/app/templates/input.hbs +++ b/tests/dummy/app/templates/input.hbs @@ -154,54 +154,5 @@ \{{paper-input event-enter="myEnterActionFunctionName"}}{{/code-block}}

-

Custom Validations

-

- In addition to {{#code-inline}}required{{/code-inline}}, {{#code-inline}}min{{/code-inline}}, {{#code-inline}}max{{/code-inline}}, and {{#code-inline}}maxlength{{/code-inline}}, you may define your own custom validations. - What you need to do is to define an object with two attributes, {{#code-inline}}errorMessage{{/code-inline}} and {{#code-inline}}isError{{/code-inline}}. - -

    -
  • errorMessage This is the text you want to display to the user when there is an error.
  • -
  • isError This is a function that takes one parameter, which is the input's value, and returns true when the value is invalid or false when the value is valid.
  • -
-

-

- Here is an example of validating the input value matches typical email formats. -

-

Template

- {{#code-block language="handlebars"}} - \{{paper-input label="E-mail" type="email" value=email customValidation=emailValidation}}{{/code-block}} -

- Define your {{#code-inline}}emailValidation{{/code-inline}} object in your controller. -{{#code-block language="handlebars"}} -emailValidation: { - 'errorMessage': 'Please provide email in a valid format', - 'isError': (inputValue) => { - var emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; - return !emailPattern.test(inputValue); - } -}{{/code-block}} - {{paper-input label="E-mail" type="email" value=customemail customValidation=emailValidation}} -

-

- You may also define multiple custom constraints by using an array of validation objects. -

- {{#code-block language="handlebars"}} - \{{paper-input label="E-mail" type="email" value=email customValidation=multipleConstraints}}{{/code-block}} -{{#code-block language="handlebars"}} -multipleConstraints: [ - { - 'errorMessage': 'Value is not even', - 'isError': (inputValue) => { - return (+inputValue % 2) === 1; - } - }, - { - 'errorMessage': 'Value does not equal 4', - 'isError': (inputValue) => { - return +inputValue !== 4; - } - } -];}{{/code-block}} - {{paper-input label="Value should be even and equal 4." type="email" value=customMultiple customValidation=multipleConstraints}} {{/paper-content}} diff --git a/tests/unit/components/paper-input-test.js b/tests/unit/components/paper-input-test.js index e8ff60645..cd4a50c31 100644 --- a/tests/unit/components/paper-input-test.js +++ b/tests/unit/components/paper-input-test.js @@ -311,177 +311,3 @@ test('validates maxlength with no error', function(assert) { assert.equal(charCounterDiv[0].innerHTML, '10/10'); }); - -test('custom validations with one object and error', function(assert) { - var inputGroup, - errorDiv, - customValidationConstraint, - component = this.subject(), - expectedError = 'Value does not equal 4'; - - assert.expect(2); - - customValidationConstraint = { - 'errorMessage': 'Value does not equal 4', - 'isError': function (inputValue) { - return +inputValue !== 4; - } - }; - - component.set('customValidation', customValidationConstraint); - component.set('value', '5'); - component.set('isTouched', true); - - this.render(); - - inputGroup = document.getElementsByTagName('md-input-container'); - assert.equal(inputGroup.length === 1, true); - - errorDiv = document.getElementById('error-input-'+inputGroup[0].id); - assert.equal(errorDiv.innerHTML, expectedError, 'Error text does not equal ' + expectedError); - -}); - -test('custom validations with one object and no error', function(assert) { - var inputGroup, - errorDiv, - customValidationConstraint, - component = this.subject(); - - assert.expect(2); - - customValidationConstraint = { - 'errorMessage': 'Value does not equal 4', - 'isError': function (inputValue) { - return +inputValue !== 4; - } - }; - - component.set('customValidation', customValidationConstraint); - component.set('value', '4'); - component.set('isTouched', true); - - this.render(); - - inputGroup = document.getElementsByTagName('md-input-container'); - assert.equal(inputGroup.length === 1, true); - - errorDiv = document.getElementById('error-input-' + inputGroup[0].id); - assert.equal(errorDiv.innerText, ''); - -}); - -test('custom validations with multiple constraints and error caught for first constraint', function(assert) { - var inputGroup, - errorDiv, - customValidationArray, - component = this.subject(), - expectedError = 'Value is not even'; - - assert.expect(2); - - customValidationArray = [ - { - 'errorMessage': 'Value is not even', - 'isError': function isInputisNotEven(inputValue) { - return (+inputValue % 2) === 1; - } - }, - { - 'errorMessage': 'Value does not equal 4', - 'isError': function isInputFour(inputValue) { - return +inputValue !== 4; - } - } - ]; - - component.set('customValidation', customValidationArray); - component.set('value', '5'); - component.set('isTouched', true); - - this.render(); - - inputGroup = document.getElementsByTagName('md-input-container'); - assert.equal(inputGroup.length === 1, true); - - errorDiv = document.getElementById('error-input-'+inputGroup[0].id); - assert.equal(errorDiv.innerHTML, expectedError, 'Error text does not equal ' + expectedError); - -}); - - -test('custom validations with multiple constraints and error caught for second constraint', function(assert) { - var inputGroup, - errorDiv, - customValidationArray, - component = this.subject(), - expectedError = 'Value does not equal 4'; - - assert.expect(2); - - customValidationArray = [ - { - 'errorMessage': 'Value is not even', - 'isError': function isInputisNotEven(inputValue) { - return (+inputValue % 2) === 1; - } - }, - { - 'errorMessage': 'Value does not equal 4', - 'isError': function isInputFour(inputValue) { - return +inputValue !== 4; - } - } - ]; - - component.set('customValidation', customValidationArray); - component.set('value', '6'); - component.set('isTouched', true); - - this.render(); - - inputGroup = document.getElementsByTagName('md-input-container'); - assert.equal(inputGroup.length === 1, true); - - errorDiv = document.getElementById('error-input-'+inputGroup[0].id); - assert.equal(errorDiv.innerHTML, expectedError, 'Error text does not equal ' + expectedError); - -}); - -test('custom validations with multiple constraints and no error', function(assert) { - var inputGroup, - errorDiv, - customValidationArray, - component = this.subject(); - - assert.expect(2); - - customValidationArray = [ - { - 'errorMessage': 'Value is not even', - 'isError': function isInputisNotEven(inputValue) { - return (+inputValue % 2) === 1; - } - }, - { - 'errorMessage': 'Value does not equal 4', - 'isError': function isInputFour(inputValue) { - return +inputValue !== 4; - } - } - ]; - - component.set('customValidation', customValidationArray); - component.set('value', '4'); - component.set('isTouched', true); - - this.render(); - - inputGroup = document.getElementsByTagName('md-input-container'); - assert.equal(inputGroup.length === 1, true); - - errorDiv = document.getElementById('error-input-' + inputGroup[0].id); - assert.equal(errorDiv.innerText, ''); - -}); -