Reputation: 573
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);
}
);
}
}
Upvotes: 4
Views: 10334
Reputation: 2393
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);
}
}
Upvotes: 0
Reputation: 19578
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)
}
}
Upvotes: 1
Reputation: 1340
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.
Upvotes: 2