Skip to content

Commit 0617786

Browse files
committed
[FEATURE ember-glimmer-angle-bracket-built-ins]
Implement `<Input />`.
1 parent 05472ed commit 0617786

File tree

18 files changed

+1764
-263
lines changed

18 files changed

+1764
-263
lines changed

packages/@ember/-internals/glimmer/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,8 @@
265265
export { default as RootTemplate } from './lib/templates/root';
266266
export { default as template } from './lib/template';
267267
export { default as Checkbox } from './lib/components/checkbox';
268-
export { default as TextField } from './lib/components/text_field';
269-
export { default as TextArea } from './lib/components/text_area';
268+
export { default as TextField } from './lib/components/text-field';
269+
export { default as TextArea } from './lib/components/textarea';
270270
export { default as LinkComponent } from './lib/components/link-to';
271271
export { default as Component, ROOT_REF } from './lib/component';
272272
export { default as Helper, helper } from './lib/helper';
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/**
2+
@module @ember/component
3+
*/
4+
import { computed } from '@ember/-internals/metal';
5+
import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
6+
import { assert } from '@ember/debug';
7+
import { DEBUG } from '@glimmer/env';
8+
import Component from '../component';
9+
10+
let Input: any;
11+
12+
if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
13+
/**
14+
The `{{input}}` helper lets you create an HTML `<input />` component.
15+
It causes a `TextField` component to be rendered. For more info,
16+
see the [TextField](/api/ember/release/classes/TextField) docs and
17+
the [templates guide](https://guides.emberjs.com/release/templates/input-helpers/).
18+
19+
```handlebars
20+
{{input value="987"}}
21+
```
22+
23+
renders as:
24+
25+
```HTML
26+
<input type="text" value="987" />
27+
```
28+
29+
### Text field
30+
31+
If no `type` option is specified, a default of type 'text' is used.
32+
Many of the standard HTML attributes may be passed to this helper.
33+
<table>
34+
<tr><td>`readonly`</td><td>`required`</td><td>`autofocus`</td></tr>
35+
<tr><td>`value`</td><td>`placeholder`</td><td>`disabled`</td></tr>
36+
<tr><td>`size`</td><td>`tabindex`</td><td>`maxlength`</td></tr>
37+
<tr><td>`name`</td><td>`min`</td><td>`max`</td></tr>
38+
<tr><td>`pattern`</td><td>`accept`</td><td>`autocomplete`</td></tr>
39+
<tr><td>`autosave`</td><td>`formaction`</td><td>`formenctype`</td></tr>
40+
<tr><td>`formmethod`</td><td>`formnovalidate`</td><td>`formtarget`</td></tr>
41+
<tr><td>`height`</td><td>`inputmode`</td><td>`multiple`</td></tr>
42+
<tr><td>`step`</td><td>`width`</td><td>`form`</td></tr>
43+
<tr><td>`selectionDirection`</td><td>`spellcheck`</td><td>&nbsp;</td></tr>
44+
</table>
45+
When set to a quoted string, these values will be directly applied to the HTML
46+
element. When left unquoted, these values will be bound to a property on the
47+
template's current rendering context (most typically a controller instance).
48+
A very common use of this helper is to bind the `value` of an input to an Object's attribute:
49+
50+
```handlebars
51+
Search:
52+
{{input value=searchWord}}
53+
```
54+
55+
In this example, the initial value in the `<input />` will be set to the value of `searchWord`.
56+
If the user changes the text, the value of `searchWord` will also be updated.
57+
58+
### Actions
59+
60+
The helper can send multiple actions based on user events.
61+
The action property defines the action which is sent when
62+
the user presses the return key.
63+
64+
```handlebars
65+
{{input action="submit"}}
66+
```
67+
68+
The helper allows some user events to send actions.
69+
70+
* `enter`
71+
* `insert-newline`
72+
* `escape-press`
73+
* `focus-in`
74+
* `focus-out`
75+
* `key-press`
76+
* `key-up`
77+
78+
For example, if you desire an action to be sent when the input is blurred,
79+
you only need to setup the action name to the event name property.
80+
81+
```handlebars
82+
{{input focus-out="alertMessage"}}
83+
```
84+
See more about [Text Support Actions](/api/ember/release/classes/TextField)
85+
86+
### Extending `TextField`
87+
88+
Internally, `{{input type="text"}}` creates an instance of `TextField`, passing
89+
arguments from the helper to `TextField`'s `create` method. You can extend the
90+
capabilities of text inputs in your applications by reopening this class. For example,
91+
if you are building a Bootstrap project where `data-*` attributes are used, you
92+
can add one to the `TextField`'s `attributeBindings` property:
93+
94+
```javascript
95+
import TextField from '@ember/component/text-field';
96+
TextField.reopen({
97+
attributeBindings: ['data-error']
98+
});
99+
```
100+
101+
Keep in mind when writing `TextField` subclasses that `TextField`
102+
itself extends `Component`. Expect isolated component semantics, not
103+
legacy 1.x view semantics (like `controller` being present).
104+
See more about [Ember components](/api/ember/release/classes/Component)
105+
106+
### Checkbox
107+
108+
Checkboxes are special forms of the `{{input}}` helper. To create a `<checkbox />`:
109+
110+
```handlebars
111+
Emberize Everything:
112+
{{input type="checkbox" name="isEmberized" checked=isEmberized}}
113+
```
114+
115+
This will bind checked state of this checkbox to the value of `isEmberized` -- if either one changes,
116+
it will be reflected in the other.
117+
118+
The following HTML attributes can be set via the helper:
119+
120+
* `checked`
121+
* `disabled`
122+
* `tabindex`
123+
* `indeterminate`
124+
* `name`
125+
* `autofocus`
126+
* `form`
127+
128+
### Extending `Checkbox`
129+
130+
Internally, `{{input type="checkbox"}}` creates an instance of `Checkbox`, passing
131+
arguments from the helper to `Checkbox`'s `create` method. You can extend the
132+
capablilties of checkbox inputs in your applications by reopening this class. For example,
133+
if you wanted to add a css class to all checkboxes in your application:
134+
135+
```javascript
136+
import Checkbox from '@ember/component/checkbox';
137+
138+
Checkbox.reopen({
139+
classNames: ['my-app-checkbox']
140+
});
141+
```
142+
143+
@method input
144+
@for Ember.Templates.helpers
145+
@param {Hash} options
146+
@public
147+
*/
148+
Input = Component.extend({
149+
tagName: '',
150+
151+
isCheckbox: computed('type', function(this: { type?: unknown }) {
152+
return this.type === 'checkbox';
153+
}),
154+
});
155+
156+
Input.toString = () => '@ember/component/input';
157+
158+
if (DEBUG) {
159+
const UNSET = {};
160+
161+
Input.reopen({
162+
value: UNSET,
163+
164+
didReceiveAttrs() {
165+
this._super();
166+
167+
assert(
168+
"`<Input @type='checkbox' @value={{...}} />` is not supported; " +
169+
"please use `<Input @type='checkbox' @checked={{...}} />` instead.",
170+
!(this.type === 'checkbox' && this.value !== UNSET)
171+
);
172+
},
173+
});
174+
}
175+
}
176+
177+
export default Input;

