Webpage can not refer to a static value from a provider


#1

Hello,

I was trying to refer to a static value in a provider from a web page (html file)

I first defined some static properties in a provider . please see below code snippets , I excerpt it for clearance

import { Injectable } from '@angular/core';
import {Post} from "fbsj";

@Injectable()
export class TransSender {

  public static type:number = Post.Sender;
  public static count:number = 0;
  public static pushed:number = 0;
  ... ... 

  constructor(){};

  public static setter(count:number, pushed:number, read:number, saved:number, rated:number, followed:number, blocked:number, forwarded:number) {
    TransSender.count = count;
    TransSender.pushed = pushed;
    ... ...
  }
}

then I enlisted the provider in ionicBootstrap:

import {Component, ViewChild} from '@angular/core';
import {ionicBootstrap, Platform, MenuController, Nav} from 'ionic-angular';
... ...
import {TransSender} from './providers/trans-sender/trans-sender';
@Component({
  templateUrl: 'build/app.html',
})
export class MyApp {
  @ViewChild(Nav) nav: Nav;

  // make HelloIonicPage the root (or first) page
  rootPage: any = HelloIonicPage;
  pages: Array<{title: string, component: any}>;

  constructor(
    private platform: Platform,
    private menu: MenuController
  ) {
    this.initializeApp();

    // set our app's pages
    this.pages = [
      { title: 'My First List', component: ListPage },
      { title: 'Anonymous', component: SenderPage },
      { title: 'Main Page', component: HelloIonicPage }
    ];
  }

  initializeApp() {
    this.platform.ready().then(() => {
      StatusBar.styleDefault();
      ... ...
    });
  }

  openPage(page) {
    this.menu.close();
    this.nav.setRoot(page.component);
  }
}

ionicBootstrap(MyApp,[User,TransSender]);

then I injected it into the client class in “general.ts”

import {Component} from '@angular/core';
import {NavController, NavParams} from 'ionic-angular';
... ...
import {TransSender} from "../../providers/trans-sender/trans-sender";

@Component({
  templateUrl: 'build/pages/general/general.html',
})
export class GeneralPage {
  selectedItem: any;

  private type:number;
  private count:number;
  private pushed:number;
 ... ...
  constructor(private nav: NavController, navParams: NavParams, sender: TransSender) {
   
    this.selectedItem = navParams.get('item');
  ... ...
   this.pushed = TransSender.pushed;
  }
 ... ...          
}

then I referred to the static value in the corresponding html file (general.html) as the following

<ion-content>
 <div *ngIf="selectedItem" class="selection">
    <b>{{selectedItem.title}}</b>
    <ion-icon name="{{selectedItem.icon}}"></ion-icon>
  
     <div>
         count <b>{{TransSender.count}}</b>
     </div>
     <div>
         pushed <b>{{pushed}}</b>
     </div>

  </div>
</ion-content>

when I run “ionic serve -s -l -c”, I got the following errors

... ...
  erve  /build/pages/general/general.html
