2
votes

I am "at my wit's end" and haven't a clue what to do. I am close to crying!

I am trying to teach myself Angular2 after many happy years of using AngularJS - I have an Angular2 app that has top level component like so called App

@Component({
    selector: 'app', // <app></app>
    providers: [...FORM_PROVIDERS],
    directives: [...ROUTER_DIRECTIVES],
    template: require('./app.html')
})

@RouteConfig([
    {path: '/', component: Home, name: 'Home'},
    {path: 'about', component: About, name: 'About'},
    {path: 'profile', component: Profile, name: 'Profile'},
    {path: 'profile/:id', component: ForeignProfile, name: 'ForeignProfile'}
])

export class App {
    userStatus: UserStatus;
    userOnlineContacts: UserContact[];

    constructor(private userData: UserData) {
        this.userStatus = new UserStatus();
    }

    ngOnInit() {
         Observable.forkJoin([
            this.userData.getUserStatus(),
            this.userData.getContacts()
         ]).subscribe( (t) => {
                this.userStatus = t[0];
                this.userOnlineContacts = t[1].onlineContacts;
         });

        // Polling thing for this.userOnlineContacts...
        this.userData.pollContacts().subscribe(
            (data) => {
                this.userOnlineContacts = data.onlineContacts;
            });
    }
}

I looks like this in my index.html

<app>Loading...</app>

Now I am using the ng-router so within my app.html I have my <router-outlet></router-outlet> tags nested into my HTML. So my child components get injected into these tags - I never place my selector tags into the HTML (so I assume I can't pass the data using data-binding. Inside the <router-outlet></router-outlet> I access the child compinent when I navigate to the ForeignProfile component, the child component code is like so...

@Component({
    selector: 'foreign-profile',
    template: require('app/components/foreign-profile/foreign-profile.html'),
    providers: [],
    directives: [],
    pipes: []
})

export class ForeignProfile implements OnInit {

    userProfile: any;
    routeId: string;
    userPhotos: any;
    userInfo: any;

    constructor(private userData: UserData, private routeParams: RouteParams) {
        this.routeId = routeParams.get('id');
        this.userProfile = {}; // not sure why I need to put this here otherwise we have 'undefined' error
    }

    ngOnInit() {
        // Use forkJoin as we don't need order in the calls
        Observable.forkJoin([
            this.userData.getUserPhotos(this.routeId),
            this.userData.getUserInfo(this.routeId),
            this.userData.getUserBaseData(this.routeId)])
            .subscribe(t => {
                this.userPhotos = t[0];
                this.userInfo = t[1];
                this.userProfile = t[2];
                // do more stuff here

        });
    }
}

Inside this component I wish to access the properties of the App component, e.g. this.userStatus & this.userOnlineContacts. I don't want to create a service to make repeat HTTP calls I want to inject the parent properties directly into the child. In AngularJS I could have used something like $scope.$parent.userStatus. Now I looked into Injecting the parent (App) into the child (ForeignProfile) by amending the component:

this is the ForeignProfile Component

import {App} from '../../app'

@Component({
    // blah blah...
})

export class ForeignProfile implements OnInit {

    // code removed for example, easier to read
    userStatus: any;

    constructor(private userData: UserData, private routeParams: RouteParams, @Inject app:App) {
        // code removed for example, easier to read
        this.userStatus = app.userStatus;
    }

And then with the parent...

import {Injectable, Component} from 'angular2/core';

@Injectable()
@Component({
    selector: 'app', // <app></app>

This sent my console crazy. Can someone explain how exactly I could access the properties from the App component in my ForeignProfile Component? I have wasted hours on this so far! Thanks in advance.

1
Just use a shared service to communicate between parent and child. There are tons of similar questions. - Günter Zöchbauer
@GünterZöchbauer, this is why $scope was so useful. This puts any consumer of the shared service responsible for the logic necessary to leverage its API. This doesn't lend itself to DRY programming. There should be a reasonable way for a parent component to provide data to any children components without knowing implementation details. (e.g. vm.someProperty.someMethod(someVar)) This ought to be supported, IMO. - bodine
If you use $scope you need some contract so the client accesses a value using a predefined name. This is what service classes are for, a common contract. I think this is the right way. But that is how it's done in Java-like languages. In JS-world this is uncommon but currently there is a strong shift back from dynamic languares to typed languages. - Günter Zöchbauer

1 Answers

1
votes

As correctly mentioned by @Günter Zöchbauer you must use angular services concept for purposes of sharing data between components.

In case if you still want to access your App component, you can do it in ForeignProfile constructor like this:

constructor(@Host() @Inject(forwardRef(() => App)) app: App)
{

}