Criando AngularJS Component em TypeScript

Disponível também em inglês

O objetivo é mostrar o conceito de componente usando AngularJS Component. Eu demonstro como separar as responsabilidade na aplicação. Eu recomendo muito o uso de TypeScript pois é muito produtivo e reduz o erro em tempo de execução.

Chamada do AngularJS Component:

<fsl-pessoa layout="edicao"             model="$ctrl.pessoa"             on-event="$ctrl.onChanges(evento, pessoa, index)">
</fsl-pessoa>

O AngularJS Component é basicamente definido por duas classes: PessoaComponent e PessoaComponentController. A primeira é a definição em si do componente e a outra os métodos e eventos necessários que execução das regras do componente.

Esse último, o “controller” possui algumas métodos internos “eventos” que são chamados no ciclo de vida do componente, nesse exemplo são eles:

1 – constructor – construtor do componente, entra apenas uma vez, primeiro a ser chamado.
2 – $onInit – inicializados, chamado logo após o construtor.
3 – $onChanges – qualquer dado que seja modificado dentro ou fora do componente entrará nesse evento.

Definição do component:

(() => {

    class PessoaComponent implements ng.IComponentOptions {

        bindings: { [binding: string]: string };
        controller = App.Pessoa.PessoaComponentController;

        templateUrl = ['util', '$attrs', (util: App.IUtilProvider, $attrs: ng.IAttributes) => {
            return util.buildTemplateUrl('pessoa/pessoa', $attrs['layout'] || '');
        }];

        constructor() {
            this.bindings = {
                model: '<',
                onEvent: '&'
            };
        }

    }

    angular
        .module('app.pessoa')
        .component('fslPessoa', new PessoaComponent());

})();

namespace App.Pessoa {

    export class PessoaComponentController implements ng.IComponentController {

        model: App.Pessoa.IPessoa | App.Pessoa.IPessoa[];
        pessoa: App.Pessoa.IPessoa;
        pessoas: App.Pessoa.IPessoa[];
        onEvent: (values: any) => void;

        constructor(
            private util: App.IUtilProvider
        ) {

        }
        
        $onInit = () => {
            this.receberModel(this.model);
        }
                
        $onChanges = (changes) => {
            if (!changes.model.isFirstChange()) {
                this.receberModel(changes.model.currentValue);
            }
        }

        incluirPessoa = () => {
            var pessoa = this.criarNovaPessoa();
            this.pessoas.push(pessoa);
            this.dispararEvento("incluir", pessoa);
        }

        excluirPessoa = (pessoa: App.Pessoa.IPessoa) => {
            var index = this.pessoas.indexOf(pessoa);
            this.pessoas.splice(index, 1);
            this.dispararEvento("excluir", pessoa);
        }

        editarPessoa = (pessoa: App.Pessoa.IPessoa) => {
            this.dispararEvento("editar", pessoa);
        }

        salvarPessoa = () => {
            this.dispararEvento("salvar", this.pessoa);
            this.pessoa = this.criarNovaPessoa();
        }

        private criarNovaPessoa = () => {
            return {
                id: this.util.generateGuid()
            }
        }

        private dispararEvento = (evento: string, pessoa: App.Pessoa.IPessoa, index?: number) => {
            if (angular.isDefined(this.onEvent)) {
                this.onEvent({ pessoa: pessoa, evento: evento, index: index });
            }
        }

        private receberModel = (model: App.Pessoa.IPessoa | App.Pessoa.IPessoa[]) => {
            if (model) {
                if (angular.isArray(model)) {
                    this.pessoas = <App.Pessoa.IPessoa[]>model;
                } else {
                    angular.copy(<App.Pessoa.IPessoa>model, this.pessoa);
                }
            }

            this.pessoa = this.pessoa || this.criarNovaPessoa();
            this.pessoas = this.pessoas || [];
        }

    }

    PessoaComponentController.$inject = [
        'util'
    ];

}

Template do component (pessoa-edicao.html):

Nome: <input type="text"
             ng-model="$ctrl.pessoa.nome" 
             ng-model-options="{ updateOn: 'blur' }" /><br /> 

E-mail: <input type="text" 
               ng-model="$ctrl.pessoa.email" 
               ng-model-options="{ updateOn: 'blur' }" /><br />

Idade: <input type="text" 
              ng-model="$ctrl.pessoa.idade" 
              ng-model-options="{ updateOn: 'blur' }" /><br />

<button type="button" 
        ng-click="$ctrl.salvarPessoa()" 
        ng-disabled="!$ctrl.pessoa.nome">salvar pessoa</button>

Template do component (pessoa.lista-edicao.html):

<table style="width:100%">
    <tr>
        <th>Nome</th>
        <th>E-mail</th>
        <th>Idade</th>
    </tr>
    <tr ng-repeat="pessoa in $ctrl.pessoas track by $index">
        <td ng-bind="pessoa.nome"></td>
        <td ng-bind="pessoa.email"></td>
        <td ng-bind="pessoa.idade"></td>
        <td>
            <button type="button" ng-click="$ctrl.editarPessoa(pessoa)">editar</button>
            <button type="button" ng-click="$ctrl.excluirPessoa(pessoa)">excluir</button>
        </td>
    </tr>
</table>

<button type="button" ng-click="$ctrl.incluirPessoa()">incluir pessoa</button>

Bom é isso. Espero que tenha ajudado.

Abaixo encontrará alguns links úteis.

Criando AngularJS Component em TypeScript

AngularJS Component: Perguntas, sugestões ou críticas são bem vindas. Boa sorte!

Faça download completo do código fonte no github.
Sobre o Autor:
Trabalha como arquiteto de soluções e desenvolvedor, tem mais de 18 anos de experiência em desenvolvimento de software em diversas plataformas sendo mais de 16 anos somente para o mercado de seguros.