Какова наилучшая практика создания компонента AngularJS 1.5 в Typescript?
Я экспериментирую с синтаксисом .component()
в Angular 1.5.
Кажется, что последний способ состоит в том, чтобы закодировать контроллер в строке в компоненте, а в отдельном файле, и я вижу преимущество этого, учитывая, что шаблон компонента минимален.
Проблема в том, что я кодировал мои контроллеры как классы typescript и хотел бы продолжать делать это, потому что это похоже на Angular2.
Мое лучшее усилие - это что-то вроде этого:
export let myComponent = {
template: ($element, $attrs) => {
return [
`<my-html>Bla</my-html>`
].join('')
},
controller: MyController
};
class MyController {
}
Это работает, но это не изящно. Есть ли лучший способ?
Ответы
Ответ 1
Если вы хотите полностью принять подход Angular 2, вы можете использовать:
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
Я использую простой декодер Typescript для создания компонента
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 }));
}
}
поэтому я могу использовать его так
@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--;
}
}
Здесь вы можете попробовать рабочий jsbin http://jsbin.com/jipacoxeki/edit?html,js,output
Ответ 3
Это шаблон, который я использую:
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
Я боролся с тем же вопросом и поставил свое решение в этой статье:
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
Я использую следующий шаблон для использования angular 1.5 component с 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
Я бы предложил не использовать специально разработанные решения, а вместо этого использовать библиотеку ng-metadata
. Вы можете найти его на https://github.com/ngParty/ng-metadata. Таким образом, ваш код наиболее совместим с Angular 2. И как указано в readme, это
Нет хаков. Нет переопределений. Производство готово.
Я просто переключился после использования пользовательского решения из ответов здесь, но это проще, если вы сразу же используете эту библиотеку. В противном случае вам придется перенести все небольшие изменения синтаксиса. Одним из примеров является то, что в других решениях здесь используется синтаксис
@Component('moduleName', 'selectorName', {...})
а Angular 2 использует
@Component({
selector: ...,
...
})
Итак, если вы не используете ng-metadata
сразу, вы значительно увеличите усилия по переносу своей кодовой базы позже.
Полный пример наилучшей практики написания компонента будет следующим:
// 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){}
}
(скопировано из ng-metadata recipies)
Ответ 7
Я считаю, что один хороший подход - использовать angular-ts-decorators. С его помощью вы можете определить компоненты в AngularJS следующим образом:
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
}
});
}
}
а затем зарегистрируйте их в своем модуле, используя:
import { NgModule } from 'angular-ts-decorators';
import { MyComponent } from './my-component';
@NgModule({
declarations: [MyComponent]
})
export class MyModule {}
Если вы хотите проверить пример реального приложения, используя его, вы можете проверить этот.