NativeScript / nativescript-angular

Integrating NativeScript with Angular
http://docs.nativescript.org/angular/tutorial/ng-chapter-0
Apache License 2.0
1.21k stars 241 forks source link

how to set 'Page' level properties in angular component #362

Closed BenevidesLecontes closed 8 years ago

BenevidesLecontes commented 8 years ago

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";
        }
    }
NathanWalker commented 8 years ago

@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.

vakrilov commented 8 years ago

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.

BenevidesLecontes commented 8 years ago

@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.

vakrilov commented 8 years ago

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?

NathanWalker commented 8 years ago

@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?

vakrilov commented 8 years ago

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

  1. On the first page or after navigation
  2. Android or ios
NathanWalker commented 8 years ago

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

vakrilov commented 8 years ago

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

Can you share some code?

NathanWalker commented 8 years ago

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

NathanWalker commented 8 years ago

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 👍

BenevidesLecontes commented 8 years ago

That save me days!

armackey commented 5 years ago

@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?