2016-02-17 3 views
36

Ich experimentiere mit der .component() Syntax in Angular 1.5.Was ist die beste Vorgehensweise zum Erstellen einer AngularJS 1.5-Komponente in Typescript?

Es scheint, dass die neueste Mode ist, den Controller in-line in der Komponente eher als in einer separaten Datei zu codieren, und ich kann den Vorteil davon sehen, vorausgesetzt, dass die Komponente Boilerplate minimal ist.

Das Problem ist, dass ich meine Controller als Typoskript-Klassen codiert habe und dies auch weiterhin tun möchte, weil das mit Angular2 übereinstimmt.

Meine beste Anstrengung ist so etwas wie dieses:

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

} 

Es funktioniert, aber es ist nicht elegant. Gibt es einen besseren Weg?

+0

Mit nicht elegant meinen Sie, dass Sie den Code aufgeräumt haben wollen? – Katana24

+0

@ Katana24 Ich nehme an, man könnte es so sagen :). Ich konnte kein Beispiel für eine 1.5-Komponente in Typescript finden, also habe ich mich gefragt, ob die Art und Weise, wie ich es gemacht habe, Best Practice ist. z.B. Wie kann ich das Ganze als eine Klasse definieren? – kpg

+0

Um ehrlich zu sein, wenn es gut funktioniert, aber es ist nicht der Stil, eckig 1 Zeug in Typoskript zu schreiben und dein Beitrag ist der erste, den ich gesehen habe. Generell denke ich, dass Sie Angular 1 in reinem Javascript unter Beachtung der empfohlenen Konventionen schreiben sollten. Ich weiß, das beantwortet deine Frage nicht wirklich ... – Katana24

Antwort

7

ich folgendes Muster ist mit Winkel 1.5 verwenden component mit Typoskript

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: "&" 
     } 
    }); 
+2

Der Typ 'Funktion' ist sicherlich eines der Dinge, die mir fehlten. Ich sehe nicht einmal, wo das dokumentiert ist! – kpg

+0

Ich habe Ihren Code ausprobiert, aber wenn ich ihn benutze, erhalte ich den folgenden Fehler: "Klassenkonstruktoren müssen mit | new |" aufgerufen werden. Weißt du, warum? – Shamshiel

+0

@Shamshiel das ist super spät, aber das kann manchmal passieren, wenn Angular nicht erkennen kann, dass ein Objekt eine ES6-Klasse ist. Ich glaube, dass Firefox bei der Deklaration von Komponenten-Controllern besonders anfällig für dieses Problem ist. – abalos

9

Ich war mit der gleichen Frage zu kämpfen und meine Lösung in diesem Artikel setzen:

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()); 
 

 
}

+0

Sie sollten die wichtigen Teile Ihrer Lösung ("Antwort") in Ihre Antwort einfügen. – wpercy

+0

Ja wird es bearbeiten. Posted this von meinem Handy * erröten *. –

12

Dies ist das Muster, das ich verwenden:

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()); 
+0

Da du require benötigst, baust du das mit babel? – joargp

+0

Ich habe mit Webpack gebaut. – romiem

31

ich ein einfaches Typoskript Dekorateur bin mit der Komponente

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 })); 
    } 
} 

so dass es wie dieses Ich

@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--; 
    } 
} 
erstellen

Sie können versuchen, verwenden können ein funktionierender Jsbin hier http://jsbin.com/jipacoxeki/edit?html,js,output

+0

BTW das ist fast identisch mit Angular 2-Komponente, so weniger Schmerzen bei Upgrades.Für den Fall, beide Versionen Dekorateur verwenden können –

+0

in diesem Typoskript Dekorateur Linie 'controllerAs ?: Zeichenfolge,' und die nächsten 2 Zeilen gibt es diesen Fehler in meinem Fall Ng1Component umbenannt werden: 'TS1005:„;“ erwartet ". Warum? :/ Vielen Dank. Ich habe gerade kopiert und eingefügt –

+2

Annahme von [scarlz] (http://stackoverflow.com/a/36634697/4068027) Sie Ihre Optionen geben Sie mit 'ng.IComponentOptions' ersetzen könnte – Aides

33

Wenn Sie einen Winkel 2 Ansatz vollständig übernehmen wollen, könnten Sie verwenden:

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 }); 
}; 
+1

Und wie würden Sie auf "prop" in MyComponent mit Typescript zugreifen? – gerasalus

+2

Es funktioniert perfekt in Chrome, Safari und Rand, aber Firefox wird mit Fehler 'Fehler: Klassenkonstruktoren mit aufgerufen werden müssen | neu |' auf dem '$ controllerInit' Befehl. Irgendwelche Ideen, wie man es repariert? –

+0

Sollte ich jede Modul-Lader dieses NG2 Stil Werke in NG1 zu bekommen? Wie angular weiß diese Komponente nur über Annotation? Haben Sie ein Beispiel Repo? –

1

würde ich vorschlagen, nicht kundenspezifische Lösungen zu verwenden, aber die ng-metadata Bibliothek stattdessen zu verwenden. Sie finden es unter https://github.com/ngParty/ng-metadata. So ist Ihr Code am besten mit Angular 2 kompatibel. Und wie in der Readme-Datei angegeben ist es

No hacks. No overrides. Production ready.

Ich wechselte kurz nach einer maßgeschneiderten Lösung aus den Antworten hier verwendet wird, aber es ist einfacher, wenn Sie diese Bibliothek sofort nutzen. Andernfalls müssen Sie alle kleinen Syntaxänderungen migrieren. Ein Beispiel dafür wäre, dass die anderen Lösungen hier die Syntax

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

während Angular 2 verwendet

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

Also, wenn Sie nicht ng-metadata sofort zu verwenden, werden Sie erheblich erhöhen den Aufwand für die Migration Ihre Codebasis später.

Ein vollständiges Beispiel für die beste Praxis eine Komponente schreiben die folgende wäre:

// 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){} 

} 

(kopiert von ng-metadata recipies)

1

Ich glaube, ein guter Ansatz ist angular-ts-decorators zu verwenden. Mit ihm können Sie Komponenten in AngularJS wie folgt definieren:

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 
     } 
     }); 
    } 
} 

und dann registrieren Sie sie in Ihrem Modul:

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

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

Wenn Sie ein Beispiel einer realen Anwendung, es überprüfen möchten, können Sie Überprüfen Sie this one.

Verwandte Themen