web-dev-qa-db-fra.com

Quelle est la meilleure pratique pour créer un composant AngularJS 1.5 dans Typescript?

J'expérimente avec la syntaxe .component() dans Angular 1.5.

Il semble que la dernière mode consiste à coder le contrôleur en ligne dans le composant plutôt que dans un fichier séparé, et je peux voir l'avantage de cela étant donné que le composant passe-partout est minimal.

Le problème est que j'ai codé mes contrôleurs en tant que classes TypeScript et que je souhaite continuer à le faire car cela semble être cohérent avec Angular2.

Mon meilleur effort est quelque chose comme ça:

export let myComponent = {
  template: ($element, $attrs) => {
    return [
      `<my-html>Bla</my-html>`
    ].join('')
  },
  controller: MyController
};
class MyController {

}

Cela fonctionne, mais ce n'est pas élégant. Y a-t-il un meilleur moyen?

36
kpg

Si vous souhaitez adopter complètement une approche Angular 2, vous pouvez utiliser:

module.ts

import { MyComponent } from './MyComponent';

angular.module('myModule', [])
  .component('myComponent', MyComponent);

MyComponent.ts

import { Component } from './decorators';

@Component({
  bindings: {
    prop: '<'
  },
  template: '<p>{{$ctrl.prop}}</p>'
})
export class MyComponent {

   prop: string;

   constructor(private $q: ng.IQService) {}

   $onInit() {
     // do something with this.prop or this.$q upon initialization
   }
}

decorators.ts

/// <reference path="../typings/angularjs/angular.d.ts" />

export const Component = (options: ng.IComponentOptions) => {
  return controller => angular.extend(options, { controller });
};
35
scarlz

J'utilise un décorateur TypeScript simple pour créer le composant

function Component(moduleOrName: string | ng.IModule, selector: string, options: {
  controllerAs?: string,
  template?: string,
  templateUrl?: string
}) {
  return (controller: Function) => {
    var module = typeof moduleOrName === "string"
      ? angular.module(moduleOrName)
      : moduleOrName;
    module.component(selector, angular.extend(options, { controller: controller }));
  }
}

donc je peux l'utiliser comme ça

@Component(app, 'testComponent', {
  controllerAs: 'ct',
  template: `
    <pre>{{ct}}</pre>
    <div>
      <input type="text" ng-model="ct.count">
      <button type="button" ng-click="ct.decrement();">-</button>
      <button type="button" ng-click="ct.increment();">+</button>
    </div>
  `
})
class CounterTest {
  count = 0;
  increment() {
    this.count++;
  }
  decrement() {
    this.count--;
  }
}

Vous pouvez essayer un jsbin de travail ici http://jsbin.com/jipacoxeki/edit?html,js,output

32
Ali Malekpour

C'est le motif que j'utilise:

ZippyComponent.ts

import {ZippyController} from './ZippyController';

export class ZippyComponent implements ng.IComponentOptions {

    public bindings: {
        bungle: '<',
        george: '<'
    };
    public transclude: boolean = false;
    public controller: Function = ZippyController;
    public controllerAs: string = 'vm'; 
    public template: string = require('./Zippy.html');
}

ZippyController.ts

export class ZippyController {

    bungle: string;
    george: Array<number>;

    static $inject = ['$timeout'];

    constructor (private $timeout: ng.ITimeoutService) {
    }
}

Zippy.html

<div class="zippy">
    {{vm.bungle}}
    <span ng-repeat="item in vm.george">{{item}}</span>
</div>

main.ts

import {ZippyComponent} from './components/Zippy/ZippyComponent';

angular.module('my.app', [])
    .component('myZippy', new ZippyComponent());
13
romiem

Je me débattais avec la même question et ai mis ma solution dans cet article:

http://almerosteyn.github.io/2016/02/angular15-component-TypeScript

module app.directives {

  interface ISomeComponentBindings {
    textBinding: string;
    dataBinding: number;
    functionBinding: () => any;
  }

