Skip to content

Commit 645a315

Browse files
committed
Switch to using a <form> element and FormData rather than <Input>
1 parent 52a367d commit 645a315

File tree

2 files changed

+55
-26
lines changed

2 files changed

+55
-26
lines changed

src/assets/downloads/style.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ p {
129129
font-style: italic;
130130
}
131131

132+
.rentals form p {
133+
font-size: 80%;
134+
display: block;
135+
text-align: center;
136+
}
137+
132138
.rentals input {
133139
padding: 11px;
134140
font-size: 18px;

src/markdown/tutorial/part-2/12-provider-components.md

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ In this chapter, we'll work on adding a new search feature, and refactor our `in
1212

1313
During this refactor, you will learn about:
1414

15-
* Using Ember's built-in `<Input>` component
15+
* Using forms
1616
* The provider component pattern
1717
* Using block parameters when invoking components
1818
* Yielding data to caller components
@@ -21,15 +21,17 @@ During this refactor, you will learn about:
2121

2222
As our app grows and as we add more features to it, one thing that would be really nice to have is some search functionality. It would be great if our users could just type a word into a search box and our app could just respond with matching and relevant rentals. So how could we go about implementing this feature?
2323

24-
Well, we can start simple. Before we worry about implementing the "search" part of this feature, let's just get something on the page. The first step is to add an `<input>` tag to our `index` page, and make it look pretty with a class.
24+
Well, we can start simple. Before we worry about implementing the "search" part of this feature, let's just get something on the page. The first step is to add a form with an `<input>` tag to our `index` page, and make it look pretty with a class.
2525

2626
```run:file:patch lang=handlebars cwd=super-rentals filename=app/templates/index.hbs
27-
@@ -7,2 +7,7 @@
27+
@@ -7,2 +7,9 @@
2828
<div class="rentals">
29-
+ <label>
30-
+ <span>Where would you like to stay?</span>
31-
+ <input class="light">
32-
+ </label>
29+
+ <form>
30+
+ <label>
31+
+ <span>Where would you like to stay?</span>
32+
+ <input class="light">
33+
+ </label>
34+
+ </form>
3335
+
3436
<ul class="results">
3537
```
@@ -60,10 +62,12 @@ Let's start simple again and begin our refactor by creating a new template for o
6062

6163
```run:file:create lang=handlebars cwd=super-rentals filename=app/components/rentals.hbs
6264
<div class="rentals">
63-
<label>
64-
<span>Where would you like to stay?</span>
65-
<input class="light">
66-
</label>
65+
<form>
66+
<label>
67+
<span>Where would you like to stay?</span>
68+
<input class="light">
69+
</label>
70+
</form>
6771
6872
<ul class="results">
6973
{{#each @rentals as |rental|}}
@@ -76,13 +80,15 @@ Let's start simple again and begin our refactor by creating a new template for o
7680
There is one minor change to note here: while extracting our markup into a component, we also renamed the `@model` argument to be `@rentals` instead, just in order to be a little more specific about what we're iterating over in our `{{#each}}` loop. Otherwise, all we're doing here is copy-pasting what was on our `index.hbs` page into our new component template. Now we just need to actually use our new component in the index template where we started this whole refactor! Let's render our `<Rentals>` component in our `index.hbs` template.
7781

7882
```run:file:patch lang=handlebars cwd=super-rentals filename=app/templates/index.hbs
79-
@@ -6,13 +6,2 @@
83+
@@ -6,15 +6,2 @@
8084
8185
-<div class="rentals">
82-
- <label>
83-
- <span>Where would you like to stay?</span>
84-
- <input class="light">
85-
- </label>
86+
- <form>
87+
- <label>
88+
- <span>Where would you like to stay?</span>
89+
- <input class="light">
90+
- </label>
91+
- </form>
8692
-
8793
- <ul class="results">
8894
- {{#each @model as |rental|}}
@@ -213,32 +219,49 @@ visit http://localhost:4200/tests?nocontainer&nolint&deterministic
213219
wait #qunit-banner.qunit-pass
214220
```
215221

216-
## Using Ember's `<Input>`
222+
## Using a `form`
217223

218-
Now that we have our component all set up, we can finally wire up our search box and store our search query! First things first: let's create a component class to store our query state.
224+
Now that we have our component all set up, we can finally wire up our search box and store our search query! First things first: let's create a component class to store our query state and handle events from the `form` element:
219225

220226
```run:file:create lang=js cwd=super-rentals filename=app/components/rentals.js
221227
import Component from '@glimmer/component';
222228
import { tracked } from '@glimmer/tracking';
229+
import { action } from '@ember/object';
223230

224231
export default class Rentals extends Component {
225232
@tracked query = '';
233+
234+
@action
235+
updateQuery(event) {
236+
let formData = new FormData(event.currentTarget);
237+
this.query = formData.get('rental-search-term');
238+
}
239+
240+
@action
241+
handleSubmit(event) {
242+
event.preventDefault();
243+
this.updateQuery(event);
244+
}
226245
}
227246
```
228247

229248
Next, we'll wire up our query state in the component template.
230249

231250
```run:file:patch lang=handlebars cwd=super-rentals filename=app/components/rentals.hbs
232-
@@ -3,3 +3,3 @@
233-
<span>Where would you like to stay?</span>
234-
- <input class="light">
235-
+ <Input @value={{this.query}} class="light" />
236-
</label>
251+
@@ -1,7 +1,8 @@
252+
<div class="rentals">
253+
- <form>
254+
+ <form {{on "input" this.updateQuery}} {{on "submit" this.handleSubmit}}>
255+
<label>
256+
<span>Where would you like to stay?</span>
257+
- <input class="light">
258+
+ <input name="rental-search-term" class="light">
259+
</label>
260+
+ <p>The results below will update as you type.</p>
261+
</form>
237262
```
238263

239-
Interesting! There are a few things happening in this one-line template change. First, we're moving from using a plain HTML `<input>` tag to using an `<Input>` tag instead! As it turns out, Ember provides us with a helpful little *[`<Input>` component](../../../components/built-in-components/#toc_input)* for this exact use case. The `<Input>` component is actually just a wrapper around the `<input>` element.
240-
241-
Ember's `<Input>` component is pretty neat; it will wire up things behind the scenes such that, whenever the user types something into the input box, `this.query` changes accordingly. In other words, `this.query` is kept in sync with the value of what is being searched; we finally have the perfect way of storing the state of our search query!
264+
[`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) is a built-in JavaScript object for handling forms. It requires the `name` attribute on the `input`. We handle both `submit` and `input` events for the form so that the query updates both when the user types into the input and when they submit the form.
242265

243266
> Zoey says...
244267
>

0 commit comments

Comments
 (0)