Fork me on GitHub

Migrate the List Item

Note: If you skipped ahead to this section, click here to download a zip of the tutorial at this point.

Our last relevant Blaze Template is the list item - this is a little bit tricky because this template need to interact with the parent Component and get the actual Todo item, and also expose events for the parent Component - so we will use a new Angular 2 features called Input and Output for that.

So let's start with the Component migration this time:

13.1 Added the list item component client/imports/components/list-item.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import {Component, Input, Output, EventEmitter} from '@angular/core';
import {
  setCheckedStatus,
  updateText,
  remove,
} from '../../../imports/api/todos/methods';
import {displayError} from '../../../imports/ui/lib/errors';
 
declare let _;
 
@Component({
  selector: 'list-item',
  templateUrl: '/client/imports/components/list-item.html'
})
export class ListItemComponent {
  @Input("todo") todo: any;
  @Input("editing") editing: boolean;
  @Output("editChange") editChange = new EventEmitter();
 
  constructor() {
  }
 
  toggleEdit(isEdit) {
    if (!isEdit || (isEdit && this.editing)) {
      this.editChange.emit({
        editing: isEdit,
        todoId: this.todo._id
      });
    }
  }
 
  remove() {
    remove.call({
      todoId: this.todo._id,
    }, displayError);
  }
 
  checkedChange(event) {
    setCheckedStatus.call({
      todoId: this.todo._id,
      newCheckedStatus: event.target.checked,
    });
  }
 
  updateText(event) {
    _.throttle((event) => {
      updateText.call({
        todoId: this.todo._id,
        newText: event.target.value,
      }, displayError);
    }, 300)(event);
  }
}

We copied the code from the old Blaze Template, and added two Inputs and one Output in the Component declaration:

  • todo which is the actual todo item.
  • editing which is an indication for the current item that being edited.
  • editChange which is an event we expose to the parent Component that triggered when starting to edit an item in the list.

And add the new Component to the NgModule:

13.2 Added the new component to the module client/imports/app.module.ts
8
9
10
11
12
13
14
 
17
18
19
20
21
22
23
24
import {JoinComponent} from "./components/join.component";
import {SigninComponent} from "./components/signin.component";
import {FormsModule} from "@angular/forms";
import {ListItemComponent} from "./components/list-item.component";
 
@NgModule({
    // Components, Pipes, Directive
...some lines skipped...
        ListShowComponent,
        ListRedirectorComponent,
        JoinComponent,
        SigninComponent,
        ListItemComponent
    ],
    // Entry Components
    entryComponents: [

Now let's migrate the HTML Template of this Component:

13.3 Added the list item view client/imports/components/list-item.ng2.html
1
2
3
4
5
6
7
8
9
10
11
<div class="list-item" [ngClass]="{'editing': editing, 'checked': todo.checked}">
    <label class="checkbox">
        <input type="checkbox" [checked]="todo.checked" (change)="checkedChange($event)" name="checked">
        <span class="checkbox-custom"></span>
    </label>
 
    <input type="text" [value]="todo.text" (blur)="toggleEdit(false)" (focus)="toggleEdit(true)" (keyup)="updateText($event)" placeholder="Task name">
    <a class="js-delete-item delete-item" (click)="remove()">
        <span class="icon-trash"></span>
    </a>
</div>

And now we need to use this new Component in the ListShowComponent:

13.4 Updated the usage in the list show component client/imports/components/list-show.ng2.html
51
52
53
54
55
56
57
    <div class="content-scrollable list-items">
        <div *ngIf="todosReady">
            <div *ngFor="let todo of todos | async">
                <list-item [todo]="todo" [editing]="editingTodo" (editChange)="onTodoItemEditChange($event)"></list-item>
            </div>
            <div class="wrapper-message" *ngIf="!todos || todos.length == 0">
                <div class="title-message">No tasks here</div>

And let's implement the actual event handler and use declare the usage of the new Component:

13.5 Added events and usage of the new component client/imports/components/list-show.component.ts
22
23
24
25
26
27
28
 
101
102
103
104
105
106
107
108
109
110
111
112
    private editing : boolean = false;
    private editModel : any;
    private newItemModel : string = '';
    private editingTodo : number | boolean;
 
    constructor(private currentRoute: ActivatedRoute, private router: Router) {
        this.editModel = {
...some lines skipped...
        }
    }
 
    onTodoItemEditChange(event) {
        if (event.editing) {
            this.editingTodo = event.todoId;
        }
        else {
            this.editingTodo = false;
        }
    }
}

And we are done! You can now remove all the files that related to the list item and removed it's import! (we did it in commit #9.6)