-
-
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.
Merge pull request #1152 from MehulKChaudhari/tutorial/depedent-selec…
…t-dropdowns New tutorial chapter: dependent select dropdowns
- Loading branch information
Showing
4 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
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
101 changes: 101 additions & 0 deletions
101
apps/tutorial/public/docs/8-form-data-controlled/20-dependent-select-dropdowns/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,101 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
import { fn } from '@ember/helper'; | ||
import { cell } from 'ember-resources'; | ||
import { TrackedObject } from 'tracked-built-ins'; | ||
import { RemoteData } from 'ember-resources/util/remote-data'; | ||
|
||
const DATA_SOURCES = ['people', 'planets']; | ||
// A cell is @tracked data that can be used anywhere -- makes for smaller demos | ||
// This could be `@tracked selectedAPI = DATA_SOURCES[0];` in a component class | ||
const selectedAPI = cell(DATA_SOURCES[0]); | ||
const selectedStuff = new TrackedObject(); | ||
|
||
/////////// Select | ||
// Since we need a few of these for the demo, here is a shared Select component | ||
function handleSelectChange(handler, options, event) { | ||
let stringValue = event.target.value; | ||
return handler(stringValue); | ||
} | ||
function isSelected(a, b) { | ||
return a === b; | ||
} | ||
// for brevity, this only supports an array of strings for @options | ||
const Select = <template> | ||
<select {{on 'change' (fn handleSelectChange @onChange @options)}}> | ||
{{#each @options as |item|}} | ||
<option value={{item}} selected={{isSelected @value item}}> | ||
{{item}} | ||
</option> | ||
{{/each}} | ||
</select> | ||
</template>; | ||
/////////// End Select | ||
|
||
////////////// Demo | ||
|
||
function data() { | ||
return JSON.stringify({ selectedAPI: selectedAPI.current, selectedStuff }, null, 3); | ||
} | ||
|
||
function urlForDataSource(selectedData) { | ||
return `https://swapi.dev/api/${selectedData}/`; | ||
} | ||
|
||
function setSelected(propertyName, value) { | ||
selectedStuff[propertyName] = value; | ||
} | ||
|
||
function names(options) { | ||
return options.map(option => option.name); | ||
} | ||
|
||
<template> | ||
<pre>{{data}}</pre> | ||
|
||
<label> | ||
Parent Dropdown | ||
<Select | ||
@options={{DATA_SOURCES}} | ||
@onChange={{selectedAPI.set}} | ||
@selected={{selectedAPI.current}} | ||
/> | ||
</label> | ||
|
||
<label> | ||
Select {{selectedAPI.current}} | ||
|
||
{{! | ||
it's important to model async behavior, and this util makes that a bit easier. | ||
Docs here: https://ember-resources.pages.dev/funcs/util_remote_data.RemoteData }} | ||
{{#let (RemoteData (urlForDataSource selectedAPI.current)) as |request|}} | ||
{{#if request.isLoading}} | ||
Loading... | ||
{{/if}} | ||
|
||
{{#if request.value}} | ||
<Select | ||
@options={{names request.value.results}} | ||
@onChange={{fn setSelected selectedAPI.current}} | ||
> | ||
<:option as |item|> | ||
{{item.name}} | ||
</:option> | ||
</Select> | ||
{{/if}} | ||
{{/let}} | ||
</label> | ||
|
||
<style> | ||
label { | ||
border: 1px solid; | ||
padding: 1rem; | ||
display: block; | ||
margin: 0.5rem 0; | ||
} | ||
select { | ||
padding: 0.5rem 1rem; | ||
} | ||
</style> | ||
</template> |
69 changes: 69 additions & 0 deletions
69
apps/tutorial/public/docs/8-form-data-controlled/20-dependent-select-dropdowns/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,69 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
import { fn } from '@ember/helper'; | ||
import { cell } from 'ember-resources'; | ||
import { TrackedObject } from 'tracked-built-ins'; | ||
import { RemoteData } from 'ember-resources/util/remote-data'; | ||
|
||
const DATA_SOURCES = ['people', 'planets']; | ||
// A cell is @tracked data that can be used anywhere -- makes for smaller demos | ||
// This could be `@tracked selectedAPI = DATA_SOURCES[0];` in a component class | ||
const selectedAPI = cell(DATA_SOURCES[0]); | ||
const selectedStuff = new TrackedObject(); | ||
|
||
/////////// Select | ||
// Since we need a few of these for the demo, here is a shared Select component | ||
function handleSelectChange(handler, options, event) { | ||
let stringValue = event.target.value; | ||
return handler(stringValue); | ||
} | ||
function isSelected(a, b) { | ||
return a === b; | ||
} | ||
// for brevity, this only supports an array of strings for @options | ||
const Select = <template> | ||
<select {{on 'change' (fn handleSelectChange @onChange @options)}}> | ||
{{#each @options as |item|}} | ||
<option value={{item}} selected={{isSelected @value item}}> | ||
{{item}} | ||
</option> | ||
{{/each}} | ||
</select> | ||
</template>; | ||
/////////// End Select | ||
|
||
////////////// Demo | ||
|
||
function data() { | ||
return JSON.stringify({ selectedAPI: selectedAPI.current, selectedStuff }, null, 3); | ||
} | ||
|
||
function setSelected(propertyName, value) { | ||
selectedStuff[propertyName] = value; | ||
} | ||
|
||
<template> | ||
<pre>{{data}}</pre> | ||
|
||
<label> | ||
Parent Dropdown | ||
<Select | ||
@options={{DATA_SOURCES}} | ||
@onChange={{selectedAPI.set}} | ||
@selected={{selectedAPI.current}} | ||
/> | ||
</label> | ||
|
||
<style> | ||
label { | ||
border: 1px solid; | ||
padding: 1rem; | ||
display: block; | ||
margin: 0.5rem 0; | ||
} | ||
select { | ||
padding: 0.5rem 1rem; | ||
} | ||
</style> | ||
</template> |
52 changes: 52 additions & 0 deletions
52
...orial/public/docs/8-form-data-controlled/20-dependent-select-dropdowns/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,52 @@ | ||
You need to fetch data from the API corresponding to the selected option in the first dropdown. This requires creating a function that builds the correct API URL based on the selected value. | ||
|
||
In this example, the fetching of data from the [Options][swapi] | ||
should occur automatically based on changes in option selected`. | ||
|
||
```javascript | ||
function urlForDataSource(selectedData) { | ||
return `https://swapi.dev/api/${selectedData}/`; | ||
} | ||
``` | ||
|
||
Use the `RemoteData` utility to manage the asynchronous data fetching. This will allow you to handle loading states and response values effectively. | ||
|
||
```hbs | ||
{{#let (RemoteData (urlForDataSource selectedAPI.current)) as |request|}} | ||
{{/let}} | ||
``` | ||
|
||
After fetching the data, you need to update the second dropdown with the results. Use the `names` function to extract the names from the fetched data and pass them as options to the second dropdown. | ||
|
||
```javascript | ||
function names(options) { | ||
return options.map(option => option.name); | ||
} | ||
``` | ||
|
||
```hbs | ||
{{#let (RemoteData (urlForDataSource selectedAPI.current)) as |request|}} | ||
{{#if request.value}} | ||
<Select | ||
@options={{names request.value.results}} | ||
@onChange={{(fn setSelected selectedAPI.current)}} | ||
> | ||
<:option as |item|> | ||
{{item.name}} | ||
</:option> | ||
</Select> | ||
{{/if}} | ||
{{/let}} | ||
``` | ||
Use conditional rendering to display a loading message while the API call is in progress. This enhances user experience by providing feedback on the data fetching process. | ||
|
||
```hbs | ||
{{#if request.isLoading}} | ||
Loading... | ||
{{/if}} | ||
``` | ||
|
||
Docs for `RemoteData` can [be found here][docs-remote-data]. | ||
|
||
[docs-remote-data]: https://ember-resources.pages.dev/modules/util_remote_data | ||
[swapi]: https://swapi.dev/ |