Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Angular ONE with ngMetadata

Angular ONE with ngMetadata

Angular 1 is dead, right? Long live Angular 2. Well, Not entirely! There are thousands of apps written in old good Angular 1. Also Angular 2 ecosystem is very small, because stable version was released 2 months ago. For sure you wanna migrate to Angular 2 or be prepared when the right time comes right? Typescript and es2015 will push you forward but not entirely, Angular 1 is still awkward to use with those "new" standards. Don't you worry, there is a solution and yes it has a name! ngMetadata

ngMetadata: https://github.com/ngParty/ng-metadata

Martin Hochel

November 23, 2016
Tweet

More Decks by Martin Hochel

Other Decks in Programming

Transcript

  1. angular .module('app', ['ui.router','ocLazyLoad']) .service('logger', ['$log', function LoggerService($log) {} ]) .filter('wat',

    function watFilter(){}) .directive('uhOh', function uhOhDirective(){ return { bindToController: { one: '<', two: '@', onThree: '&' } controller: class UhOhCtrl{ static $inject = ['$http']; constructor($http){ this.$http = $http } } } });
  2. @Component({ selector: 'my-uh-oh', }) class UhOhComponent { @Input() one; @Input()

    two; @Output() three = new EventEmitter(); } @NgModule({ declarations: [UhOhComponent] providers: [LoggerService] }) class AppModule{} Angular 2
  3. My codebase <section id="todoapp"> <header id="header"> <h1>todos</h1> <form id="todo-form" ng-submit="addTodo()">

    <input id="new-todo" placeholder="What needs to be done?" ng-model="newTodo" ng-disabled="saving" autofocus> </form> </header> <section id="main" ng-show="todos.length" ng-cloak> <input id="toggle-all" type="checkbox" ng-model="allChecked" ng-click="markAll(allChecked)"> <label for="toggle-all">Mark all as complete</label> <ul id="todo-list"> <li ng-repeat="todo in todos | filter:statusFilter track by $index" ng-class="{completed: todo.completed, editing: todo == editedTodo}"> <div class="view"> <input class="toggle" type="checkbox" ng-model="todo.completed" ng-change="toggleCompleted(todo)"> <label ng-dblclick="editTodo(todo)">{{todo.title}}</label> <button class="destroy" ng-click="removeTodo(todo)"></button> </div> <form ng-submit="saveEdits(todo, 'submit')"> <input class="edit" ng-trim="false" ng-model="todo.title" todo-escape="revertEdits(todo)" ng-blur="saveEdits(todo, 'blur')" todo-focus="todo == editedTodo"> </form> </li> </ul> </section> <footer id="footer" ng-show="todos.length" ng-cloak> <span id="todo-count"><strong>{{remainingCount}}</strong> <ng-pluralize count="remainingCount" when="{ one: 'item left', other: 'items left' }"></ng-pluralize> </span> <ul id="filters"> <li> <a ng-class="{selected: status == ''} " href="#/">All</a> </li> <li> <a ng-class="{selected: status == 'active'}" href="#/active">Active</a> </li> <li>
  4. Refactored! export const TodoFormComponent = { bindings: { todo: '<',

    onAddTodo: '&' }, templateUrl, controller: class TodoFormComponent { static $inject = [‘eventEmitter’] constructor(EventEmitter) { this.EventEmitter = EventEmitter; } $onChanges(changes) { if (changes.todo) { this.todo = Object.assign({}, this.todo); } } onSubmit() { // without EventEmitter wrapper this.onAddTodo({ $event: { todo: this.todo } }); } } };
  5. Hybrid app - downgraded ng2 component import { HeroDetailComponent }

    from './hero-detail.component'; import { downgradeComponent } from '@angular/upgrade/static'; angular.module('heroApp', []) .controller('MainController', MainController) .directive('heroDetail', downgradeComponent({ component: HeroDetailComponent, inputs: ['hero'], outputs: ['deleted'] }) as angular.IDirectiveFactory);
  6. Hybrid app - upgraded ng1 service to ng2 @NgModule({ imports:

    [ BrowserModule, UpgradeModule ], providers: [{ provide: 'heroes', useFactory: (i: any) => i.get('heroes'), deps: ['$injector'] }], /* . . . */ }) export class AppModule { ngDoBootstrap() {} } @Component({ selector: 'hero-detail', template: ` <h2>{{hero.id}}: {{hero.name}}</h2> ` }) export class HeroDetailComponent { hero: Hero; constructor(@Inject('heroes') heroes) { this.hero = heroes.get()[0]; } }
  7. Did you notice? angular.module('heroApp', []) .controller('MainController', MainController) .directive('heroDetail', downgradeComponent({ component:

    HeroDetailComponent, inputs: ['hero'], outputs: ['deleted'] }) as angular.IDirectiveFactory); @NgModule({ providers: [{ provide: 'heroes', useFactory: (i: any) => i.get('heroes'), deps: ['$injector'] }], /* . . . */ }) export class AppModule { ngDoBootstrap() {} } @Component({...}) export class HeroDetailComponent { hero: Hero; constructor(@Inject('heroes') heroes) { this.hero = heroes.get()[0]; } }
  8. @Component import { Component, EventEmitter, Input, Output } from 'ng-metadata/core';

    import { Hero } from '../hero'; @Component({ selector: 'hero-detail', template: ` <h2>{{$ctrl.hero.name}} details!</h2> <div><label>id: </label>{{$ctrl.hero.id}}</div> <button ng-click="$ctrl.onDelete()">Delete</button> ` }) export class HeroDetailComponent { @Input() hero: Hero; @Output() deleted = new EventEmitter<Hero>(); onDelete() { this.deleted.emit(this.hero); } }
  9. @Directive import { Directive, HostListener, Input, Inject, Output, EventEmitter }

    from 'ng-metadata/core'; @Directive({ selector: '[myHighlight]' }) export class HighlightDirective { @Input() set defaultColor(colorName: string){ this._defaultColor = colorName || this._defaultColor; } @Input('color') highlightColor: string; @Output() onColorClick = new EventEmitter<string>(); @HostListener('mouseenter') onMouseEnter() { this.highlight(this.highlightColor || this._defaultColor); } @HostListener('mouseleave') onMouseLeave() { this.highlight(null); } }
  10. @Injectable import {Injectable} from 'ng-metadata/core' import { Hero } from

    './hero'; @Injectable() export class HeroesService { get() { return [ new Hero(1, 'Windstorm'), new Hero(2, 'Spiderman'), ]; } } import { Component,OnInit,HostBinding,Inject } from 'ng-metadata/core' @Component({ selector: 'my-heroes', template: `...` }) export class HeroesComponent implements OnInit { @HostBinding('attr.role') role = 'widget'; constructor( private heroesSvc: HeroesService, @Inject('$log') private $log: ng.ILogService ) {} ngOnInit(){ this.$log.log( this.heroesSvc.get() );
  11. @Pipe import { Pipe } from 'ng-metadata/core'; @Pipe( { name:'uppercase'

    } ) class UppercasePipe{ transform( input: string):string{ return input.toUpperCase(); } }
  12. provide import {provide} from 'ng-metadata/core'; export const MyApp = angular.module(

    'myApp', [] ) .directive( ...provide( MyCmp ) ) .directive( ...provide( MyAttr ) ) .filter( ...provide( UppercasePipe ) ) .service( ...provide( Greeter ) );
  13. @NgModule import { NgModule } from 'ng-metadata/core'; @NgModule({ declarations: [

    MyPipe, MyComponent ], providers: [ MyService ], imports: [ MyCommonModule, ThirdPartyModule, ‘ui.router’ ] }) export class MyModule {}
  14. bootstrap // main.ts import { platformBrowserDynamic } from 'ng-metadata/platform-browser-dynamic'; import

    { AppModule } from './app.module'; platformBrowserDynamic().bootstrapModule( AppModule );
  15. ngMetadata @Component import { Component, EventEmitter, Input, Output } from

    'ng-metadata/core'; import { Hero } from '../hero'; @Component({ selector: 'hero-detail', template: ` <h2>{{$ctrl.hero.name}} details!</h2> <div><label>id: </label>{{$ctrl.hero.id}}</div> <button ng-click="$ctrl.onDelete()">Delete</button> ` }) export class HeroDetailComponent { @Input() hero: Hero; @Output() deleted = new EventEmitter<Hero>(); onDelete() { this.deleted.emit(this.hero); } }
  16. Upgrade to ng2 @Component import { Component, EventEmitter, Input, Output

    } from '@angular/core'; import { Hero } from '../hero'; @Component({ selector: 'hero-detail', template: ` <h2>{{hero.name}} details!</h2> <div><label>id: </label>{{hero.id}}</div> <button (click)="onDelete()">Delete</button> ` }) export class HeroDetailComponent { @Input() hero: Hero; @Output() deleted = new EventEmitter<Hero>(); onDelete() { this.deleted.emit(this.hero); } } ‘ng-metadata/core’ => ‘@angular/core’ ng-* => (*) or [ * ] $ctrl.* => *
  17. ngMetadata @NgModule import { NgModule } from 'ng-metadata/core'; import {

    HeroDetailComponent } from './hero-detail.component'; @NgModule({ declarations:[ HeroDetailComponent ] }) export class AppModule {};
  18. Downgrade so it works with ng1 @Component import { downgradeComponent

    } from '@angular/upgrade/static/'; import { provideNg2Component } from 'ng-metadata/upgrade'; import { NgModule } from 'ng-metadata/core'; import { HeroDetailComponent } from './hero-detail.component'; @NgModule({ declarations:[ provideNg2Component({ component: HeroDetailComponent, downgradeFn:downgradeComponent }) ] }) export class AppModule {};