This repository was archived by the owner on Dec 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Tasks
Gregor Woiwode edited this page Nov 18, 2019
·
40 revisions
- Open your terminal
- Execute
npm start -- --openstarting your Angular application - Visit http://localhost:4200
- Have a look at the beautiful start page the Angular team has prepared for you ❤️ .
- Open your favourite editor and open
src/app/app.component.htmlof your application - Find the
spanelement having the content Welcome - Change the content from Welcome to whatever you like (e.g. Hello ng-de 2019! 😉)
🕵️♂️ Plesae note that the application automatically recompiles and refreshes automatically.
- Add a second property to
app.component.ts - You can name your component what ever you like (e.g. name, age, street, location, hobby, ...)
- Bind your property in the template
app.component.htmlusing double curly braces
- Open your terminal
- Execute
ng generate component todo-checker - ☝️ Recognize that the
app.module.tswas updated too and that theTodoCheckerComponenthas been registered. - Open
src/app/todo/todo-checker.component.htmland paste the following template<!-- todo-checker.component.html --> <div class="todo"> <label class="todo__label"> {{ "TODO: Bind the value of the todo's text" }} <input type="checkbox" /> <span class="todo__checkmark"></span> </label> </div>
- Open
app.component.html - Replace the existing template with your component
<!-- app.component.html --> <ws-todo-checker></ws-todo-checker>
- Finally, have a look at your browser. Your component should be displayed.
- Open
todo-checker.component.ts - Add a property
todo, which is an object defining a stringtextand a boolean flagisDone. - Bind the
todoto the HTML template. - Create an event binding for the checkbox listening for the
change-Event. - Every time the change event is fired the method
emitToggleshould be executed. - Implement the method
emitTogglethat shows analert-message for the moment.
<!-- Binding with curly braces -->
{{ todo.text }}
<!-- Property binding -->
<input [checked]="...">
<!-- Event binding -->
<input (change)="...">- Open
todo-checker.component.ts - Make the property
todoto be an@Input - Define an
@Outputcalledtoggleand initialize it with anEventEmitter - Make
toggleemit whenever thechange-Event of the checkbox is fired - Open
app.component.ts - Make the
AppCompoenentpass a todo-Object (withtextandisDone) to the<ws-todo-checker>using the property-binding - Handle the
toggle-Event ofws-todo-checkerby showing analert-Window displaying the respectivetodowhenever the event is fired
// Imports
import { Input, Output } from '@angular/core';
// Input-Binding
@Input todo;
// Output-Binding
@Output toggle = new EventEmitter();
this.toggle.emit(/* ... */);<!-- Property binding -->
<ws-todo-checker [todo]="...">
<!-- Event binding -->
<ws-todo-checker (toggle)="notify($event)" >- open
app.component.ts. - replace the single
todowith a collection of at least 2todos. - bind
todosto the template using*ngFor. - adjust the event binding
togglecalling the methodcheckOrUncheckTodo(see hints) updating the respective todo.
<ws-todo-checker
[todo]="todo"
(toggle)="checkOrUncheckTodo"
*ngFor="let todo of todos"
><ws-todo-checker>todos = [/* ... */]
// ...
checkOrUncheckTodo(todoForUpdate) {
this.todos = this.todos.map(todo => todo.text === todoForUpdate.text
? { ...todo, isDone: !todo.isDone }
: todo
);
}- Generate an interface executing
ng generate interfacemodels/todo(It creates a directory model and places a file namedtodo.ts` in it)- define 2 properties:
text: string; isDone: boolean;
- define 2 properties:
- Look up every place you are using
todo(you can use the project wide search hitting CTRL | CMD+SHIFT+F) - Annotate each
todo-property with your created typeTodo
@Input() todo: Todo;
@Output() toggle = new EventEmitter<Todo>();- Install the extension Angular Language Service to have Auto-Completion in your templates.
- Open your terminal
- Generate a new component by running
ng g c todo-quick-add - Open
src/app/todo/todo-quick-add.component.htmland paste the following template<!-- todo-quick-add.component.html --> <input <!-- DEFINE THE TEMPLATE REFERENCE --> type="text" class="todo__input" placeholder="What needs to be done?" (keydown.enter)="emitCreate(<!-- PASS THE TEMPLATE REFERENCE TO YOUR COMPONENT -->)" /> <button (click)="emitCreate(<!-- PASS THE TEMPLATE REFERENCE TO YOUR COMPONENT -->)" class="todo__button--primary"> Add </button>
- Implement the method
emitCreate(input: HTMLInputElement) - Make
emitCreateemit the eventcreate(You need to define an @Output for that) - The event
createshould transport thetextcoming from the input-Field. - Furthermore the property
isDoneshould initially be set tofalse. - Open
app.component.html - Handle the
create-Event and add the providedTodoto the existing collecitontodos - Every time you enter a new todo the list of
<ws-todo-checker>should automatically grow.
- Generate a new
TodosModule - Generate a
TodosComponentthat is the root component of Todos feature - move all existing components used by the todos feature into the newly created
/todosfolder - move component declarations of those components from
AppModuleintoTodosModule - move todo markup from
app.component.htmlintotodos.component.html - move todos array and methods from
app.component.tsintotodos.component.ts - export
TodosComponentfromTodosModule - import
TodosModuleinAppModule - use the new
TodosComponentinapp.component.html
ng generate module todos
ng generate component todos
@NgModule({
declarations: [
TodosComponent,
// ...
],
exports: [TodosComponent]
})
export class TodosModule {}<!-- app.component.html -->
<ws-todos></ws-todos>- Generate a
TodosServicein a/sharedsub-folder of/todos(this is an Angular convention to show that this service will be used by multiple components). This service will handle all todo data operations. - Move the todo array from
TodosComponentinto your new service. - Create a method
getAllTodosthat returns the array and use it inTodosComponent.
Bonus
- Create method
create(that adds a todo) and use it inTodosComponent. - Create method
update(that replaces a todo) and use it inTodosComponent.
ng generate service todos/shared/todos
- open
todos.module.ts - register
HttpClientModule
import { HttpClientModule } from '@angular/common/http';
// ...
@NgModule({
imports: [
CommonModule,
HttpClientModule
],
//...
})
export class TodosModule {}- Open a 2nd terminal
- Switch to the directory
serverund runnpm start - Open
todos.service.ts - Implement the method
querythat loads all todos from the API (URL: http://localhost:3000/todos) - Bind the loaded todos via subscribe
- Checkout http://localhost:4200 if the todos from the API are displayed
- You can find a detailed documentation of the API in the server's README
- Make sure to subscribe in the
todos.component.ts - Make sure to call the service in
ngOnInit
// todos.service.ts
constructor(private http: HttpClient) {}
query(): Observable<Todo[]> {
return this.http.get<Todo[]>('http://localhost:3000/todos');
}
// todos.component.ts
ngOnInit(): void {
this.todosService.query().subscribe(todosFromApi => this.todos = todosFromApi);
}- Enhance
todos.service.tsto store a todo in the database
💡 Please note, after storing a todo you need to manually refresh the page to see the created todo in the list. We will take care about this UX-fail in the next step.
return this.http.post<Todo>('http://localhost:3000/todos', newTodo);- When a todo is checked it should immediately persisted (using Put-Request)
- When a todo is deleted it should immediately removed (using DELETE-Request)
- If you want to add a delete button to
<todo-checker>you can use the following template<div class="todo"> <label class="todo__label" [ngClass]="{ 'todo--is-done': todo.isDone }"> {{ todo.text }} <input type="checkbox" [checked]="todo.isDone" (change)="emitToggle()" /> <span class="todo__checkmark"></span> </label> <!-- ⭐️ NEW NEW NEW ⭐️ --> <span class="todo__delete" (click)="emitRemove()"></span> </div>
- If you want to add a delete button to
- Open
todos.component.ts. - Use the operator
switchMaploading all todos after a todo has been created successfully. - Check http://localhost:4200 to see that the list of todos updates automatically.
import { switchMap } from 'rxjs/operators';
createTodo(newTodo: Todo) {
this.todosService
.create(newTodo)
.pipe(switchMap(() => this.todosService.query()))
.subscribe(todos => (this.todos = todos))
}- Open
todos.component.ts. - Add a new property
sink - Initialize
sinkwithnew Subscription - Go through the component code and add each
subscribe-Block to yoursink - Implement
OnDestroy - Call
this.sink.unsubscribe()inngOnDestroycleaning up all Subscriptions.
import { Subscription } from 'rxjs';
sink = new Subscription();
sink.add(/* ... */);
sink.unsubscribe();- Create an
AppRoutingModulethat- imports
RouterModule - configure
RouterModule(usingRouterModule.forRoot()) to- map route
'todos'toTodosComponent - redirect to
'todos'when url path is empty
- map route
- Re-exports
RouterModule
- imports
- Import
AppRoutingModulein AppModule - use
router-outletto show todos inapp.component.html
💡 If your todos are shown 2 times, try removing the direct view child (
ws-todos).
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'todos' },
{ path: 'todos', component: TodosComponent }
];- Create a
TodosRoutingModulelike you did withAppRoutingModuleand import it inTodosModule - Configure route
{ path: 'todos/:query', component: TodosComponent }inTodosRoutingModule - Change AppRoutingModule routes to
[{ path: '', pathMatch: 'full', redirectTo: 'todos/all' }] - Create a todos navigation component called
todos-link-navigationas part ofTodosModule(see hints for html content):ng generate component todos/todos-link-navigation - Add the
<todos-link-navigation></todos-link-navigation>totodos.component.html - Extend the
query()method oftodos.service.tsto accept an optional string parameter for filtering the results:
query(param?: string): Observable<Todo[]> {
return this.http.get<Todo[]>(`${todosUrl}?query=${param ? param : 'all'}`);
}- Use
ActivatedRoute#paramMapto read out the dynamicqueryurl-parameter:
// todos.component.ts ngOnInit():
this.route.paramMap
.pipe(
switchMap(paramMap => this.todosService.query(paramMap.get('query')))
)
.subscribe(todos => (this.todos = todos)<!-- todos-link-navigation.component.html -->
<ul class="todo__link-navigation">
<li class="todo__link-navigation__link">
<a routerLink="../all" routerLinkActive="todo__link--active">All</a>
</li>
<li class="todo__link-navigation__link">
<a routerLink="../active" routerLinkActive="todo__link--active">Active</a>
</li>
<li class="todo__link-navigation__link">
<a routerLink="../complete" routerLinkActive="todo__link--active"
>Complete</a
>
</li>
</ul>Watch your trainers implement lazy loading :-)
- Meet Angular
- Enhance AppComponent
- Create your 1st component
- Simple bindings
- Dataflow
- Multiply your components
- Secure your coding with types
- Create your 1st form
- Extract a feature module
- Create your 1st service
- Set up Http
- Load todos from API
- Hints
- Persist new todos
- Auto refresh todos
- Clean up subscriptions
- Set up Routing
- Create Todo Navigation
- Lazy Loading
- Provide Wildcard Route