Angular2: Injecting a provider into a component using a custom decorator or annotation?

Jason Dreyzehner

I'm hiding tabs in Ionic 2 for certain @Pages (an Ionic 2 decorator) using a simple TabsProvider:

tabs.ts

import { Injectable } from 'angular2/core';
import { BehaviorSubject } from 'rxjs';

@Injectable()
export class TabsProvider {
  currentState = new BehaviorSubject<boolean>(true);
  public showTabs(){
    this.currentState.next(true);
  }
  public hideTabs(){
    this.currentState.next(false);
  }
}

The tabs component subscribes to currentState, and TabsProvider is injected into various pages as below:

sample-page.ts:

import {Page} from 'ionic-angular';
import { TabsProvider } from './tabs';

@Page({
  ...
})
export class SamplePage {
  tabsProvider: TabsProvider;

  constructor(tabsProvider: TabsProvider) {
    this.tabsProvider = tabsProvider;
  }

  onPageWillEnter(){
    this.tabsProvider.hideTabs();
  }

  onPageWillLeave(){
    this.tabsProvider.showTabs();
  }
}

This code is practically all boilerplate, and would be much cleaner if I could define this functionality in a decorator (or annotation), e.g.:

import { Page } from 'ionic-angular';
import { hideTabs } from './tabs';

@hideTabs()
@Page({
  ...
})
export class BuyPage {
}

But I'm having trouble determining how to inject TabsProvider and add the onPageWillEnter and onPageWillLeave methods to SamplePage.

Can a decorator (or annotation) somehow inject additional Angular providers?

The farthest I've gotten so far:

in tabs.ts:

export function hideTabs() {
  return function(cls: any) {
    cls.prototype.onPageWillEnter = function() {
      this.tabsProvider.hideTabs();
    };
    cls.prototype.onPageWillLeave = function() {
      this.tabsProvider.showTabs();
    };
    return cls;
  }
}

This gets us part of what we're looking for, but it's still necessary to import and inject TabsProvider as a specific instance member:

sample-page.ts

import {Page, Events} from 'ionic-angular';
import { hideTabs, TabsProvider } from './tabs';

@hideTabs()
@Page({
  ...
})
export class SamplePage {
  constructor(public tabsProvider: TabsProvider) {
  }
}

Is it possible to fully abstract this into @hideTabs()?

Edit:

Relevant parts of the tabs component (for anyone interested in implementing) pages/tabs/tabs.ts:

import { Page } from 'ionic-angular';
import { TabsProvider } from './tabs';

@Page({
  ...
})
export class TabsPage {

  ...

  currentState: boolean;
  constructor(TabsProvider: TabsProvider) {
    TabsProvider.currentState.subscribe((state: boolean) => {
      this.currentState = state;
    });
  }
}

pages/tabs/tabs.html:

<div [ngClass]="{'hide-tabs': !currentState}">
  <ion-tabs>
    ...
  </ion-tabs>
</div>

pages/tabs/tabs.scss:

.hide-tabs ion-tabbar-section {
    display: none;
}
Nico Burns

I have just had the same problem, and this is the solution I came up with. The general approach is to:

  • Use Reflect.getOwnMetadata to read the existing dependencies from the class.
  • Add to the list of dependencies
  • Create a new constructor which wraps the old constructor and deals with these dependencies.

tabs.ts:

    import TabsProvider from "./tabs";

    export function hideTabs() {
      return function(cls: Function) : any {

        // Save existing onPageWillEnter and onPageWillLeave inplementations
        let enter = cls.prototype.onPageWillEnter || function () {};
        let leave = cls.prototype.onPageWillLeave || function () {};

        // Create new constructor for class
        const newCls = function (tabsProvider, ...args) {
            this.__hideTabs__tabsProvider = tabsProvider;
            return cls.apply(this, args);
        }

        // Copy prototype to new constructor
        newCls.prototype = constructor.prototype;

        // Copy metadata to new constructor
        let metadatakeys = Reflect.getMetadataKeys(cls);
        metadatakeys.forEach(function (key) {
          Reflect.defineMetadata(key, Reflect.getOwnMetadata(key, cls), newCls)
        });

        // Read dependencies list from 'cls', add our own dependency, and write list to 'newCls'
        let dependencies = Reflect.getOwnMetadata('design:paramtypes', cls);
        dependencies = [TabsProvider].concat(dependencies)
        Reflect.defineMetadata('design:paramtypes', params, newCls);

        // Define new onPageWillEnter and onPageWillLeave implementation which implement tab show/hide functionality
        // and also call the old implementation saved above (if they exist)
        newCls.prototype.onPageWillEnter = function() {
          this.tabsProvider.hideTabs();
          enter.apply(this, arguments);
        };

        newCls.prototype.onPageWillLeave = function() {
          this.tabsProvider.showTabs();
          leave.apply(this, arguments);
        };

        return newCls;
      }
    }

