|
18 | 18 | *
|
19 | 19 | * <core-input multiline placeholder="Enter multiple lines here"></core-input>
|
20 | 20 | *
|
21 |
| - * The text input's value is considered "committed" if the user hits the `enter` |
22 |
| - * key or blurs the input after changing the value. The "change" event is fired |
| 21 | + * The text input's value is considered "committed" if the user hits the "enter" |
| 22 | + * key or blurs the input after changing the value. The `change` event is fired |
23 | 23 | * when the value becomes committed, and the committed value is stored in the
|
24 |
| - * "value" property. The current value of the input is stored in the "inputValue" |
| 24 | + * `value` property. The current value of the input is stored in the `inputValue` |
25 | 25 | * property.
|
26 | 26 | *
|
27 |
| - * core-input also can optionally validate the value by providing it with a |
28 |
| - * regular expression to match against, or a validation function. The |
29 |
| - * "input-invalid" event is fired if the input value changes and is invalid. |
30 |
| - * The "invalid" property is also available for observation. |
| 27 | + * Validation |
| 28 | + * ---------- |
| 29 | + * |
| 30 | + * core-input can optionally validate the value using the HTML5 constraints API, |
| 31 | + * similar to native inputs. There are two methods to enable input validation: |
| 32 | + * |
| 33 | + * 1. By setting the `type` attribute. For example, setting it to `email` will |
| 34 | + * check the value is a valid email, and setting it to `number` will check |
| 35 | + * the input is a number. |
| 36 | + * |
| 37 | + * 2. By setting attributes related to validation. The attributes are `pattern`, |
| 38 | + * `min`, `max`, `step` and `required`. |
| 39 | + * |
| 40 | + * Only `required` is supported for multiline inputs currently. |
31 | 41 | *
|
32 | 42 | * Example:
|
33 | 43 | *
|
34 |
| - * // valid only if the value is a number |
35 |
| - * <core-input validate="^[0-9]*$" on-input-invalid="{{inputInvalidAction}}"></core-input> |
| 44 | + * <core-input type="email" placeholder="enter your email"></core-input> |
| 45 | + * |
| 46 | + * <core-input type="number" min="5" placeholder="enter a number greater than or equal to 5"></core-input> |
36 | 47 | *
|
37 |
| - * this.$.input.validate = /^[0-9]*$/; // valid only if the value is a number |
| 48 | + * <core-input pattern=".*abc.*" placeholder="enter something containing 'abc'"></core-input> |
38 | 49 | *
|
39 |
| - * this.$.input2.validate = function(value) { |
40 |
| - * return value === 'foo'; // valid only if the value is 'foo' |
41 |
| - * } |
| 50 | + * See https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation |
| 51 | + * for more info on validation. |
42 | 52 | *
|
43 | 53 | * @group Polymer Core Elements
|
44 | 54 | * @element core-input
|
|
89 | 99 | </template>
|
90 | 100 |
|
91 | 101 | <template if="{{!multiline}}">
|
92 |
| - <input id="input" value="{{inputValue}}" disabled?="{{disabled}}" type="{{type}}" placeholder="{{placeholder}}" required?="{{required}}" readonly?="{{readonly}}" aria-label="{{label || placeholder}}" aria-invalid="{{invalid}}" on-change="{{inputChangeAction}}" on-focus="{{inputFocusAction}}" on-blur="{{inputBlurAction}}"> |
| 102 | + <input id="input" value="{{inputValue}}" disabled?="{{disabled}}" type="{{type}}" placeholder="{{placeholder}}" required?="{{required}}" readonly?="{{readonly}}" pattern="{{pattern}}" min="{{min}}" max="{{max}}" step="{{step}}" maxlength="{{maxlength}}" aria-label="{{label || placeholder}}" aria-invalid="{{invalid}}" on-keydown="{{keydownAction}}" on-change="{{inputChangeAction}}" on-focus="{{inputFocusAction}}" on-blur="{{inputBlurAction}}"> |
93 | 103 | </template>
|
94 | 104 |
|
95 | 105 | </template>
|
|
118 | 128 | */
|
119 | 129 | disabled: false,
|
120 | 130 |
|
121 |
| - /** |
122 |
| - * Set the input type. Not supported for `multiline`. |
123 |
| - * |
124 |
| - * @attribute type |
125 |
| - * @type string |
126 |
| - * @default text |
127 |
| - */ |
128 |
| - type: 'text', |
129 |
| - |
130 | 131 | /**
|
131 | 132 | * If true, the user cannot modify the value of the input.
|
132 | 133 | *
|
|
136 | 137 | */
|
137 | 138 | readonly: false,
|
138 | 139 |
|
139 |
| - /** |
140 |
| - * If true, the input is invalid until the value becomes non-null. |
141 |
| - * |
142 |
| - * @attribute required |
143 |
| - * @type boolean |
144 |
| - * @default false |
145 |
| - */ |
146 |
| - required: false, |
147 |
| - |
148 | 140 | /**
|
149 | 141 | * If true, this input accepts multi-line input like a `<textarea>`
|
150 | 142 | *
|
|
187 | 179 | * @default ''
|
188 | 180 | */
|
189 | 181 | value: '',
|
190 |
| - |
| 182 | + |
| 183 | + /** |
| 184 | + * Set the input type. Not supported for `multiline`. |
| 185 | + * |
| 186 | + * @attribute type |
| 187 | + * @type string |
| 188 | + * @default text |
| 189 | + */ |
| 190 | + type: 'text', |
| 191 | + |
| 192 | + /** |
| 193 | + * If true, the input is invalid if its value is null. |
| 194 | + * |
| 195 | + * @attribute required |
| 196 | + * @type boolean |
| 197 | + * @default false |
| 198 | + */ |
| 199 | + required: false, |
| 200 | + |
191 | 201 | /**
|
192 |
| - * If this property is not null, the text input's inputValue will be |
193 |
| - * validated. You can validate the value with either a regular expression |
194 |
| - * or a custom function. |
| 202 | + * A regular expression to validate the input value against. See |
| 203 | + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes |
| 204 | + * for more info. Not supported if `multiline` is true. |
195 | 205 | *
|
196 |
| - * To use a regular expression, set this property to a RegExp object or |
197 |
| - * a string containing the regular expression to match against. To use a |
198 |
| - * custom validator, set this property to a function with the signature |
199 |
| - * function(value) that returns a boolean. The input is valid if the |
200 |
| - * function returns true. |
| 206 | + * @attribute pattern |
| 207 | + * @type string |
| 208 | + * @default '.*' |
| 209 | + */ |
| 210 | + // FIXME(yvonne): The default is set to .* because we can't bind to pattern such |
| 211 | + // that the attribute is unset if pattern is null. |
| 212 | + pattern: '.*', |
| 213 | + |
| 214 | + /** |
| 215 | + * If set, the input is invalid if the value is less than this property. See |
| 216 | + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes |
| 217 | + * for more info. Not supported if `multiline` is true. |
201 | 218 | *
|
202 |
| - * Example: |
| 219 | + * @attribute min |
| 220 | + */ |
| 221 | + min: null, |
| 222 | + |
| 223 | + /** |
| 224 | + * If set, the input is invalid if the value is greater than this property. See |
| 225 | + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes |
| 226 | + * for more info. Not supported if `multiline` is true. |
203 | 227 | *
|
204 |
| - * // valid only if the value is a number |
205 |
| - * <core-input validate="^[0-9]*$"></core-input> |
206 |
| - * |
207 |
| - * // valid only if the value is a number |
208 |
| - * this.$.input.validate = /^[0-9]*$/; |
| 228 | + * @attribute max |
| 229 | + */ |
| 230 | + max: null, |
| 231 | + |
| 232 | + /** |
| 233 | + * If set, the input is invalid if the value is not `min` plus an integral multiple |
| 234 | + * of this property. See |
| 235 | + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Validation-related_attributes |
| 236 | + * for more info. Not supported if `multiline` is true. |
209 | 237 | *
|
210 |
| - * this.$.input2.validate = function(value) { |
211 |
| - * // valid only if the value is 'foo' |
212 |
| - * return value === 'foo'; |
213 |
| - * } |
| 238 | + * @attribute step |
| 239 | + */ |
| 240 | + step: null, |
| 241 | + |
| 242 | + /** |
| 243 | + * The maximum length of the input value. |
214 | 244 | *
|
215 |
| - * @attribute validate |
216 |
| - * @type string|RegExp|Function(value) |
217 |
| - * @default null |
| 245 | + * @attribute maxlength |
| 246 | + * @type number |
218 | 247 | */
|
219 |
| - validate: null, |
| 248 | + maxlength: null, |
220 | 249 |
|
221 | 250 | /**
|
222 | 251 | * If this property is true, the text input's inputValue failed validation.
|
|
225 | 254 | * @type boolean
|
226 | 255 | * @default false
|
227 | 256 | */
|
228 |
| - invalid: false, |
| 257 | + invalid: false |
229 | 258 | },
|
230 | 259 |
|
231 | 260 | ready: function() {
|
232 | 261 | this.handleTabindex(this.getAttribute('tabindex'));
|
233 | 262 | },
|
234 | 263 |
|
235 |
| - validateValue: function() { |
236 |
| - var valid = true; |
237 |
| - |
238 |
| - if (this.validate) { |
239 |
| - |
240 |
| - if (!this.inputValue) { |
241 |
| - valid = !this.required; |
242 |
| - } else if (typeof this.validate === 'string') { |
243 |
| - valid = new RegExp(this.validate).exec(this.inputValue); |
244 |
| - } else if (this.validate.exec) { |
245 |
| - valid = this.validate.exec(this.inputValue); |
246 |
| - } else if (this.validate instanceof Function) { |
247 |
| - valid = this.validate.call(this, this.inputValue); |
248 |
| - } |
249 |
| - |
250 |
| - } else if (this.required) { |
251 |
| - valid = !!this.inputValue; |
252 |
| - } |
253 |
| - |
254 |
| - this.invalid = !valid; |
255 |
| - }, |
256 |
| - |
257 | 264 | invalidChanged: function() {
|
258 | 265 | this.classList.toggle('invalid', this.invalid);
|
259 |
| - this.fire('input-'+ this.invalid ? 'invalid' : 'valid', {value: this.inputValue}); |
| 266 | + this.fire('input-'+ (this.invalid ? 'invalid' : 'valid'), {value: this.inputValue}); |
260 | 267 | },
|
261 | 268 |
|
262 | 269 | inputValueChanged: function() {
|
263 |
| - if (this.validate || this.required) { |
264 |
| - this.validateValue(); |
265 |
| - } |
| 270 | + this.updateValidity_(); |
266 | 271 | },
|
267 | 272 |
|
268 | 273 | valueChanged: function() {
|
269 | 274 | this.inputValue = this.value;
|
270 | 275 | },
|
271 | 276 |
|
272 | 277 | requiredChanged: function() {
|
273 |
| - this.validateValue(); |
| 278 | + this.updateValidity_(); |
274 | 279 | },
|
275 | 280 |
|
276 | 281 | attributeChanged: function(attr, oldVal, curVal) {
|
|
296 | 301 | this.value = this.inputValue;
|
297 | 302 | },
|
298 | 303 |
|
| 304 | + updateValidity_: function() { |
| 305 | + if (this.$.input.willValidate) { |
| 306 | + this.invalid = !this.$.input.validity.valid; |
| 307 | + } |
| 308 | + }, |
| 309 | + |
| 310 | + keydownAction: function() { |
| 311 | + // for type = number, the value is the empty string unless the input is a valid number. |
| 312 | + // FIXME(yvonne): check other types |
| 313 | + if (this.type === 'number') { |
| 314 | + this.async(function() { |
| 315 | + this.updateValidity_(); |
| 316 | + }); |
| 317 | + } |
| 318 | + }, |
| 319 | + |
299 | 320 | inputChangeAction: function() {
|
300 | 321 | this.commit();
|
301 | 322 | if (!window.ShadowDOMPolyfill) {
|
|
347 | 368 | this.$.input.focus();
|
348 | 369 | },
|
349 | 370 |
|
350 |
| - setSelectionRange: function( selectionStart, selectionEnd, selectionDirection ) { |
| 371 | + setSelectionRange: function(selectionStart, selectionEnd, selectionDirection) { |
351 | 372 | // forward setSelectionRange method to the internal input / textarea element
|
352 |
| - this.$.input.setSelectionRange( selectionStart, selectionEnd, selectionDirection ); |
| 373 | + this.$.input.setSelectionRange(selectionStart, selectionEnd, selectionDirection); |
353 | 374 | },
|
354 | 375 |
|
355 |
| - setRangeText: function( replacement, start, end, selectMode ) { |
| 376 | + setRangeText: function(replacement, start, end, selectMode) { |
356 | 377 | // forward setRangeText method to the internal input element
|
357 |
| - if ( !this.multiline ) { |
358 |
| - this.$.input.setRangeText( replacement, start, end, selectMode ); |
| 378 | + if (!this.multiline) { |
| 379 | + this.$.input.setRangeText(replacement, start, end, selectMode); |
359 | 380 | }
|
360 | 381 | },
|
361 | 382 |
|
362 |
| - stepDown: function( n ) { |
| 383 | + stepDown: function(n) { |
363 | 384 | // forward stepDown method to the internal input element
|
364 |
| - if ( !this.multiline ) { |
365 |
| - this.$.input.stepDown( n ); |
| 385 | + if (!this.multiline) { |
| 386 | + this.$.input.stepDown(n); |
366 | 387 | }
|
367 | 388 | },
|
368 | 389 |
|
369 |
| - stepUp: function( n ) { |
| 390 | + stepUp: function(n) { |
370 | 391 | // forward stepUp method to the internal input element
|
371 |
| - if ( !this.multiline ) { |
372 |
| - this.$.input.stepUp( n ); |
| 392 | + if (!this.multiline) { |
| 393 | + this.$.input.stepUp(n); |
373 | 394 | }
|
| 395 | + }, |
| 396 | + |
| 397 | + get willValidate() { |
| 398 | + return this.$.input.willValidate; |
| 399 | + }, |
| 400 | + |
| 401 | + get validity() { |
| 402 | + return this.$.input.validity; |
| 403 | + }, |
| 404 | + |
| 405 | + get validationMessage() { |
| 406 | + return this.$.input.validationMessage; |
| 407 | + }, |
| 408 | + |
| 409 | + checkValidity: function() { |
| 410 | + var r = this.$.input.checkValidity(); |
| 411 | + this.updateValidity_(); |
| 412 | + return r; |
| 413 | + }, |
| 414 | + |
| 415 | + setCustomValidity: function(message) { |
| 416 | + this.$.input.setCustomValidity(message); |
| 417 | + this.updateValidity_(); |
374 | 418 | }
|
375 | 419 |
|
376 | 420 | });
|
377 |
| - |
378 |
| - CoreInput = function() { |
379 |
| - return document.createElement('core-input'); |
380 |
| - }; |
381 |
| - CoreInput.validate = { |
382 |
| - number: /^[0-9]*$/ |
383 |
| - }; |
384 | 421 | </script>
|
385 | 422 |
|
386 | 423 | </polymer-element>
|
0 commit comments