12

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 ?

Rodrigo
  • 231
  • 3
  • 16
Pascal
  • 12,265
  • 25
  • 103
  • 195

2 Answers2

19

I would leverage ngTemplateOutlet directive to do that.

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

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;
    }
}
yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Awesome demo! And the code is very similar to angular 1.X!!! Can you tell me what declaration that is: @ViewChild('editTmpl') editTmpl: TemplateRef , I am doing typescript for some weeks now and I have never seen that syntax. – Pascal Jul 24 '16 at 16:17
  • It's just access to template inside component class https://angular.io/docs/ts/latest/api/core/index/TemplateRef-class.html – yurzui Jul 24 '16 at 16:21
  • ok thanks. I prefer your solution much over dfsq workaround for angular 2 before RC2 and your implementation for angular RC2+ :-) – Pascal Jul 24 '16 at 16:26
  • Can you please tell me why you do: this.selected = Object.assign({}, contact); Why not just do this.selected = contact ? Its because of the two-way binding and the cancel feature right? – Pascal Jul 26 '16 at 18:31
  • It's a copy of object. Yes its for the right cancel behavior. In this case item of array won't change – yurzui Jul 26 '16 at 18:38
  • Confirmed this still works in Angular 2.2.3. ;) - BTW: Can I use templateUrls thus seperating this into multiple HTML files? – A T Dec 01 '16 at 12:37
  • A little bit of necromancy here just to let you know that this approach also works with with Angular CLI 1.3.x and Angular 4.x.x. – Rodrigo Sep 22 '17 at 14:12
2

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

dfsq
  • 191,768
  • 25
  • 236
  • 258
  • So you create a component with selector contact-display/contact-edit and a directive [contact]? – Pascal Jul 24 '16 at 10:31
  • No, you don't need a directive. It's just a component with property contact (or whatever). I will update the answer. – dfsq Jul 24 '16 at 10:38
  • Check the demo I created of how I would do it. – dfsq Jul 24 '16 at 12:15
  • After 3 month I must agree with your statement:" it would make sense to design your app with edit and display components" else the wrapper component has 300 lines ;-) – Pascal Nov 06 '16 at 22:20
  • @dfsq just flagging that your demo doesn't seem to load anymore. – Zze Jan 18 '17 at 03:33