78 votes

Appeler une fonction statique à partir d'un modèle angular2

J'essaie de construire des services "utilitaires" (classes) pour un projet angulaire. Les classes utilitaires ont des fonctions statiques (pour ne pas avoir à instancier des objets inutiles). Voici un exemple :

import { Injectable } from '@angular/core';

@Injectable()
export class DateService {
   constructor() {
   }

   public static parseDate(dateString: string): Date {
       if (dateString) {
           return new Date(dateString);
       } else {
           return null;
       }
   }
}

Dans mon fichier de classe de composant, je l'importe ensuite comme suit :

import { DateService } from '../utilities/date.service';

et dans la classe, un code comme celui-ci fonctionne :

ngOnInit():void {
  let temp = DateService.parseDate("2012/07/30");
  console.log(temp);  // Mon Jul 30 2012 00:00:00 GMT-0500 (Central Daylight Time)
 }

Cependant, j'aimerais pouvoir utiliser ces fonctions utilitaires dans le modèle html angulaire, comme suit :

<label for="date">Date</label>
          <input type="date" class="form-control" id="date" required
            [value]="event.date | date: 'yyyy-MM-dd'" (input)="event.date=DateService.parseDate($event.target.value)" name="date">

Malheureusement, cela ne fonctionne pas ; cela donne une erreur "Cannot read property 'parseDate' of undefined".

Maintenant, je peux déplacer la fonction "parseDate" vers la classe du composant, et cela fonctionne bien (avec le changement nécessaire dans le modèle, bien sûr)... cependant, si j'ai un tas de composants, ils devront tous avoir leur propre fonction "parseDate" et je pense que nous savons tous que c'est une mauvaise idée qui ne s'adapte pas bien. (veuillez ignorer la nature triviale de la fonction parseDate)

De plus, même si je ne souhaite pas vraiment instancier un objet juste pour exécuter une fonction statique, j'essaie de le faire avec une injection de dépendance réelle. Je l'ajoute au tableau des fournisseurs, et je construis une instance dans le constructeur - comme ceci :

constructor(private _dateService: DateService) { }

et ensuite changer mon modèle en :

label for="date">Date</label>
          <input type="date" class="form-control" id="date" required
            [value]="event.date | date: 'yyyy-MM-dd'" (input)="event.date=_dateService.parseDate($event.target.value)" name="date">

Cela échoue également, cette fois avec une erreur "self.context._dateService.parseDate is not a function". En supprimant le "static" de la fonction, le problème est résolu et je peux passer à autre chose, mais je dois toujours instancier quelque chose juste pour exécuter ce que j'ai fait. debe être une simple fonction statique. Il existe sûrement une meilleure solution.

Qu'en pensez-vous ?

0 votes

Vous pouvez créer un tube personnalisé pour éviter d'avoir à appeler la fonction statique dans le modèle.

0 votes

Si vous allez injecter un service, alors pourquoi utiliser le mot-clé static ? Qu'y a-t-il de mal à traiter une seule instance du service ?

115voto

Günter Zöchbauer Points 21340

Seuls les membres d'instance de la classe des composants peuvent être appelés depuis la vue.

Si vous voulez appeler des membres statiques, vous devez fournir un getter dans le composant.

export class MyComponent {
  parseDate = DateService.parseDate;
}

alors vous pouvez l'utiliser comme

(input)="event.date=parseDate($event.target.value)"

1 votes

Pourriez-vous également créer un décorateur personnalisé pour ajouter la fonction statique au prototype ? Cela permettrait également de la rendre disponible dans le modèle, mais pas dans le corps du composant.

0 votes

Désolé, je ne connais pas les décorateurs personnalisés. Je n'utilise pas TS moi-même.

1 votes

Je ne vois pas de "vrai" getter en TypeScript : public get parseDate(){ return DateService.parseDate;}

32voto

chrispy Points 2550

La réponse de Gunter est parfaitement valable et c'est la façon dont j'ai procédé la plupart du temps.

Si vous utilisez Typescript, vous avez en outre la possibilité de créer un décorateur personnalisé pour fournir des fonctions à votre vue afin que votre composant reste épuré.

Exemple :

Définir un décorateur :

import {StaticClassFunctions} from "./static-class-functions"

export function CustomDecorator(): Function {
    return (target: Function): Function => {
        target.prototype.yourStaticMethod = (param1) => {
            return StaticClassFunctions.yourStaticMethod(param1);
        }
    }
}

Appliquez le décorateur à votre composant :

@Component{ ... }
@CustomDecorator()
export class YourComponent { ... }

Vous avez maintenant accès à ces fonctions statiques dans votre vue sans avoir à les déclarer dans votre composant ! Très utile pour les fonctions "utilitaires" répétitives pour supporter le formatage de la vue et autres (comme le casting d'enum !).

<span>{{yourStaticMethod(yourInput)}}</span>

Vous n'y aurez pas accès dans votre composant, à moins que vous ne déclariez la fonction en haut de la page pour qu'elle puisse être compilée.

1 votes

Si j'ai plusieurs fonctions statiques, dois-je écrire un décorateur personnalisé pour chacune de ces fonctions ?

0 votes

Non ! Vous pouvez mettre autant de fonctions que vous voulez dans ce décorateur. Le décorateur est returning la fonction qu'elle décore et la prend comme argument, vous pouvez donc la modifier comme bon vous semble.

4 votes

J'ai copié-collé cette chose exacte. J'obtiens TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.

32voto

Sergey Kandaurov Points 1114

Vous pouvez déclarer un champ dans votre composant qui rendra la classe accessible pour votre modèle.

export class YourComponent {
    public DateService= DateService;
}

1 votes

C'est beaucoup plus facile que d'utiliser un décorateur, +1 pour la simplicité

0 votes

Ce n'est probablement pas la "bonne" façon de faire, mais c'est certainement la plus simple de toutes ces options et elle fonctionne comme prévu !

1 votes

Assurez-vous de mettre DateService= DateService et non DateService : DateService. J'espère que cela vous fera gagner 5 minutes

11voto

Martin Points 582

Il existe déjà un modèle pour faire cela dans Angular appelé pipes . Dans Angular 1, cela s'appelait filters .

Dans Angular, vous créez votre classe de tuyaux personnalisée et utilisez la fonction | dans le modèle pour passer des valeurs. Il y en a un intégré pour les dates, il est utilisé comme suit :

{{ "2017-01-24" | parseDate }}

Bien sûr, si ce tuyau ne fait pas ce que vous voulez, vous pouvez créer le vôtre :

@Pipe({
  name: 'parseDate'
})
export class ParseDate implements PipeTransform {

  transform(value: string): string {
    return DateService.parseDate(value);
  }
}

Pour plus d'informations, veuillez consulter le site : https://angular.io/docs/ts/latest/guide/pipes.html

3voto

Nero theZero Points 926

C'est comme ça que je l'ai fait une fois -

public get DateService(): typeof DateService {
    return DateService;
}

et l'a utilisé à partir du modèle comme -

(input)="event.date=DateService.parseDate($event.target.value)"

Il s'agit essentiellement de la version de @Sergey, mais sous la forme d'un getter plus "TypeScripty" qui précise que je renvoie le type de la classe et donc ses membres statiques seraient exposés au modèle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X