问题
I am experimenting with the .component()
syntax in Angular 1.5.
It seems that the latest fashion is to code the controller in-line in the component rather that in a separate file, and I can see the advantage of that given that the component boilerplate is minimal.
The problem is that I having been coding my controllers as typescript classes and would like to continue doing so because that seems to be consistent with Angular2.
My best effort is something like this:
export let myComponent = {
template: ($element, $attrs) => {
return [
`<my-html>Bla</my-html>`
].join('')
},
controller: MyController
};
class MyController {
}
It works, but it's not elegant. Is there a better way?
回答1:
If you wanted to completely adopt an Angular 2 approach, you could use:
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 });
};
回答2:
I am using a simple Typescript decorator to create the component
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 I can use it like this
@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--;
}
}
You can try a working jsbin here http://jsbin.com/jipacoxeki/edit?html,js,output
回答3:
This is the pattern I use:
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());
回答4:
I was struggling with the same question and put my solution in this 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());
}
回答5:
I'm using the following pattern to use angular 1.5 component with 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: "&"
}
});
回答6:
I'd suggest not to use custom made solutions, but to use the ng-metadata
library instead. You can find it at https://github.com/ngParty/ng-metadata. Like this your code is the most compatible with Angular 2 possible. And as stated in the readme it's
No hacks. No overrides. Production ready.
I just switched after using a custom made solution from the answers here, but it's easier if you use this library right away. Otherwise you’ll have to migrate all the small syntax changes. One example would be that the other solutions here use the syntax
@Component('moduleName', 'selectorName', {...})
while Angular 2 uses
@Component({
selector: ...,
...
})
So if you're not using ng-metadata
right away, you'll considerably increase the effort of migrating your codebase later on.
A full example for the best practice to write a component would be the following:
// 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){}
}
(copied from ng-metadata recipies)
回答7:
I believe one good approach is to use angular-ts-decorators. With it you can define Components in AngularJS like this:
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
}
});
}
}
and then register them in your module using:
import { NgModule } from 'angular-ts-decorators';
import { MyComponent } from './my-component';
@NgModule({
declarations: [MyComponent]
})
export class MyModule {}
If you want to check an example of a real application using it, you can check this one.
来源:https://stackoverflow.com/questions/35451652/what-is-best-practice-to-create-an-angularjs-1-5-component-in-typescript