Skip to content

Commit

Permalink
[FEATURE ember-glimmer-angle-bracket-built-ins]
Browse files Browse the repository at this point in the history
Implement `<Input />`.
  • Loading branch information
chancancode committed Mar 17, 2019
1 parent 05472ed commit 0617786
Show file tree
Hide file tree
Showing 18 changed files with 1,764 additions and 263 deletions.
4 changes: 2 additions & 2 deletions packages/@ember/-internals/glimmer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@
export { default as RootTemplate } from './lib/templates/root';
export { default as template } from './lib/template';
export { default as Checkbox } from './lib/components/checkbox';
export { default as TextField } from './lib/components/text_field';
export { default as TextArea } from './lib/components/text_area';
export { default as TextField } from './lib/components/text-field';
export { default as TextArea } from './lib/components/textarea';
export { default as LinkComponent } from './lib/components/link-to';
export { default as Component, ROOT_REF } from './lib/component';
export { default as Helper, helper } from './lib/helper';
Expand Down
177 changes: 177 additions & 0 deletions packages/@ember/-internals/glimmer/lib/components/input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/**
@module @ember/component
*/
import { computed } from '@ember/-internals/metal';
import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { assert } from '@ember/debug';
import { DEBUG } from '@glimmer/env';
import Component from '../component';

let Input: any;

if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
/**
The `{{input}}` helper lets you create an HTML `<input />` component.
It causes a `TextField` component to be rendered. For more info,
see the [TextField](/api/ember/release/classes/TextField) docs and
the [templates guide](https://guides.emberjs.com/release/templates/input-helpers/).
```handlebars
{{input value="987"}}
```
renders as:
```HTML
<input type="text" value="987" />
```
### Text field
If no `type` option is specified, a default of type 'text' is used.
Many of the standard HTML attributes may be passed to this helper.
<table>
<tr><td>`readonly`</td><td>`required`</td><td>`autofocus`</td></tr>
<tr><td>`value`</td><td>`placeholder`</td><td>`disabled`</td></tr>
<tr><td>`size`</td><td>`tabindex`</td><td>`maxlength`</td></tr>
<tr><td>`name`</td><td>`min`</td><td>`max`</td></tr>
<tr><td>`pattern`</td><td>`accept`</td><td>`autocomplete`</td></tr>
<tr><td>`autosave`</td><td>`formaction`</td><td>`formenctype`</td></tr>
<tr><td>`formmethod`</td><td>`formnovalidate`</td><td>`formtarget`</td></tr>
<tr><td>`height`</td><td>`inputmode`</td><td>`multiple`</td></tr>
<tr><td>`step`</td><td>`width`</td><td>`form`</td></tr>
<tr><td>`selectionDirection`</td><td>`spellcheck`</td><td>&nbsp;</td></tr>
</table>
When set to a quoted string, these values will be directly applied to the HTML
element. When left unquoted, these values will be bound to a property on the
template's current rendering context (most typically a controller instance).
A very common use of this helper is to bind the `value` of an input to an Object's attribute:
```handlebars
Search:
{{input value=searchWord}}
```
In this example, the initial value in the `<input />` will be set to the value of `searchWord`.
If the user changes the text, the value of `searchWord` will also be updated.
### Actions
The helper can send multiple actions based on user events.
The action property defines the action which is sent when
the user presses the return key.
```handlebars
{{input action="submit"}}
```
The helper allows some user events to send actions.
* `enter`
* `insert-newline`
* `escape-press`
* `focus-in`
* `focus-out`
* `key-press`
* `key-up`
For example, if you desire an action to be sent when the input is blurred,
you only need to setup the action name to the event name property.
```handlebars
{{input focus-out="alertMessage"}}
```
See more about [Text Support Actions](/api/ember/release/classes/TextField)
### Extending `TextField`
Internally, `{{input type="text"}}` creates an instance of `TextField`, passing
arguments from the helper to `TextField`'s `create` method. You can extend the
capabilities of text inputs in your applications by reopening this class. For example,
if you are building a Bootstrap project where `data-*` attributes are used, you
can add one to the `TextField`'s `attributeBindings` property:
```javascript
import TextField from '@ember/component/text-field';
TextField.reopen({
attributeBindings: ['data-error']
});
```
Keep in mind when writing `TextField` subclasses that `TextField`
itself extends `Component`. Expect isolated component semantics, not
legacy 1.x view semantics (like `controller` being present).
See more about [Ember components](/api/ember/release/classes/Component)
### Checkbox
Checkboxes are special forms of the `{{input}}` helper. To create a `<checkbox />`:
```handlebars
Emberize Everything:
{{input type="checkbox" name="isEmberized" checked=isEmberized}}
```
This will bind checked state of this checkbox to the value of `isEmberized` -- if either one changes,
it will be reflected in the other.
The following HTML attributes can be set via the helper:
* `checked`
* `disabled`
* `tabindex`
* `indeterminate`
* `name`
* `autofocus`
* `form`
### Extending `Checkbox`
Internally, `{{input type="checkbox"}}` creates an instance of `Checkbox`, passing
arguments from the helper to `Checkbox`'s `create` method. You can extend the
capablilties of checkbox inputs in your applications by reopening this class. For example,
if you wanted to add a css class to all checkboxes in your application:
```javascript
import Checkbox from '@ember/component/checkbox';
Checkbox.reopen({
classNames: ['my-app-checkbox']
});
```
@method input
@for Ember.Templates.helpers
@param {Hash} options
@public
*/
Input = Component.extend({
tagName: '',

isCheckbox: computed('type', function(this: { type?: unknown }) {
return this.type === 'checkbox';
}),
});

