<!-- @license Copyright (c) 2015 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../iron-validatable-behavior/iron-validatable-behavior.html"> <script> /* `<iron-input>` adds two-way binding and custom validators using `Polymer.IronValidatorBehavior` to `<input>`. ### Two-way binding By default you can only get notified of changes to an `input`'s `value` due to user input: <input value="{{myValue::input}}"> `iron-input` adds the `bind-value` property that mirrors the `value` property, and can be used for two-way data binding. `bind-value` will notify if it is changed either by user input or by script. <input is="iron-input" bind-value="{{myValue}}"> ### Custom validators You can use custom validators that implement `Polymer.IronValidatorBehavior` with `<iron-input>`. <input is="iron-input" validator="my-custom-validator"> ### Stopping invalid input It may be desirable to only allow users to enter certain characters. You can use the `prevent-invalid-input` and `allowed-pattern` attributes together to accomplish this. This feature is separate from validation, and `allowed-pattern` does not affect how the input is validated. <!-- only allow characters that match [0-9] --> <input is="iron-input" prevent-invalid-input allowed-pattern="[0-9]"> @hero hero.svg @demo demo/index.html */ Polymer({ is: 'iron-input', extends: 'input', behaviors: [ Polymer.IronValidatableBehavior ], properties: { /** * Use this property instead of `value` for two-way data binding. */ bindValue: { observer: '_bindValueChanged', type: String }, /** * Set to true to prevent the user from entering invalid input. The new input characters are * matched with `allowedPattern` if it is set, otherwise it will use the `pattern` attribute if * set, or the `type` attribute (only supported for `type=number`). */ preventInvalidInput: { type: Boolean }, /** * Regular expression to match valid input characters. */ allowedPattern: { type: String }, _previousValidInput: { type: String, value: '' }, _patternAlreadyChecked: { type: Boolean, value: false } }, listeners: { 'input': '_onInput', 'keypress': '_onKeypress' }, get _patternRegExp() { var pattern; if (this.allowedPattern) { pattern = new RegExp(this.allowedPattern); } else if (this.pattern) { pattern = new RegExp(this.pattern); } else { switch (this.type) { case 'number': pattern = /[0-9.,e-]/; break; } } return pattern; }, ready: function() { this.bindValue = this.value; }, /** * @suppress {checkTypes} */ _bindValueChanged: function() { if (this.value !== this.bindValue) { this.value = !(this.bindValue || this.bindValue === 0) ? '' : this.bindValue; } // manually notify because we don't want to notify until after setting value this.fire('bind-value-changed', {value: this.bindValue}); }, _onInput: function() { // Need to validate each of the characters pasted if they haven't // been validated inside `_onKeypress` already. if (this.preventInvalidInput && !this._patternAlreadyChecked) { var valid = this._checkPatternValidity(); if (!valid) { this.value = this._previousValidInput; } } this.bindValue = this.value; this._previousValidInput = this.value; this._patternAlreadyChecked = false; }, _isPrintable: function(event) { // What a control/printable character is varies wildly based on the browser. // - most control characters (arrows, backspace) do not send a `keypress` event // in Chrome, but the *do* on Firefox // - in Firefox, when they do send a `keypress` event, control chars have // a charCode = 0, keyCode = xx (for ex. 40 for down arrow) // - printable characters always send a keypress event. // - in Firefox, printable chars always have a keyCode = 0. In Chrome, the keyCode // always matches the charCode. // None of this makes any sense. // For these keys, ASCII code == browser keycode. var anyNonPrintable = (event.keyCode == 8) || // backspace (event.keyCode == 9) || // tab (event.keyCode == 13) || // enter (event.keyCode == 27); // escape // For these keys, make sure it's a browser keycode and not an ASCII code. var mozNonPrintable = (event.keyCode == 19) || // pause (event.keyCode == 20) || // caps lock (event.keyCode == 45) || // insert (event.keyCode == 46) || // delete (event.keyCode == 144) || // num lock (event.keyCode == 145) || // scroll lock (event.keyCode > 32 && event.keyCode < 41) || // page up/down, end, home, arrows (event.keyCode > 111 && event.keyCode < 124); // fn keys return !anyNonPrintable && !(event.charCode == 0 && mozNonPrintable); }, _onKeypress: function(event) { if (!this.preventInvalidInput && this.type !== 'number') { return; } var regexp = this._patternRegExp; if (!regexp) { return; } // Handle special keys and backspace if (event.metaKey || event.ctrlKey || event.altKey) return; // Check the pattern either here or in `_onInput`, but not in both. this._patternAlreadyChecked = true; var thisChar = String.fromCharCode(event.charCode); if (this._isPrintable(event) && !regexp.test(thisChar)) { event.preventDefault(); } }, _checkPatternValidity: function() { var regexp = this._patternRegExp; if (!regexp) { return true; } for (var i = 0; i < this.value.length; i++) { if (!regexp.test(this.value[i])) { return false; } } return true; }, /** * Returns true if `value` is valid. The validator provided in `validator` will be used first, * then any constraints. * @return {boolean} True if the value is valid. */ validate: function() { // Empty, non-required input is valid. if (!this.required && this.value == '') { this.invalid = false; return true; } var valid; if (this.hasValidator()) { valid = Polymer.IronValidatableBehavior.validate.call(this, this.value); } else { this.invalid = !this.validity.valid; valid = this.validity.valid; } this.fire('iron-input-validate'); return valid; } }); /* The `iron-input-validate` event is fired whenever `validate()` is called. @event iron-input-validate */ </script>