When people assumes that you built the project with Angular.
If you just landed on this tutorial make sure to read part 1 to part 3 over here first: https://nerdic-coder.com/2018/04/21/stencil-tutorial-tour-of-heroes/
If you have not done part 3 yet, you can pick up the code from here to follow along in this part 4 of the tutorial: https://github.com/nerdic-coder/stencil-tour-of-heroes/tree/v3.0
Routing
Now we will split up the heroes list and heroes details into two separate pages, we will also create a new dashboard view.
We will be using the stencil router to navigate between the pages.
Add routes
Our app already have a stencil router since before in the my-app.tsx
, but it’s only been loading one page up until now.
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
<main> | |
<stencil-router> | |
<stencil-route url='/' component='app-heroes' exact={true}> | |
</stencil-route> | |
</stencil-router> | |
</main> |
Add a navigation link (stencil-route-link)
Add a
element and, within that, an
element that, when clicked, triggers navigation to the
Heroes
component.
Add a dashboard view
Create a new folder dashboard
inside the components
folder and create the following files in the new folder,
- dashboard.css
- dashboard.spec.ts
- dashboard.tsx
Replace the default file content in these three files as follows and then return for a little discussion:
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
/* DashboardComponent's private CSS styles */ | |
[class*='col-'] { | |
float: left; | |
padding-right: 20px; | |
padding-bottom: 20px; | |
} | |
[class*='col-']:last-of-type { | |
padding-right: 0; | |
} | |
a { | |
text-decoration: none; | |
} | |
*, *:after, *:before { | |
-webkit-box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
box-sizing: border-box; | |
} | |
h3 { | |
text-align: center; margin-bottom: 0; | |
} | |
h4 { | |
position: relative; | |
} | |
.grid { | |
margin: 0; | |
} | |
.col-1-4 { | |
width: 25%; | |
} | |
.module { | |
padding: 20px; | |
text-align: center; | |
color: #eee; | |
max-height: 120px; | |
min-width: 120px; | |
background-color: #607D8B; | |
border-radius: 2px; | |
} | |
.module:hover { | |
background-color: #EEE; | |
cursor: pointer; | |
color: #607d8b; | |
} | |
.grid-pad { | |
padding: 10px 0; | |
} | |
.grid-pad > [class*='col-']:last-of-type { | |
padding-right: 20px; | |
} | |
@media (max-width: 600px) { | |
.module { | |
font-size: 10px; | |
max-height: 75px; } | |
} | |
@media (max-width: 1024px) { | |
.grid { | |
margin: 0; | |
} | |
.module { | |
min-width: 60px; | |
} | |
} |
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
import { render } from '@stencil/core/testing'; | |
import { Dashboard } from './dashboard'; | |
describe('app-dashboard', () => { | |
it('should build', () => { | |
expect(new Dashboard()).toBeTruthy(); | |
}); | |
describe('rendering', () => { | |
beforeEach(async () => { | |
await render({ | |
components: [Dashboard], | |
html: '<app-dashboard></app-dashboard>' | |
}); | |
}); | |
}); | |
}); |
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
import { Component, State } from '@stencil/core'; | |
import { Hero } from '../../models/hero'; | |
import { HeroService } from '../../services/hero.service'; | |
@Component({ | |
tag: 'app-dashboard', | |
styleUrl: 'dashboard.css' | |
}) | |
export class Dashboard { | |
private heroService: HeroService; | |
@State() private heroes: Hero[]; | |
constructor() { | |
this.heroService = HeroService.Instance; | |
} | |
componentWillLoad() { | |
this.getHeroes(); | |
} | |
getHeroes(): void { | |
this.heroService.getHeroes() | |
.subscribe(heroes => this.heroes = heroes.slice(1, 5)); | |
} | |
render() { | |
return ( | |
<div class='app-dashboard'> | |
<h2>Top Heroes</h2> | |
<div class="grid grid-pad"> | |
{this.heroes ? (this.heroes.map((hero) => | |
<a class="col-1-4"> | |
<div class="module hero"> | |
<h4>{hero.name}</h4> | |
</div> | |
</a> | |
)) : (null)} | |
</div> | |
</div> | |
); | |
} | |
} |
The template presents a grid of hero name links.
- The
map
repeater creates as many links as are in the component’sheroes
array. - The links are styled as colored blocks by the
dashboard.css
. - The links don’t go anywhere yet but they will shortly.
This getHeroes
reduces the number of heroes displayed to four (2nd, 3rd, 4th, and 5th).
Add the dashboard route
Add the following routes to the stencil-router
in my-apps.tsx
file:
This makes the dashboard the default page and also availabe on path /dashboard
.
Navigating to hero details
Now we will make hero details into it’s own page, start with deleting the hero details from the heroes.tsx
file.
Then add a hero details stencil route in my-apps.tsx
:
The “:id” part defines that a parameter with a hero id will be at that position in the url.
Then we’ll add details links to the heroes in the dashboard with the “hero.id” as the id parameter,
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
<stencil-route-link url={`/detail/${hero.id}`} class="col-1-4"> | |
<div class="module hero"> | |
<h4>{hero.name}</h4> | |
</div> | |
</stencil-route-link> |
We also add a similar link in the heroes list,
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
<li> | |
<stencil-route-link url={`/detail/${hero.id}`}> | |
<span class="badge">{hero.id}</span> {hero.name} | |
</stencil-route-link> | |
</li> |
Extract the id route parameter
Now in the hero-details.tsx
we need to catch the id from thr route parameter, we do this with the MatchResults from the stencil router package.
The end result will look something like this:
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
import { Component, Prop } from '@stencil/core'; | |
import { MatchResults } from '@stencil/router'; | |
import { Hero } from '../../models/hero'; | |
import { HeroService } from '../../services/hero.service'; | |
@Component({ | |
tag: 'app-hero-details', | |
styleUrl: 'hero-details.css' | |
}) | |
export class HeroDetails { | |
private heroService: HeroService; | |
@Prop() match: MatchResults; | |
@Prop({ mutable: true }) private hero: Hero; | |
constructor() { | |
this.heroService = HeroService.Instance; | |
} | |
componentWillLoad() { | |
this.getHero(); | |
} | |
getHero() { | |
this.heroService.getHero(parseInt(this.match.params.id)) | |
.subscribe(hero => { | |
this.hero = hero; | |
}); | |
} | |
handleChangeName(event) { | |
this.hero = { | |
id: this.hero.id, | |
name: event.target.value | |
}; | |
} | |
render() { | |
return ( | |
<div class='app-hero-details'> | |
{this.hero ? ( | |
<div> | |
<h2>{ this.hero.name.toUpperCase() } Details</h2> | |
<div><span>ids: </span>{this.hero.id}</div> | |
<div> | |
<label>name: | |
<input type="text" value={this.hero.name} onInput={(event) => this.handleChangeName(event)} placeholder="name" /> | |
</label> | |
</div> | |
</div> | |
) : ( | |
null | |
) | |
} | |
</div> | |
); | |
} | |
} |
First we create a variable from the MatchResults
then we will read the id value like this this.match.params.id
in the newly created method getHero()
.
Add HeroService.getHero()
Open HeroService
and add this getHero()
method:
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
getHero(id: number): Observable<Hero> { | |
// TODO: send the message _after_ fetching the hero | |
this.messageService.add(`HeroService: fetched hero id=${id}`); | |
return of(HEROES.find(hero => hero.id === id)); | |
} |
Find the way back
By clicking the browser’s back button, you can go back to the hero list or dashboard view, depending upon which sent you to the detail view.
It would be nice to have a button on the HeroDetail
view that can do that.
Add a go back button to the bottom of the component template and bind it to the component’s goBack()
method.
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
goBack(): void { | |
window.history.back(); | |
} |
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
<button onClick={() => this.goBack()}>go back</button> |
Refresh the browser and start clicking. Users can navigate around the app, from the dashboard to hero details and back, from heroes list to the mini detail to the hero details and back to the heroes again.
Conclusion
This concludes part 4 of this series and next up is dealing with HTTP requests in StencilJS, coming soon!
You can review the full example of this tutorial on this Github tag: https://github.com/nerdic-coder/stencil-tour-of-heroes/tree/v4.0
Now continue on to the final part of this Stencil series: Stencil — Tutorial: Tour of Heroes Part 5
Leave a Reply