Input.toString = () => '@ember/component/input';

if (DEBUG) {
const UNSET = {};

Input.reopen({
value: UNSET,

didReceiveAttrs() {
this._super();

assert(
"`<Input @type='checkbox' @value={{...}} />` is not supported; " +
"please use `<Input @type='checkbox' @checked={{...}} />` instead.",
!(this.type === 'checkbox' && this.value !== UNSET)
);
},
});
}
}

export default Input;
2 changes: 1 addition & 1 deletion packages/@ember/-internals/glimmer/lib/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ export default class RuntimeResolver implements IRuntimeResolver<OwnedTemplateMe

assert(
'Invoking `{{input}}` using angle bracket syntax or `component` helper is not yet supported.',
_name !== 'input'
EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS || _name !== 'input'
);

let name = _name;
Expand Down
19 changes: 17 additions & 2 deletions packages/@ember/-internals/glimmer/lib/setup-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
import { Simple } from '@glimmer/interfaces';
import Component from './component';
import Checkbox from './components/checkbox';
import Input from './components/input';
import LinkToComponent from './components/link-to';
import TextArea from './components/text_area';
import TextField from './components/text_field';
import TextField from './components/text-field';
import TextArea from './components/textarea';
import {
clientBuilder,
DOMChanges,
Expand All @@ -21,6 +22,7 @@ import loc from './helpers/loc';
import { InertRenderer, InteractiveRenderer } from './renderer';
import TemplateCompiler from './template-compiler';
import ComponentTemplate from './templates/component';
import InputTemplate from './templates/input';
import OutletTemplate from './templates/outlet';
import RootTemplate from './templates/root';
import OutletView from './views/outlet';
Expand Down Expand Up @@ -102,6 +104,19 @@ export function setupEngineRegistry(registry: Registry) {
registry.register('component:link-to', LinkToComponent);

if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
// Internal

// These are registered as CapCase because our internal tempaltes do not
// go through the dashify transform. As a nice bonus, it also makes it
// more difficult for users to invoke them by accident.
registry.register('component:TextField', TextField);
registry.register('component:Checkbox', Checkbox);

// Public

registry.register('component:input', Input);
registry.register('template:components/input', InputTemplate);

registry.register('component:textarea', TextArea);
} else {
registry.register('component:-text-area', TextArea);
Expand Down
10 changes: 6 additions & 4 deletions packages/@ember/-internals/glimmer/lib/syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,17 @@ export function populateMacros(macros: Macros) {
let { inlines, blocks } = macros;
inlines.add('outlet', outletMacro);
inlines.add('mount', mountMacro);
inlines.add('input', inputMacro);
inlines.addMissing(refineInlineSyntax);
blocks.add('let', blockLetMacro);
blocks.addMissing(refineBlockSyntax);

if (!EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
inlines.add('input', inputMacro);
inlines.add('textarea', textAreaMacro);
}

inlines.addMissing(refineInlineSyntax);

blocks.add('let', blockLetMacro);
blocks.addMissing(refineBlockSyntax);

for (let i = 0; i < experimentalMacros.length; i++) {
let macro = experimentalMacros[i];
macro(blocks, inlines);
Expand Down
Loading

0 comments on commit 0617786

Please sign in to comment.