packages/@ember/-internals/glimmer/lib/resolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ export default class RuntimeResolver implements IRuntimeResolver<OwnedTemplateMe
319319

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

325325
let name = _name;

packages/@ember/-internals/glimmer/lib/setup-registry.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS } from '@ember/canary-features';
55
import { Simple } from '@glimmer/interfaces';
66
import Component from './component';
77
import Checkbox from './components/checkbox';
8+
import Input from './components/input';
89
import LinkToComponent from './components/link-to';
9-
import TextArea from './components/text_area';
10-
import TextField from './components/text_field';
10+
import TextField from './components/text-field';
11+
import TextArea from './components/textarea';
1112
import {
1213
clientBuilder,
1314
DOMChanges,
@@ -21,6 +22,7 @@ import loc from './helpers/loc';
2122
import { InertRenderer, InteractiveRenderer } from './renderer';
2223
import TemplateCompiler from './template-compiler';
2324
import ComponentTemplate from './templates/component';
25+
import InputTemplate from './templates/input';
2426
import OutletTemplate from './templates/outlet';
2527
import RootTemplate from './templates/root';
2628
import OutletView from './views/outlet';
@@ -102,6 +104,19 @@ export function setupEngineRegistry(registry: Registry) {
102104
registry.register('component:link-to', LinkToComponent);
103105

104106
if (EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
107+
// Internal
108+
109+
// These are registered as CapCase because our internal tempaltes do not
110+
// go through the dashify transform. As a nice bonus, it also makes it
111+
// more difficult for users to invoke them by accident.
112+
registry.register('component:TextField', TextField);
113+
registry.register('component:Checkbox', Checkbox);
114+
115+
// Public
116+
117+
registry.register('component:input', Input);
118+
registry.register('template:components/input', InputTemplate);
119+
105120
registry.register('component:textarea', TextArea);
106121
} else {
107122
registry.register('component:-text-area', TextArea);

packages/@ember/-internals/glimmer/lib/syntax.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,17 @@ export function populateMacros(macros: Macros) {
9898
let { inlines, blocks } = macros;
9999
inlines.add('outlet', outletMacro);
100100
inlines.add('mount', mountMacro);
101-
inlines.add('input', inputMacro);
102-
inlines.addMissing(refineInlineSyntax);
103-
blocks.add('let', blockLetMacro);
104-
blocks.addMissing(refineBlockSyntax);
105101

106102
if (!EMBER_GLIMMER_ANGLE_BRACKET_BUILT_INS) {
103+
inlines.add('input', inputMacro);
107104
inlines.add('textarea', textAreaMacro);
108105
}
109106

107+
inlines.addMissing(refineInlineSyntax);
108+
109+
blocks.add('let', blockLetMacro);
110+
blocks.addMissing(refineBlockSyntax);
111+
110112
for (let i = 0; i < experimentalMacros.length; i++) {
111113
let macro = experimentalMacros[i];
112114
macro(blocks, inlines);

0 commit comments

Comments
 (0)