Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch html templates dynamically on user action in angular 2

With Angularjs 1.x you could easily switch html templates on a button click between edit/readonly modus. The ng-include directive was the key.

<table>
    <thead>
        <th>Name</th>
        <th>Age</th>
        <th></th>
    </thead>
    <tbody>
        <tr ng-repeat="contact in model.contacts" ng-include="getTemplate(contact)">
        </tr>
    </tbody>
</table>

The get getTemplate function executes this code:

$scope.getTemplate = function (contact) {
    if (contact.id === $scope.model.selected.id) return 'edit';
    else return 'display';
};

which again lead to one of those templates to be active in the UI:

DISPLAY

  <script type="text/ng-template" id="display">
        <td>{{contact.name}}</td>
        <td>{{contact.age}}</td>
        <td>
            <button ng-click="editContact(contact)">Edit</button>
        </td>
    </script>

EDIT

<script type="text/ng-template" id="edit">
    <td><input type="text" ng-model="model.selected.name" /></td>
    <td><input type="text" ng-model="model.selected.age" /></td>
    <td>
        <button ng-click="saveContact($index)">Save</button>
        <button ng-click="reset()">Cancel</button>
    </td>
</script>

https://jsfiddle.net/benfosterdev/UWLFJ/

With Angular 2 RC 4 there exist no n-include.

How would I do the same feature just with Angular 2 RC4 ?

like image 313
Pascal Avatar asked Jul 24 '16 08:07

Pascal


2 Answers

I would leverage ngTemplateOutlet directive to do that.

Since version of 2.0.0-rc.2 (2016-06-15) context was added to NgTemplateOutlet

  • https://github.com/angular/angular/commit/164a091

so you can try to use this feature as described in my demo plunker (updated to 4.x.x)

template.html

<table>
    <thead>
        <th>Name</th>
        <th>Age</th>
        <th></th>
    </thead>
    <tbody>
        <tr *ngFor="let contact of contacts; let i = index">
            <ng-template [ngTemplateOutlet]="getTemplate(contact)" 
            [ngOutletContext]="{ $implicit: contact, index: i }"></ng-template>
        </tr>
    </tbody>
</table>


<ng-template #displayTmpl let-contact>
    <td>{{contact.name}}</td>
    <td>{{contact.age}}</td>
    <td>
        <button (click)="editContact(contact)">Edit</button>
    </td>
</ng-template>

 <ng-template #editTmpl let-i="index">
    <td><input type="text" [(ngModel)]="selected.name" /></td>
    <td><input type="text" [(ngModel)]="selected.age" /></td>
    <td>
        <button (click)="saveContact(i)">Save</button>
        <button (click)="reset()">Cancel</button>
    </td>
</ng-template>

component.ts

import { Component, ViewChild, TemplateRef } from '@angular/core';

interface Contact {
    id: number;
    name: string;
    age: number
}

@Component({
    ....
})
export class App {
    @ViewChild('displayTmpl') displayTmpl: TemplateRef<any>;
    @ViewChild('editTmpl') editTmpl: TemplateRef<any>;

    contacts: Array<Contact> = [{
            id: 1,
            name: "Ben",
            age: 28
        }, {
            id: 2,
            name: "Sally",
            age: 24
        }, {
            id: 3,
            name: "John",
            age: 32
        }, {
            id: 4,
            name: "Jane",
            age: 40
        }];

    selected: Contact;

    getTemplate(contact) {
        return this.selected && this.selected.id == contact.id ? 
        this.editTmpl : this.displayTmpl;
    }

    editContact(contact) {
        this.selected = Object.assign({}, contact);
    }

    saveContact (idx) {
        console.log("Saving contact");
        this.contacts[idx] = this.selected;
        this.reset();
    }

    reset() {
        this.selected = null;
    }
}
like image 198
yurzui Avatar answered Oct 05 '22 22:10

yurzui


You need to change the way you think about this things a little bit. It's not just a template, it's a branch of the application component tree. Think of it in terms of components and what purpose they serve in your application.

In your case, if you have "edit" and "display" views, then it would make sense to design your app with edit and display components and switch them with ngIf or ngSwitch. Each of those components would then need to be able to take data model as a property (Input) and render itself the way it needs.

So it could be something like this:

<tbody>
  <tr *ngFor="let contact of model.contacts">
    <contact-display *ngIf="getView(contact) === 'display'" [contact]="contact"></contact-display>
    <contact-edit *ngIf="getView(contact) === 'edit'" [contact]="contact"></contact-edit>
  </tr>
</tbody>

UDP. Here is a simple demo of the approach:

http://plnkr.co/edit/drzI1uL4kkKvsrm0rgOq?p=info

like image 44
dfsq Avatar answered Oct 05 '22 22:10

dfsq