{"id":26255,"date":"2018-06-17T10:20:00","date_gmt":"2018-06-17T10:20:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/premier_developer\/?p=26255"},"modified":"2019-02-14T20:18:00","modified_gmt":"2019-02-15T03:18:00","slug":"angular-how-to-simplify-components-with-typescript-inheritance","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/angular-how-to-simplify-components-with-typescript-inheritance\/","title":{"rendered":"Angular How-to: Simplify Components with TypeScript Inheritance"},"content":{"rendered":"<p><strong>Laurie Atkinson, Senior Consultant,<\/strong> By moving shared functionality to a base component and assigning dependencies inside the body of the constructor, you can simplify child components.<\/p>\n<hr>\n<p>If your Angular components contain identical or similar functionality and you find yourself copying and pasting that code, it makes sense to implement some class inheritance, which is available with <a href=\"http:\/\/www.typescriptlang.org\/docs\/handbook\/classes.html\">TypeScript<\/a>. And, you can simplify the child components even further by removing the dependencies from the parent constructor arguments and manually assigning them inside the constructor.<i><\/i><\/p>\n<h3>Create the base component<\/h3>\n<p>In this example, we have 2 services that are used in the base class and are assigned using dependency injection.<\/p>\n<p><b>base.component.ts<\/b><\/p>\n<p><pre><code>\n@Component({\n    template: ''\n})\nexport class BaseComponent {\n\n    constructor(protected utilitiesService: UtilitiesService,\n        protected loggingService: LoggingService) {\n        this.logNavigation();\n    }\n\n    protected logError(errorMessage: string) { . . .}\n    private logNavigation() { . . .}\n}\n<\/code><\/pre>\n<p><br><\/p>\n<h3>Inherit child component from base component<\/h3>\n<p>Note that our child component must pass all parent dependencies into the constructor to extend it.<\/p>\n<p><b>child.component.ts<\/b><\/p>\n<p><pre><code>\n    @Component({ . . . })\n\n    export class ChildComponent extends BaseComponent {\n    \n    constructor(private childDataService: ChildDataService,\n    \n    utilitiesService: UtilitiesService,\n    \n                  loggingService: LoggingService) {\n    \n    super(utilitiesService, loggingService);\n    \n      }\n    \n    }    \n<\/code><\/pre>\n<p><br><\/p>\n<p>As you can see, with a substantially complex base class, the child classes will potentially need to include a much longer list of all its parent\u2019s dependencies. And if the parent introduces a new dependency, then all children must be updated. This may be fine, but if you want to simplify the child classes as much as possible, then take a look at this alternative.<\/p>\n<h3>Use a class to store the injector<\/h3>\n<p>This class will hold the module\u2019s injector. It will be set once and retrieved whenever a component or service needs to get a service dependency.<\/p>\n<p><b><\/b><\/p>\n<p><b>app-injector.service.ts<\/b><\/p>\n<p>\n<pre><code>\n    import { Injector } from '@angular\/core';\n\n    export class AppInjector {\n    \n    private static injector: Injector;\n    \n    static setInjector(injector: Injector) {\n    \n            AppInjector.injector = injector;\n    \n        }\n    \n    static getInjector(): Injector {\n    \n    return AppInjector.injector;\n    \n        }\n    \n    }       \n<\/code><\/pre>\n<p><br>After the module has been bootstrapped, store the module\u2019s injector in the AppInjector class.<b><\/b><\/p>\n<p><b><\/b><\/p>\n<p><b>main.ts<\/b><\/p>\n<p><pre><code>\n    platformBrowserDynamic().bootstrapModule(AppModule).then((moduleRef) =&gt; {\n        AppInjector.setInjector(moduleRef.injector);\n    });        \n<\/code><\/pre>\n<p><br><\/p>\n<p><b><\/b><\/p>\n<h3>Use this injector class to assign dependencies<\/h3>\n<p>Now, we can modify the base component to remove all constructor arguments.<\/p>\n<p><b><\/b><\/p>\n<p><b>base.component.ts<\/b><\/p>\n<p><pre><code>\n    @Component({\n        template: ''\n    })    \n    export class BaseComponent {    \n        protected utilitiesService: UtilitiesService;    \n        protected loggingService: LoggingService;\n    \n    constructor() {    \n            \/\/ Manually retrieve the dependencies from the injector    \n            \/\/ so that constructor has no dependencies that must be passed in from child    \n            const injector = AppInjector.getInjector();    \n            this.utilitiesService = injector.get(UtilitiesService);    \n            this.loggingService = injector.get(LoggingService);    \n            this.logNavigation();\n    \n        }\n    \n        protected logError(errorMessage: string) { . . . }    \n        private logNavigation() { . . . }\n    }         \n<\/code><\/pre>\n<p><br><\/p>\n<h3>Inherit child component from base component<\/h3>\n<p>Now the child component only needs to use dependency injection for its own dependencies. <\/p>\n<p><b>child.component.ts<\/b><\/p>\n<p><pre><code>\n    @Component({ . . . })\n    export class ChildComponent extends BaseComponent {\n    \n    constructor(private childDataService: ChildDataService) {    \n        super();    \n      }    \n    }            \n<\/code><\/pre>\n<p><br><\/p>\n<h3>Use the power of polymorphism to maximize code reuse<\/h3>\n<p>For maximum reuse of code, do not limit the methods in the parent class to merely those with identical implementations. Maybe you can refactor a complex method into pieces where shared logic can be moved into the parent. Maybe a method shares most of the logic with another component, but a few pieces differ between components which could be implemented in each child.<\/p>\n<p>TypeScript inheritance allows you to override a parent method in the child class and if the parent calls that method, the child\u2019s implementation will be invoked. For example, follow the order of execution shown in this picture, starting with a call to methodA() in ChildComponent.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/31\/2019\/04\/clip_image00242.jpg\"><img decoding=\"async\" width=\"244\" height=\"157\" title=\"clip_image002[4]\" alt=\"clip_image002[4]\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/31\/2019\/04\/clip_image0024_thumb2.jpg\" border=\"0\"><\/a><\/p>\n<p>Here is a non-trivial code example to illustrate the power of this feature. This application contains many pages, each comprised of a form. All these form pages share the following functionality: <\/p>\n<ul>\n<li>only allow editing if the user has permission <\/li>\n<li>save changes via a call to an API service<\/li>\n<li>prompt the user if attempting to navigate away and there are unsaved changes<\/li>\n<\/ul>\n<h3>Build a form base component<\/h3>\n<p><b><\/b><\/p>\n<p><b>form.component.ts<\/b><\/p>\n<p><pre><code>\n    @Component({\n        template: ''\n    })\n    export class BaseFormComponent extends BaseComponent implements OnInit {\n    \n        updatePermission = 'UPDATE_FULL'; \/\/ Can override in child component\n    \n        protected routeParamName: string; \/\/ value always set in child\n    \n        protected entity: IEntity; \/\/ data on the form\n    \n        protected original: IEntity; \/\/ data before any changes are made\n    \n        protected dataService: EntityDataService; \/\/ service with http methods\n    \n        protected updateService: UpdateService;\n    \n        protected authorizationService: AuthorizationService;\n    \n        @ViewChild('confirmationDialog') confirmationDialog: ConfirmDialogComponent;\n    \n        constructor(protected route: ActivatedRoute) {\n            super(); \/\/ call parent constructor\n            const injector = AppInjector.getInjector();\n            this.updateService = injector.get(UpdateService);\n            this.authorizationService = injector.get(AuthorizationService);\n        }\n    \n        ngOnInit() {\n            this.route.data.subscribe(data =&gt; { \/\/ data from route resolver\n                this.entity = data[this.routeParamName]; \/\/ routeParamName from child\n                . . .\n                this.additionalFormInitialize(); \/\/ optional initialization in child\n            });\n        }\n    \n        protected additionalFormInitialize() { \/\/ hook for child\n    \n        }\n    \n        \/\/ Child could override this method for alternate implementation\n    \n        hasChanged() { return !UtilitiesService.isEqual(this.original, this.entity); }\n    \n        \/\/ used by canDeactive route guard\n    \n        canDeactivate() {\n            if (!this.hasChanged()) {\n                return true;\n            }\n            return this.showConfirmationDialog().then(choice =&gt; {\n                return choice !== ConfirmChoice.cancel;\n            });\n        }\n    \n        showConfirmationDialog() {\n    \n            return new Promise((resolve) =&gt; {\n    \n                this.confirmationDialog.show();\n                . . .\n    \n                this.save().then(() =&gt; {\n                . . .\n    \n                }, (err) =&gt; {\n                    this.logError(err); \/\/ Implemented in base parent component\n                });\n    \n            });\n    \n        }\n    \n        save() {\n    \n            \/\/ Value of dataService set in the child to support different APIs\n            \/\/ Optionally override saveEntity in the child\n    \n            return this.updateService.update(this.dataService, this.saveEntity).then( . . .\n    \n       }\n    \n        protected get saveEntity() { return this.entity; }\n    \n        \/\/ Use in child HTML template to limit functionality\n        get editAllowed() { \/\/ updatePermission could be overridden in the child\n            return this.authorizationService.hasPermission(this.updatePermission);\n        }\n    \n    }              \n<\/code><\/pre>\n<p><br><\/p>\n<h3>Build a specific form child component<\/h3>\n<p><b><\/b><\/p>\n<p><b>child.component.ts<\/b><\/p>\n<p><pre><code>\n    @Component({. . .})\n\n    export class ChildComponent extends BaseFormComponent {\n    \n        updatePermission = 'CHILD_UPDATE'; \/\/ can override the default permission\n    \n        protected routeParamName = 'child'; \/\/ required by parent to read route data\n    \n        constructor(private childDataService: ChildDataService) {\n    \n            super();\n    \n        }\n    \n        protected get dataService() { return this.childDataService; }\n    \n        protected get saveEntity() {\n    \n            \/\/ perhaps do some custom manipulation of data before saving\n    \n            return localVersionOfEntity;\n    \n        }\n    \n        protected additionalFormInitialize() {\n    \n            \/\/ Add custom validation to fields or other unique actions\n    \n        }\n    \n        hasChanged() {\n    \n            \/\/ Could override this method to implement comparison in another way \n    \n        }\n    \n    }                 \n<\/code><\/pre>\n<p>As you proceed with your development, if you find yourself copying and pasting code, take a moment to see if some refactoring might be in order. Class inheritance can give you an opportunity to delete some code and that always feels good. Doesn\u2019t it?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Laurie Atkinson, Senior Consultant, By moving shared functionality to a base component and assigning dependencies inside the body of the constructor, you can simplify child components. If your Angular components contain identical or similar functionality and you find yourself copying and pasting that code, it makes sense to implement some class inheritance, which is available [&hellip;]<\/p>\n","protected":false},"author":582,"featured_media":37840,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[51,366],"class_list":["post-26255","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-permierdev","tag-angular","tag-typescript"],"acf":[],"blog_post_summary":"<p>Laurie Atkinson, Senior Consultant, By moving shared functionality to a base component and assigning dependencies inside the body of the constructor, you can simplify child components. If your Angular components contain identical or similar functionality and you find yourself copying and pasting that code, it makes sense to implement some class inheritance, which is available [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/26255","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/582"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=26255"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/26255\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/37840"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=26255"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=26255"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=26255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}