I would also recommend moving the @hideTabs decorator below the @Page decorator

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Injecting lodash into Angular provider

From Dev

Angular injecting $rootScope in provider

From Dev

Injecting lodash into Angular provider

From Dev

No provider for FormGroupDirective in Angular 2 nested custom component

From Dev

Injecting Constants via Custom Annotation

From Dev

Injecting Service using Angular Seed - 'Unknown provider' error

From Dev

Injecting Service using Angular Seed - 'Unknown provider' error

From Dev

Angular2: No Pipe decorator found on Component

From Dev

Angular2: No Pipe decorator found on Component

From Dev

JQueryUI Sortable not working when using custom component with Angular2

From Dev

Angular2 Testing Component with Provider dependency

From Dev

"Unknown Provider" when injecting custom filter into service

From Dev

Angular injecting $service results in Unknown provider: $serviceProvider

From Dev

ng2 - injecting a provider into a regular class

From Dev

Angular2 Custom Component in NgSwitch

From Dev

Angular2 error - No provider error for my login component

From Dev

Angular2: Need to add provider in component for dependency on service?

From Dev

Angular - Injecting component instance into the view of another component

From Dev

Dynamic Injection in Angular, injecting a component to parent component

From Dev

Unknown Provider in AngularJS when Injecting custom service into different module

From Dev

How to add a html with angular-dart component using Decorator

From Dev

How to add a html with angular-dart component using Decorator

From Dev

Angular 2 custom decorator for versions later than RC4, gives 'More than one component' excepion

From Dev

Angular2 router-link issue when injecting a template component with router links

From Dev

using arrays in a custom annotation

From Dev

Injecting window object into a component into Angular with DI

From Dev

Angular 4: Component disappears after injecting Service

From Dev

Angular2: Injecting a service

From Dev

Angular2: Injecting a service

Related Related

  1. 1

    Injecting lodash into Angular provider

  2. 2

    Angular injecting $rootScope in provider

  3. 3

    Injecting lodash into Angular provider

  4. 4

    No provider for FormGroupDirective in Angular 2 nested custom component

  5. 5

    Injecting Constants via Custom Annotation

  6. 6

    Injecting Service using Angular Seed - 'Unknown provider' error

  7. 7

    Injecting Service using Angular Seed - 'Unknown provider' error

  8. 8

    Angular2: No Pipe decorator found on Component

  9. 9

    Angular2: No Pipe decorator found on Component

  10. 10

    JQueryUI Sortable not working when using custom component with Angular2

  11. 11

    Angular2 Testing Component with Provider dependency

  12. 12

    "Unknown Provider" when injecting custom filter into service

  13. 13

    Angular injecting $service results in Unknown provider: $serviceProvider

  14. 14

    ng2 - injecting a provider into a regular class

  15. 15

    Angular2 Custom Component in NgSwitch

  16. 16

    Angular2 error - No provider error for my login component

  17. 17

    Angular2: Need to add provider in component for dependency on service?

  18. 18

    Angular - Injecting component instance into the view of another component

  19. 19

    Dynamic Injection in Angular, injecting a component to parent component

  20. 20

    Unknown Provider in AngularJS when Injecting custom service into different module

  21. 21

    How to add a html with angular-dart component using Decorator

  22. 22

    How to add a html with angular-dart component using Decorator

  23. 23

    Angular 2 custom decorator for versions later than RC4, gives 'More than one component' excepion

  24. 24

    Angular2 router-link issue when injecting a template component with router links

  25. 25

    using arrays in a custom annotation

  26. 26

    Injecting window object into a component into Angular with DI

  27. 27

    Angular 4: Component disappears after injecting Service

  28. 28

    Angular2: Injecting a service

  29. 29

    Angular2: Injecting a service

HotTag

Archive