3     364765   group    EXCEPTION: Error: Uncaught (in promise): EXCEPTION: Error in build/pages/general/general.html:18:18
ORIGINAL EXCEPTION: TypeError: Cannot read property 'count' of undefined
ORIGINAL STACKTRACE:
TypeError: Cannot read property 'count' of undefined
    at DebugAppView._View_GeneralPage2.detectChangesInternal (GeneralPage.template.js:429:73)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
    at DebugAppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14937:45)
    at DebugAppView.AppView.detectContentChildrenChanges (http://localhost:8100/build/js/app.bundle.js:14850:20)
    at DebugAppView._View_GeneralPage0.detectChangesInternal (GeneralPage.template.js:155:8)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
    at DebugAppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14937:45)
    at DebugAppView.AppView.detectViewChildrenChanges (http://localhost:8100/build/js/app.bundle.js:14858:20)
    at DebugAppView.AppView.detectChangesInternal (http://localhost:8100/build/js/app.bundle.js:14843:15)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
ERROR CONTEXT:
[object Object]
4     364766   error    EXCEPTION: Error: Uncaught (in promise): EXCEPTION: Error in build/pages/general/general.html:18:18
ORIGINAL EXCEPTION: TypeError: Cannot read property 'count' of undefined
ORIGINAL STACKTRACE:
TypeError: Cannot read property 'count' of undefined
    at DebugAppView._View_GeneralPage2.detectChangesInternal (GeneralPage.template.js:429:73)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
    at DebugAppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14937:45)
    at DebugAppView.AppView.detectContentChildrenChanges (http://localhost:8100/build/js/app.bundle.js:14850:20)
    at DebugAppView._View_GeneralPage0.detectChangesInternal (GeneralPage.template.js:155:8)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
    at DebugAppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14937:45)
    at DebugAppView.AppView.detectViewChildrenChanges (http://localhost:8100/build/js/app.bundle.js:14858:20)
    at DebugAppView.AppView.detectChangesInternal (http://localhost:8100/build/js/app.bundle.js:14843:15)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
ERROR CONTEXT:
[object Object]
5     364767   error    STACKTRACE:
6     364767   error    Error: Uncaught (in promise): EXCEPTION: Error in build/pages/general/general.html:18:18
ORIGINAL EXCEPTION: TypeError: Cannot read property 'count' of undefined
ORIGINAL STACKTRACE:
TypeError: Cannot read property 'count' of undefined
    at DebugAppView._View_GeneralPage2.detectChangesInternal (GeneralPage.template.js:429:73)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
    at DebugAppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14937:45)
    at DebugAppView.AppView.detectContentChildrenChanges (http://localhost:8100/build/js/app.bundle.js:14850:20)
    at DebugAppView._View_GeneralPage0.detectChangesInternal (GeneralPage.template.js:155:8)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
    at DebugAppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14937:45)
    at DebugAppView.AppView.detectViewChildrenChanges (http://localhost:8100/build/js/app.bundle.js:14858:20)
    at DebugAppView.AppView.detectChangesInternal (http://localhost:8100/build/js/app.bundle.js:14843:15)
    at DebugAppView.AppView.detectChanges (http://localhost:8100/build/js/app.bundle.js:14832:15)
ERROR CONTEXT:
[object Object]
    at resolvePromise (http://localhost:8100/build/js/zone.js:538:32)
    at http://localhost:8100/build/js/zone.js:574:18
    at ZoneDelegate.invokeTask (http://localhost:8100/build/js/zone.js:356:38)
    at Object.onInvokeTask (http://localhost:8100/build/js/app.bundle.js:12336:42)
    at ZoneDelegate.invokeTask (http://localhost:8100/build/js/zone.js:355:43)
    at Zone.runTask (http://localhost:8100/build/js/zone.js:256:48)
    at drainMicroTaskQueue (http://localhost:8100/build/js/zone.js:474:36)
    at XMLHttpRequest.ZoneTask.ZoneTask.cancelFn.invoke (http://localhost:8100/build/js/zone.js:426:22)
7     364768   groupEnd 

the only way I can do get over is to define a local property in the client class ( general.ts), and assign the value in the in the constructor. and refer to the local property in the html file
you may see the different handling of “count” and “pushed” in the above code spinets in “general.ts” and “general.html”

please advise
thanks


#2

You need to set up a getter, for in Angular 2 template, globals are inaccessible.

get count() { return TransSender.count; }
Count <b>{{count}}</b>


#3

Another option would be to expose the TransSender in GeneralPage:

constructor(... public sender: TransSender)
count <b>{{sender.count}}</b>

#4

Hello, itlr, and rapropos:

Thanks for suggestions.

I guess it might be better to answer in a separate section, because the answer is relattive to both suggestions, and I do not want to end up answering twice :slight_smile:

I tried both solutions, and had some funny observations:

It seems that : the variables are “incorporated” into the scope of a client after the injection of a provider, just like any variables you defined locally within the client.

so the key point is: you do not need a prefix to a variable after the injection. nothing to do with “getter” or injection method (could be either ionicBootstrap or constructor injection)

with that said, I myself tend to injection by constructor, because affected scope is just a client, and you reduce the possibility of name conflicts, in the meantime, the static variable ensure the globalization of the value

when I say “client”, I mean the acceptor or user of the provider.

cheer


#5

No, I don’t think this is true.

class Service {
  afo:string = "afo";
}
class Page {
  constructor(public service:Service) {
  }
}
{{service.afo}} works
{{afo}} does not

#6

Hello, rapropos,

I have tried again, and you are right.
in my previous testings, I just saw that the errors disappeared, assuming that everything was fine; yet later I found myself can not display the values of the variables on the display…

and the solution turned out to be the combination of the two suggestions,

first define instance “getters” for static variables, then injected in the constructor with “public” decoration. finally referred with instance reference prefix…

see the code snippets below:

the provider

import { Injectable } from '@angular/core';

@Injectable()
export class TransSender {
... ...
  private static _count:number = 0;
  private static _pushed:number = 0;
... ...
  public get count(){
    return TransSender._count;
  }

  public get pushed(){
    return TransSender._pushed;
  }
... ...

the client

import {Component} from '@angular/core';
import {NavController, NavParams} from 'ionic-angular';
import {TransSender} from "../../providers/trans-sender/trans-sender";

@Component({
  templateUrl: 'build/pages/general/general.html',
  providers:[TransSender]
})
export class GeneralPage {
  selectedItem: any;
  
  constructor(private nav: NavController, navParams: NavParams, public sender: TransSender) {
   this.selectedItem = navParams.get('item');
  }

the reference in the html file

 <ion-row>
                <ion-col ><h2>count: {{sender.count}}</h2></ion-col>
            </ion-row>
            <ion-row>
                <ion-col width-33><h2>pushed: {{sender.count}}</h2></ion-col>
            </ion-row>
            <ion-row>
... ...

I personally believe this approach is superior than ionicBootstrap(); because the injection now happens only with an individual page(a local scope), rather than the overall application, thus reduces the possibility of name conflict. yet the static variables ensure the globalization of the value across the application …

cheers


#7

Whatever works for you, I guess, but I disagree. It smells like you’re sort of rolling your own parallel DI, and it would definitely not be clear to me just looking at the code why you wouldn’t just use the existing DI infrastructure to ensure TransSender is a singleton across the entire app.