web-dev-qa-db-fra.com

Tuyau dynamique dans Angular 2

J'essaie de créer un composant où vous pouvez passer le tuyau à utiliser pour une liste à l'intérieur du composant. D'après ce que j'ai pu trouver en testant et en recherchant des réponses, la seule solution semble créer quelque chose comme:

<my-component myFilter="sortByProperty"></my-component>

my-component modèle:

<li *ngFor="#item of list | getPipe:myFilter"></li>

Qui mappe alors myFilter à la logique de pipe correcte et l'exécute, mais cela semble un peu sale et pas optimal.

Je pensais qu'ils auraient trouvé une meilleure solution à ce problème depuis Angular 1 où vous feriez également quelque chose dans ce sens.

N'y a-t-il pas une meilleure façon de le faire dans Angular 2?

16
Chrillewoodz

En s'appuyant sur la réponse de borislemke, voici une solution qui n'a pas besoin de eval() et que je trouve plutôt propre:

dynamic.pipe.ts:

import {
    Injector,
    Pipe,
    PipeTransform
} from '@angular/core';


@Pipe({
  name: 'dynamicPipe'
})
export class DynamicPipe implements PipeTransform {

    public constructor(private injector: Injector) {
    }

    transform(value: any, pipeToken: any, pipeArgs: any[]): any {
        if (!pipeToken) {
            return value;
        }
        else {
            let pipe = this.injector.get(pipeToken);
            return pipe.transform(value, ...pipeArgs);
        }
    }
}

app.module.ts:

// …
import { DynamicPipe } from './dynamic.pipe';

@NgModule({
  declarations: [
    // …
    DynamicPipe,
  ],
  imports: [
    // …
  ],
  providers: [
    // list all pipes you would like to use
    PercentPipe,
    ],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts:

import { Component, OnInit } from '@angular/core';
import { PercentPipe } from '@angular/common';

@Component({
  selector: 'app-root',
  template: `
    The following should be a percentage: 
    {{ myPercentage | dynamicPipe: myPipe:myPipeArgs }}
    `,
  providers: []
})

export class AppComponent implements OnInit {
  myPercentage = 0.5;
  myPipe = PercentPipe;
  myPipeArgs = [];
}
31
balu

La façon la plus simple de résoudre ce problème serait de ne pas utiliser les tuyaux dans les modèles HTML, mais d'injecter le tuyau dans le constructeur d'un composant (à l'aide de DI), puis d'appliquer la transformation de manière fonctionnelle. Cela fonctionne assez bien avec une carte observable ou des flux rxjs similaires.

3
Patrick Michalina

J'ai réussi à faire fonctionner quelque chose, c'est un peu sale et mauvais (avec eval) mais ça fait l'affaire pour moi. Dans mon cas, j'ai un composant de table avec différents types de données dans chaque ligne (par exemple, titre, URL, date, statut). Dans ma base de données, l'état est marqué comme 1 comme enabled ou 0 pour disabled. Bien sûr, il est plus préférable de montrer activé/désactivé à mon utilisateur. De plus, ma colonne de titre est multilingue, ce qui en fait un objet avec en ou id comme clé.

// Example row object:
title: {
    "en": "Some title in English",
    "id": "Some title in Indonesian"
},
status: 1 // either 1 or 0

Idéalement, j'ai besoin de 2 canaux différents pour convertir mes données à montrer à l'utilisateur de mon application. Quelque chose comme translateTitle et getStatus fera l'affaire. Appelons le tube du parent dynamicPipe.

/// some-view.html
{{ title | dynamicPipe:'translateTitle' }}
{{ status | dynamicPipe:'getStatus' }}


/// dynamic.pipe.ts
//...import Pipe and PipeTransform

@Pipe({name:'dynamicPipe'})
export class DynamicPipe implements PipeTransform {

    transform(value:string, modifier:string) {
        if (!modifier) return value;
        return eval('this.' + modifier + '(' + value + ')')
    }

    getStatus(value:string|number):string {
        return value ? 'enabled' : 'disabled'
    }

    translateTitle(value:TitleObject):string {
        // defaultSystemLanguage is set to English by default
        return value[defaultSystemLanguage]
    }
}

Je vais probablement avoir beaucoup de haine à utiliser eval. J'espère que ça aide!

Mise à jour: quand vous en aurez besoin

posts = {
    content: [
        {
            title:
                {
                    en: "Some post title in English",
                    es: "Some post title in Spanish"
                },
            url: "a-beautiful-post",
            created_at: "2016-05-15 12:21:38",
            status: 1
        },
        {
            title:
                {
                    en: "Some post title in English 2",
                    es: "Some post title in Spanish 2"
                },
            url: "a-beautiful-post-2",
            created_at: "2016-05-13 17:53:08",
            status: 0
        }
    ],
    pipes: ['translateTitle', null, 'humanizeDate', 'getStatus']
}

<table>
    <tr *ngFor="let row in posts">
        <td *ngFor="let column in row; let i = index">{{ column | dynamicPipe:pipes[i] }}</td>
    </tr>
</table>

Reviendra:

| title          | url            | date           | status         |
| Some post t...   a-beautiful...   an hour ago      enabled
| Some post ...2   a-beautifu...2   2 days ago       disabled
3
borislemke

Malheureusement, je ne le pense pas. C'est la même chose que dans angular1 où vous avez une fonction qui retourne une chaîne pour le Pipe dynamique que vous voulez.

En regardant les documents, c'est exactement comme ça qu'ils le montrent aussi.

https://angular.io/docs/ts/latest/guide/pipes.html

template: `
   <p>The hero's birthday is {{ birthday | date:format }}</p>
   <button (click)="toggleFormat()">Toggle Format</button>
`

Puis dans le contrôleur:

get format()   { return this.toggle ? 'shortDate' : 'fullDate'}

Hélas, cela pourrait être pire! :)

En s'appuyant sur @Balu, répondez à ce que je devais faire pour le faire fonctionner avec Angular 9

import { Injector, Pipe, PipeTransform } from '@angular/core';
import { PercentPipe, CurrencyPipe, DecimalPipe } from '@angular/common';

@Pipe({
    name: 'dynamicPipe'
})

export class DynamicPipe implements PipeTransform {

    public constructor(private injector: Injector, private percentPipe: PercentPipe) {
    }

    transform(value: any, pipeToken: any, pipeArgs: any[]): any {

        const MAP = { 'currency': CurrencyPipe, 'decimal': DecimalPipe, 'percent': PercentPipe }

        if (pipeToken && MAP.hasOwnProperty(pipeToken)) {
            var pipeClass = MAP[pipeToken];
            var pipe = this.injector.get(pipeClass);
            if (Array.isArray(pipeArgs)) {
                return pipe.transform(value, ...pipeArgs);
            } else {
                return pipe.transform(value, pipeArgs);
            }
        }
        else {
            return value;
        }
    }
}
0
Shawn