-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Start working on a section for controlled inputs * text input done * Add checkbox * Radio * Add select * Add select multiple * Textarea * Contenteditable
- Loading branch information
1 parent
e236151
commit 2f9d2c9
Showing
21 changed files
with
983 additions
and
0 deletions.
There are no files selected for viewing
41 changes: 41 additions & 0 deletions
41
apps/tutorial/public/docs/8-form-data-controlled/1-text-input/answer.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
|
||
class ControlledInput extends Component { | ||
handleInput = (event) => { | ||
let input = event.target; | ||
let value = input.value; | ||
|
||
this.args.onChange(value); | ||
} | ||
|
||
<template> | ||
<label> | ||
Example input | ||
<input type="text" value={{this.value}} {{on 'input' this.handleInput}} /> | ||
</label> | ||
</template> | ||
} | ||
|
||
class State { | ||
@tracked value; | ||
handleChange = (newValue) => this.value = newValue; | ||
} | ||
const createState = () => new State(); | ||
|
||
<template> | ||
{{#let (createState) as |x|}} | ||
<ControlledInput @value={{x.value}} @onChange={{x.handleChange}} /> | ||
|
||
<pre>{{x.value}}</pre> | ||
{{/let}} | ||
|
||
<style> | ||
input { | ||
border: 1px solid; | ||
padding: 0.25rem 0.5rem; | ||
} | ||
</style> | ||
</template> | ||
|
36 changes: 36 additions & 0 deletions
36
apps/tutorial/public/docs/8-form-data-controlled/1-text-input/prompt.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
|
||
class ControlledInput extends Component { | ||
<template> | ||
<label> | ||
Example input | ||
{{! Make this input a controlled input }} | ||
<input type="text" /> | ||
</label> | ||
</template> | ||
} | ||
|
||
// Some state is defined, and then created in the template | ||
class State { | ||
@tracked value; | ||
handleChange = (newValue) => this.value = newValue; | ||
} | ||
const createState = () => new State(); | ||
|
||
<template> | ||
{{#let (createState) as |x|}} | ||
<ControlledInput @value={{x.value}} @onChange={{x.handleChange}} /> | ||
|
||
<pre>{{x.value}}</pre> | ||
{{/let}} | ||
|
||
<style> | ||
input { | ||
border: 1px solid; | ||
padding: 0.25rem 0.5rem; | ||
} | ||
</style> | ||
</template> | ||
|
32 changes: 32 additions & 0 deletions
32
apps/tutorial/public/docs/8-form-data-controlled/1-text-input/prose.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
An _uncontrolled_ input is what we've used so far, where the inputs themselves manage their own internal state. We do not control that state, and so they are _uncontrolled_. | ||
|
||
A _controlled_ input is controlled by the invoking component. | ||
|
||
There are two parts to controlling a component: managing the value, and responding to an event on the input (which usually sets that managed value). | ||
|
||
|
||
Managing the value is the same as we've seen before with setting an input's initial value: | ||
```hbs | ||
<input value={{@value}} /> | ||
``` | ||
|
||
The second part, responding to an event on the input, usually unwraps the event and passes the current value to the calling component: | ||
```gjs | ||
class Demo extends Component { | ||
handleInput = (event) => { | ||
this.args.onChange(event.target.value); | ||
} | ||
<template> | ||
<input value={{@value}} {{on 'input' this.handleInput}} /> | ||
</template> | ||
} | ||
``` | ||
|
||
Because, with a text input, we expect a string to be passed to an `onChange` handler, we have nothing more to do. | ||
|
||
<p class="call-to-play"> | ||
Change the input within the <code>ControlledInput</code> component | ||
to <strong>be controlled</strong>. | ||
</p> | ||
|
46 changes: 46 additions & 0 deletions
46
apps/tutorial/public/docs/8-form-data-controlled/2-checkbox/answer.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
|
||
class ControlledInput extends Component { | ||
handleChange = (event) => { | ||
let input = event.target; | ||
let value = Boolean(input.checked); | ||
|
||
this.args.onChange(value); | ||
} | ||
|
||
<template> | ||
<label> | ||
Example input | ||
<input | ||
type="checkbox" | ||
checked={{this.value}} | ||
{{on 'change' this.handleChange}} /> | ||
</label> | ||
</template> | ||
} | ||
|
||
// Below is only setup for the tutorial chapter | ||
// and not exactly relevent to the topic | ||
class State { | ||
@tracked value; | ||
handleChange = (newValue) => this.value = newValue; | ||
} | ||
const createState = () => new State(); | ||
|
||
<template> | ||
{{#let (createState) as |x|}} | ||
<ControlledInput @value={{x.value}} @onChange={{x.handleChange}} /> | ||
|
||
<pre>{{x.value}}</pre> | ||
{{/let}} | ||
|
||
<style> | ||
input { | ||
border: 1px solid; | ||
padding: 0.25rem 0.5rem; | ||
} | ||
</style> | ||
</template> | ||
|
36 changes: 36 additions & 0 deletions
36
apps/tutorial/public/docs/8-form-data-controlled/2-checkbox/prompt.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
|
||
class ControlledInput extends Component { | ||
<template> | ||
<label> | ||
Example input | ||
<input type="checkbox" /> | ||
</label> | ||
</template> | ||
} | ||
|
||
// Below is only setup for the tutorial chapter | ||
// and not exactly relevent to the topic | ||
class State { | ||
@tracked value; | ||
handleChange = (newValue) => this.value = newValue; | ||
} | ||
const createState = () => new State(); | ||
|
||
<template> | ||
{{#let (createState) as |x|}} | ||
<ControlledInput @value={{x.value}} @onChange={{x.handleChange}} /> | ||
|
||
<pre>{{x.value}}</pre> | ||
{{/let}} | ||
|
||
<style> | ||
input { | ||
border: 1px solid; | ||
padding: 0.25rem 0.5rem; | ||
} | ||
</style> | ||
</template> | ||
|
41 changes: 41 additions & 0 deletions
41
apps/tutorial/public/docs/8-form-data-controlled/2-checkbox/prose.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
Just like the controlled input, the _controlled checkbox_ is roughly the same approach, but with a different property and event binding combination. | ||
|
||
Instead of setting value, we'll set `checked`. | ||
```hbs | ||
<input type="checkbox" checked={{@value}} /> | ||
``` | ||
|
||
And for the event binding, we'll use the `change` event. | ||
```gjs | ||
class Demo extends Component { | ||
handleChange = (event) => { | ||
let value = Boolean(event.target.checked); | ||
this.args.onChange(value); | ||
} | ||
<template> | ||
<input | ||
type="checkbox" | ||
value={{@value}} | ||
{{on 'change' this.handleChange}} /> | ||
</template> | ||
} | ||
``` | ||
|
||
Checkboxes have a default `value` of `on` (as a string), which may not be a desired value to pass to your `onChange` handler. In this example, we can convert the `checked` property to a boolean. | ||
|
||
<p class="call-to-play"> | ||
Change the checkbox within the <code>ControlledInput</code> component | ||
to <strong>be controlled</strong>. | ||
</p> | ||
|
||
|
||
### References | ||
|
||
- [Checkbox Inputs at MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox) | ||
|
||
API Docs: | ||
- [let](https://api.emberjs.com/ember/5.8/classes/Ember.Templates.helpers/methods/let?anchor=let) | ||
- [on](https://api.emberjs.com/ember/5.8/classes/Ember.Templates.helpers/methods/on?anchor=on) | ||
- [Component](https://api.emberjs.com/ember/5.8/modules/@glimmer%2Fcomponent) | ||
- [tracked](https://api.emberjs.com/ember/5.8/functions/@glimmer%2Ftracking/tracked) |
68 changes: 68 additions & 0 deletions
68
apps/tutorial/public/docs/8-form-data-controlled/3-radio/answer.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
|
||
class ControlledInput extends Component { | ||
handleChange = (event) => { | ||
let radio = event.target; | ||
|
||
this.args.onChange(radio.value); | ||
} | ||
|
||
isChecked = (value) => this.args.value === value | ||
|
||
<template> | ||
<fieldset> | ||
<legend>Favorite Race</legend> | ||
<label> | ||
Zerg | ||
<input | ||
type="radio" name="bestRace" value="zerg" | ||
checked={{this.isChecked "zerg"}} | ||
{{on 'change' this.handleChange}} | ||
/> | ||
</label> | ||
<label> | ||
Protoss | ||
<input | ||
type="radio" name="bestRace" value="protoss" | ||
checked={{this.isChecked "protoss"}} | ||
{{on 'change' this.handleChange}} | ||
/> | ||
</label> | ||
<label> | ||
Terran | ||
<input | ||
type="radio" name="bestRace" value="terran" | ||
checked={{this.isChecked "terran"}} | ||
{{on 'change' this.handleChange}} | ||
/> | ||
</label> | ||
</fieldset> | ||
</template> | ||
} | ||
|
||
// Below is only setup for the tutorial chapter | ||
// and not exactly relevent to the topic | ||
class State { | ||
@tracked value; | ||
handleChange = (newValue) => this.value = newValue; | ||
} | ||
const createState = () => new State(); | ||
|
||
<template> | ||
{{#let (createState) as |x|}} | ||
<ControlledInput @value={{x.value}} @onChange={{x.handleChange}} /> | ||
|
||
<pre>{{x.value}}</pre> | ||
{{/let}} | ||
|
||
|
||
<style> | ||
fieldset > label { | ||
display: block; | ||
} | ||
</style> | ||
|
||
</template> | ||
|
54 changes: 54 additions & 0 deletions
54
apps/tutorial/public/docs/8-form-data-controlled/3-radio/prompt.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
|
||
class ControlledInput extends Component { | ||
<template> | ||
<fieldset> | ||
<legend>Favorite Race</legend> | ||
<label> | ||
Zerg | ||
<input | ||
type="radio" name="bestRace" value="zerg" | ||
/> | ||
</label> | ||
<label> | ||
Protoss | ||
<input | ||
type="radio" name="bestRace" value="protoss" | ||
/> | ||
</label> | ||
<label> | ||
Terran | ||
<input | ||
type="radio" name="bestRace" value="terran" | ||
/> | ||
</label> | ||
</fieldset> | ||
</template> | ||
} | ||
|
||
// Below is only setup for the tutorial chapter | ||
// and not exactly relevent to the topic | ||
class State { | ||
@tracked value; | ||
handleChange = (newValue) => this.value = newValue; | ||
} | ||
const createState = () => new State(); | ||
|
||
<template> | ||
{{#let (createState) as |x|}} | ||
<ControlledInput @value={{x.value}} @onChange={{x.handleChange}} /> | ||
|
||
<pre>{{x.value}}</pre> | ||
{{/let}} | ||
|
||
|
||
<style> | ||
fieldset > label { | ||
display: block; | ||
} | ||
</style> | ||
|
||
</template> | ||
|
Oops, something went wrong.