Nativescript-angular: how to set 'Page' level properties in angular component

Created on 21 Jul 2016  路  12Comments  路  Source: NativeScript/nativescript-angular

Hi! i'm using angular2-seed-advanced, web + mobile with angular+nativescript, i'm trying to set my background sitting under statusbar using opaqueTokens, but somehow it don't work. My code:
Token.ts

import {OpaqueToken} from '@angular/core';
export const PAGE: OpaqueToken = new OpaqueToken('page');

App.ts

nativeScriptBootstrap(NSAppComponent, [
    provide(WindowService, {useClass: WindowNative}),
    provide(PAGE, {useClass: Page}),
    ModalNative,
    provide(HttpService, {useClass: NSHttpService}),
    provide(TranslateLoader, {
        useFactory: () => {
            return new TNSTranslateLoader('assets/i18n');
        }
    }),
    NS_APP_PROVIDERS,
    nsProvideRouter(routes, {enableTracing: false})
]);

myComponent.ts

constructor(@Inject (PAGE) private page:PAGE) {
        if (CoreConfigService.IS_MOBILE_NATIVE) {
            console.log(this.page.ios);
            this.page.actionBarHidden = true;
            this.page.backgroundSpanUnderStatusBar=true;
            this.page.backgroundImage = this.page.ios ? "res://bg_login.jpg" : "res://bg_login";
        }
    }

Most helpful comment

Just to conclude, turns out his issue was related to fact that when using an OpaqueToken to build a web/{N} shared codebase, one must provide the PAGE OpaqueToken in the correct way, for instance this works:

// somewhere in web codebase
import {OpaqueToken} from '@angular/core';
export const PAGE: any = new OpaqueToken('page');
...

// then only in the {N} app
nativeScriptBootstrap(AppComponent, [
    provide(PAGE, {
        useFactory: () => {
            const frame = topmost();
            if (frame) {
                return frame.currentPage;
            } else {
                return null;
            }
        }
    })
]);

... then in a component which is used on the web (as well as in {N} app), one can do this:

@Component({
    selector: 'any-cmp',
    templateUrl: 'any.component.html'
})
export class AnyComponent {

    constructor(@Inject(PAGE) private page:any) {
        if (Config.IS_MOBILE_NATIVE()) {
            this.page.actionBarHidden = true;
            this.page.backgroundSpanUnderStatusBar = true;
            this.page.backgroundImage = 'res://bg_login';
        }
    }

Works wonderfully and ensures that it doesn't break the web 馃憤

All 12 comments

@vakrilov I had been chatting with @BenevidesLecontes but I am also unsure of how best to modify Page level properties in an angular component? The OpaqueToken setup is just to allow it to work on web and {N}. so don't be confused by all that above ^. Bottom line: how best to modify Page props within angular components.

I'm not sure if I understand the issue correctly so - correct me if, I'm wrong.

I'm assuming the problem is the code sharing between web and mobile - no Page in web projects.
In this case I would suggest extracting the mobile-specific code in a directive that is only used in the {N} part. You can inject Page directly there as the code will be executed only in {N} context. You won't need the token and you will have typing support for the Page type.

Note: You can inject Page in any component/directive and it will be the page the component/directive will be displayed on.

@vakrilov yes you're right, i'm using <page-router-outlet></page-router-outlet> to load my components, does the same thought apply to that case?

After read your suggestion, I tried to use router-outlet to use Page inside my component, it keep throwing error, Cannot find primary outlet to load 'LoginComponent'
PS: I have ROUTER_DIRECTIVES in my components metadata.

For the actual markup - you will probably have different files for web/mobile as the markup will be different. That said - the error you are getting is not expected - can you share some repo code?

@vakrilov I think the actual issue has gotten clouded by some other things mentioned here. The real question is why this doesn't work:

import {Page} from 'ui/page';
...
constructor(private page:Page) {
            this.page.actionBarHidden = true;
            this.page.backgroundSpanUnderStatusBar=true;
            this.page.backgroundImage = this.page.ios ? "res://bg_login.jpg" : "res://bg_login";
    }

changing props on injected Page appear to have no effect?

Indeed I missed that - will investigate.
Does this happen:

  1. On the first page or after navigation
  2. Android or ios

On both. iOS at least, haven't checked Android.

I was unable to reproduce the behavior. Here is how I tested - the background is changed here as expected.

Can you share some code?

Thank you @vakrilov for taking the time to look. Let's close this and I'll look at this again with him.

Just to conclude, turns out his issue was related to fact that when using an OpaqueToken to build a web/{N} shared codebase, one must provide the PAGE OpaqueToken in the correct way, for instance this works:

// somewhere in web codebase
import {OpaqueToken} from '@angular/core';
export const PAGE: any = new OpaqueToken('page');
...

// then only in the {N} app
nativeScriptBootstrap(AppComponent, [
    provide(PAGE, {
        useFactory: () => {
            const frame = topmost();
            if (frame) {
                return frame.currentPage;
            } else {
                return null;
            }
        }
    })
]);

... then in a component which is used on the web (as well as in {N} app), one can do this:

@Component({
    selector: 'any-cmp',
    templateUrl: 'any.component.html'
})
export class AnyComponent {

    constructor(@Inject(PAGE) private page:any) {
        if (Config.IS_MOBILE_NATIVE()) {
            this.page.actionBarHidden = true;
            this.page.backgroundSpanUnderStatusBar = true;
            this.page.backgroundImage = 'res://bg_login';
        }
    }

Works wonderfully and ensures that it doesn't break the web 馃憤

That save me days!

@NathanWalker
This method looks good but I haven't been able to get this to work. Is provide a class I should import or create? Or has the standard changed?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NickIliev picture NickIliev  路  39Comments

zhaoyouliang picture zhaoyouliang  路  88Comments

AyWa picture AyWa  路  33Comments

ignaciofuentes picture ignaciofuentes  路  31Comments

jlooper picture jlooper  路  57Comments