  interface ISomeComponentController extends ISomeComponentBindings {
    add(): void;
  }

  class SomeComponentController implements ISomeComponentController {

    public textBinding: string;
    public dataBinding: number;
    public functionBinding: () => any;

    constructor() {
      this.textBinding = '';
      this.dataBinding = 0;
    }

    add(): void {
      this.functionBinding();
    }

  }

  class SomeComponent implements ng.IComponentOptions {

    public bindings: any;
    public controller: any;
    public templateUrl: string;

    constructor() {
      this.bindings = {
        textBinding: '@',
        dataBinding: '<',
        functionBinding: '&'
      };
      this.controller = SomeComponentController;
      this.templateUrl = 'some-component.html';
    }

  }

  angular.module('appModule').component('someComponent', new SomeComponent());

}
9
Almero Steyn

J'utilise le modèle suivant à utiliser angular 1.5 composant avec TypeScript

class MyComponent {
    model: string;
    onModelChange: Function;

    /* @ngInject */
    constructor() {
    }

    modelChanged() {
        this.onModelChange(this.model);
    }
}

angular.module('myApp')
    .component('myComponent', {
        templateUrl: 'model.html',
        //template: `<div></div>`,
        controller: MyComponent,
        controllerAs: 'ctrl',
        bindings: {
            model: '<',
            onModelChange: "&"
        }
    });
7
Dor Cohen

Je crois qu'une bonne approche consiste à utiliser angular-ts-decorators . Avec lui, vous pouvez définir des composants dans AngularJS comme ceci:

import { Component, Input, Output } from 'angular-ts-decorators';

@Component({
  selector: 'myComponent',
  templateUrl: 'my-component.html
})
export class MyComponent {
    @Input() todo;
    @Output() onAddTodo;

    $onChanges(changes) {
      if (changes.todo) {
        this.todo = {...this.todo};
      }
    }
    onSubmit() {
      if (!this.todo.title) return;
      this.onAddTodo({
        $event: {
          todo: this.todo
        }
      });
    }
}

puis enregistrez-les dans votre module en utilisant:

import { NgModule } from 'angular-ts-decorators';
import { MyComponent } from './my-component';

@NgModule({
  declarations: [MyComponent]
})
export class MyModule {}

Si vous voulez vérifier un exemple d'application réelle utilisant cette application, vous pouvez vérifier celui-ci .

1
Francesco Borzi

Je suggère de ne pas utiliser de solutions personnalisées, mais d'utiliser le ng-metadata bibliothèque à la place. Vous pouvez le trouver sur https://github.com/ngParty/ng-metadata . Ainsi, votre code est le plus compatible avec Angular 2 possible. Et comme indiqué dans le fichier readme, il est

Pas de piratage. Aucun remplacement. Production prête.

Je viens de changer après avoir utilisé une solution personnalisée parmi les réponses fournies, mais c'est plus facile si vous utilisez cette bibliothèque tout de suite. Sinon, vous devrez migrer toutes les petites modifications de syntaxe. Un exemple serait que les autres solutions utilisent ici la syntaxe

@Component('moduleName', 'selectorName', {...})

while Angular 2 utilisations

@Component({
  selector: ...,
  ...
})

Donc, si vous n'utilisez pas ng-metadata _ tout de suite, vous augmenterez considérablement l’effort de migration de votre base de code ultérieurement.

Un exemple complet de la meilleure pratique pour écrire un composant serait le suivant :

// hero.component.ts
import { Component, Inject, Input, Output, EventEmitter } from 'ng-metadata/core';

@Component({
  selector: 'hero',
  moduleId: module.id,
  templateUrl: './hero.html'
})
export class HeroComponent {

  @Input() name: string;
  @Output() onCall = new EventEmitter<void>();

  constructor(@Inject('$log') private $log: ng.ILogService){}

}

(copié à partir de recettes ng-métadonnées )

1
bersling