AngularJS Component with TypeScript

Also available in portuguese

The goal is to show the component pattern and concept using AngularJS Component. I demonstrate how to separate all responsabilities in application. I highly recommend the use of TypeScript it is much more productive and reduces chances of error at runtime.

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

The AngularJS Component is basically defined by two classes: PessoaComponent and PessoaComponentController. The first one is the definition itself and the other one is which contains all events, methods and rules for the component.

The “controller” has some internal methods named events and those methods are called in some time in the component life cycle:

1 – constructor – Component constructor, enter only once, first to be called.
2 – $onInit – initialization, called after constructor.
3 – $onChanges – Any data that is modified inside or outside the component will enter this event.

Component definition:

(() => {

    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 of the 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 of the 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>

This is it. I hope it helped.

AngularJS Component with TypeScript

AngularJS Component: Questions, suggestions and feedbacks will be appreciated. Good luck!

Do complete download of the source code on github.

Try demo online of that application on codefinal.
About the Author:
He works as a solution architect and developer, has more than 16 years of experience in software development on several platforms and more than 14 years only for the insurance market.