Styling and Refinements
Now we just have a few remaining loose ends to finish up — there are a few minor changes we are going to make just to provide a better experience in the application, and to make it look a little nicer.
Adding Titles and Comments
At the moment, we are only displaying the actual gif, but it would be nice to
see the title as well. We will update our GifListComponent
to include this, as
well as linking to the comment thread on Reddit for the gif.
Before we do that, we are going to create a little utility.
Create a file at
src/app/shared/utils/injection-tokens.ts
and add the following:
import { InjectionToken } from '@angular/core';
export const WINDOW = new InjectionToken<Window>('The window object', {
factory: () => window,
});
We have seen something like this before — we did a similar thing when accessing
the localStorage
API. The reasoning is the same again here — the window
object is a browser API and an Angular application does not necessarily always
run in the context of a browser. An injection token is a safer design because we
can provide alternate implementations based on the environment the application
is running in. We are not doing that here, and technically it isn’t necessary if
we know our application is only ever going to run in the browser context, but it
is a good principle to keep in mind.
I will leave you a bit of an assignment before we continue. We are going to
implement a few features all at once for our GifListComponent
. Take your best
shot at implementing the following and then I will post my solution — you will
likely need to look up the Angular Material documentation.
- Use an Angular Material toolbar to display the title of each gif
- Inject our
WINDOW
token and use it to launch the link for the particular gif in the browser when the user clicks somewhere in the toolbar. My solution uses an Angular Material icon as the link to click - Add a message that displays if there are no
gifs
Click here to reveal solution
Solution
Update the
GifListComponent
to reflect the following:
import { Component, input, inject } from '@angular/core';
import { WINDOW } from '../../shared/utils/injection-tokens';
import { Gif } from '../../shared/interfaces';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { GifPlayerComponent } from './gif-player.component';
@Component({
selector: 'app-gif-list',
template: `
@for (gif of gifs(); track gif.permalink){
<div>
<app-gif-player
[src]="gif.src"
[thumbnail]="gif.thumbnail"
data-testid="gif-list-item"
></app-gif-player>
<mat-toolbar color="primary">
<span>{{ gif.title }}</span>
<span class="toolbar-spacer"></span>
<button
mat-icon-button
(click)="window.open('https://reddit.com/' + gif.permalink)"
>
<mat-icon>comment</mat-icon>
</button>
</mat-toolbar>
</div>
} @empty {
<p>Can't find any gifs 🤷</p>
}
`,
imports: [
GifPlayerComponent,
MatToolbarModule,
MatIconModule,
MatButtonModule,
],
styles: [
`
div {
margin: 1rem;
filter: drop-shadow(0px 0px 6px #0e0c1ba8);
}
mat-toolbar {
white-space: break-spaces;
}
p {
font-size: 2em;
width: 100%;
text-align: center;
margin-top: 4rem;
}
`,
],
})
export class GifListComponent {
gifs = input.required<Gif[]>();
window = inject(WINDOW);
}