4

Is there a way to wait before the service initializes to load other components? I need to load some data synchronously before displaying the components (which require the data), and I don't want to use promises/observables (using those for async calls)

ngIf doesn't seem to cut it (component loads even though if doesn't match).

I'm not using router (don't need it for the app), so I don't think @CanActivate is suitable in this case.

Any ideas?

@Component({
    selector: 'my-app',
    directives: [MyDirective],
    template: `
    <div>
        <div *ngIf="!initialized">Loading...</div>
        <div *ngIf="initialized"><myDirective>should wait for service to be initialized</myDirective></div>
    </div>`
})
export class AppComponent {
    initialized = false;

    constructor(_myService: MyService) {
        this._myService = _myService;
    }

    ngOnInit() {
        this._myService.init()
            .then(() => {
                setTimeout( () => this.initialized = true, 2000);
            }
        );
    }
}
2
  • Isn't it the purpose of resolvers ? Commented Jun 11, 2018 at 20:39
  • I use resolvers, yet the component is initialized before the resolver is even called Commented Aug 5, 2019 at 5:54

3 Answers 3

2

I am not sure if I understood the question correctly, but from what I got, I think you can create a model instance with initial values (place holders), and allow your component to initialise with this model, and then, when your data is ready, change the model instance values, which will reflect to your component.

This way, your component doesn't need to wait, it just uses place holder data, which you can of course test for, inside the component and display your template accordingly, and when the data is ready from the parent, updating it, will update the child.

I hope this helped.

Sign up to request clarification or add additional context in comments.

1 Comment

well this is pointless, why displaying something generic while updating the parent would be enough, the question is how do you update the parent ? with a settimeout that checks the service to be initialized
1

What version of Angular are you with? Not sure if you're copy-pasting the redacted code, but it seems as if you're missing the implements keyword there in your Class.

*ngIf works good in this plunker.

From what I gather, something like *ngIf is the proper way to do things in Ng2. Basically, only show the component if the conditions are good.

You might be running into a snag because your component gets instantiated before you expect it - because you require it in your parent component.

That might be because your component itself (or the template) expects some values, but they're not there (so your constructor breaks down).

According to Lifecycle Hooks page on angular.io, that's exactly what OnInit interface is for.


Here's the code from the plunker directly (yours would be the SubComponent):

import {Component, OnInit} from 'angular2/core'

@Component({
  selector: 'sub-component',
  template: '<p>Subcomponent is alive!</p>'
})
class SubComponent {}


@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div>
      <h2>Hello {{name}}</h2>

      <div *ngIf="initialized">
        Initialized
        <sub-component>Sub</sub-component>
      </div>
      <div *ngIf="!initialized">Not initialized</div>
    </div>
  `,
  directives: [SubComponent]
})
export class App implements OnInit {
  initialized = false;
  constructor() {
    this.name = 'Angular2'

  }
  ngOnInit() {

    setTimeout(() => {
      this.initialized = true;
    }, 2000)
  }
}

7 Comments

I agree, NgIf should work. Just FYI, implements is not required. It is recommended (your TypeScript editor will catch syntax errors with it), but it is not required.
This is not necessarily helpful. There is a case I'm working on right now (which led me here) where I want to load some data over HTTP before my service is ready. I have a default value in firebase, and if my service does not get an id, it needs to copy the default value to a new object w/ an id. The problem is if I just inject it all immediately, the default value from firebase is not necessarily loaded.
@Seiyria than that's a different question, you're looking for some sort of lazy model completition. Probably not related to the original question though.
"Is there a way to wait before the service initializes to load other components?" - seems like the same problem. OP is doing it synchronously and I'm doing it async, I think that's the only difference.
Sync vs. async is all the difference. That's what I've meant by adding lazy. Do you have a specific problem? Link to a question?
|
0

What I usually do is create an EventEmitter in my data service, and then allow each component to listen for the dataLoaded event before doing anything. It may not be the most efficient and "textbook" way to go about this problem, but works well. For example, in app.component.ts (my most parent component), I load data in the ngOnInit hook. First, let's look at our data service:

data.service.ts

@Injectable()
export class DataService {

dataLoaded = new EventEmitter<any>();
prop1: string;
prop2: string;

constructor(private http: HttpClient) {}

  // Asynchronously returns some initialization data
  loadAllData () {
    return this.http.get<string>('/api/some-path');
  }

}

app.component.ts

export class AppComponent {

  constructor (private dataService: DataService) {
  }

  ngOnInit() {
    this.dataService.loadAllData().subscribe((data) => {
      // Maybe you want to set a few props in the data service
      this.dataService.prop1 = data.prop1;
      this.dataService.prop2 = data.prop2;

      // Now, emit the event that the data has been loaded
      this.dataService.dataLoaded.emit();
    });
  }
}

Now that we have the DataService loading and emitting a "loaded" event in the main app component, we can subscribe to this event in child components:

@Component({
  selector: 'child-component',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {

   constructor(private dataService: DataService) { 
      this.dataService.dataLoaded.subscribe(() => {
        // Once here, we know data has been loaded and we can do things dependent on that data
        this.methodThatRequiresData();
      });
   }

   methodThatRequiresData () {
      console.log(this.dataService.prop1);
   }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.