Is there a possibility to load services before components? - javascript

I have a component SurveyComponent and a service SurveyService. The SurveyService loads some data form JSON.
I add the SurveyService into the constructor from SurveyComponent.
constructor(public surveyService: SurveyService) {}
In the constructor from SurveyService I load some data.
constructor(private storageService: StorageService) {
this.finishedSurveys = [];
this.loadCurrentSurvey();
this.loadFinishedSurveys();
}
I put also the SurveyService into the app.module:
.....providers: [SurveyService].....
But the component loads before service so I haven no data in my component. Why does it happen? Can I solve this issue?

you can use resolve for the angular router. E.g.
Make a class APIResolver like
import { Injectable } from '#angular/core';
import { APIService } from './api.service';
import { Resolve } from '#angular/router';
import { ActivatedRouteSnapshot } from '#angular/router';
#Injectable()
export class APIResolver implements Resolve<any> {
constructor(private apiService: APIService) {}
resolve(route: ActivatedRouteSnapshot) {
return this.apiService.getItems(route.params.date);
}
}
Then, resolve your corresponding router like
{
path: 'items',
component: ItemsComponent,
resolve: { items: APIResolver }
}

You can refer to this link to use OnInit
#Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnInit {
ngOnInit() {
you call the service that you want to execute
}
}

Related

How to restrict loading of lazy loaded module till resolving some data from api in angular?

I have a lazy loaded module. I want to load some data before the module load. I tried app_initializer but it didn't work because i have already an app_initializer in AppModule. Is there any way to do this?
You can use an Resolver that will handle your data before the module will load and navigate to your specific route as that loaded completly
resolver
import { Injectable } from '#angular/core';
import { APIService } from './api.service';
import { Resolve } from '#angular/router';
import { ActivatedRouteSnapshot } from '#angular/router';
#Injectable()
export class APIResolver implements Resolve<any> {
constructor(private apiService: APIService) {}
resolve(route: ActivatedRouteSnapshot) {
return this.apiService.getItems(route.params.date);
}
}
in your route
{
path: 'items/:date',
component: ItemsComponent,
resolve: { items: APIResolver }
}
and to acces the resolved data in the desired component
import { ActivatedRoute } from '#angular/router';
constructor(private route: ActivatedRoute) { }
items: any[];
ngOnInit() {
this.items= this.route.snapshot.data.items;
}
using a component with a resolver in a given module will not init that module until all the data is resolved
you can check also https://angular.io/api/router/Resolve

What's the correct way to load and open a component inside a modal dialog in Angular4 --

I have a component called NewCustomerComponent and I want to load and display it through a modal popup in another page/component when a button is clicked. I have written the relevant bit of code [or so it seems]. But I am getting the following error --
this._childInstance.dialogInit is not a function
at ModalDialogComponent.dialogInit (modal-dialog.component.js:65)
at ModalDialogService.openDialog (modal-dialog.service.js:26)
at OrderNewComponent.newCl (order-new.component.ts:85)
My code is pretty simple too, in the component where I am trying to open the modal popup.
I'll just post the relevant portions --
import { Component, Inject, ViewContainerRef, ComponentRef } from
'#angular/core';
import { Http, Headers } from '#angular/http';
import { Router } from '#angular/router';
import { Observable, Subject } from 'rxjs';
import 'rxjs/add/operator/map';
import { CustomerSearchService } from '../../../shared/services/customer-
search.service';
import { ICustomer, Customer, CustomerD } from
'../../../shared/models/customer';
import { ModalDialogModule, ModalDialogService, IModalDialog } from 'ngx-
modal-dialog';
import { NewCustomerComponent } from
'../../../components/popups/customer/customer-new.component';
#Component({
selector: 'order-new',
templateUrl: './order-new.component.html'
})
export class OrderNewComponent {
public reference: ComponentRef<IModalDialog>;
constructor(private cusService: CustomerSearchService, private http:
Http, private modalService: ModalDialogService, private viewRef:
ViewContainerRef) {
}
ngOnInit(): void {
}
** this is where I am trying to load the newcustomercomponent and open it
in the popup. not working.
newCl() {
this.newC = true;
this.exiC = false;
this.modalService.openDialog(this.viewRef, {
title: 'Add New Customer',
childComponent: NewCustomerComponent
});
}
}
** edits. NewCustomerComponent code added for reference.
import { Component, Input, Output, EventEmitter, OnInit,
ChangeDetectorRef, Directive, ElementRef, Renderer, AfterViewInit }
from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { NgFor } from '#angular/common';
import { Observable } from 'rxjs/Rx';
import { BehaviorSubject } from 'rxjs/Rx';
import { PlatformLocation } from '#angular/common';
import { Http } from '#angular/http';
import { ICustomer, Customer } from '../../../shared/models/customer';
import { UserService } from '../../../shared/services/UserService';
import { IModalDialog, IModalDialogOptions, IModalDialogButton } from
'ngx-modal-dialog';
#Component({
selector: 'new-customer',
templateUrl: './customer-new.component.html'
})
export class NewCustomerComponent implements IModalDialog {
model: Customer = new Customer();
errors: any;
submitResponse: any;
actionButtons: IModalDialogButton[];
constructor(private userService: UserService, private http: Http) {
this.actionButtons = [
{ text: 'Close', onAction: () => true }
];
}
ngOnInit() {
}
dialogInit(reference: ComponentRef<IModalDialog>, options:
Partial<IModalDialogOptions<any>>)
{
// no processing needed
}
createCustomer() {
this.userService.createCustomer(this.model)
.take(1)
.subscribe(
(response: any) => {
this.submitResponse = response;
if (response.success) {
console.log('New customer added!');
}
else {
console.log('Unable to add customer!');
}
},
(errors: any) => this.errors = errors
);
return false;
}
cancelClicked() {
}
}
What did I do wrong here? Has it got something to do with the element reference I added in terms of the viewRef? Which portion is erroneous? What about that child component? Does it require to have some specific configuration/markup/component for this to work? I am very new to angular; I am not sure whatever the reason is.
Kindly help me rectify this scenario.
Thanks in advance,
Can you please ensure that the NewCustomerComponent is implementing the IModalDialoginterface. Also, if this is not the case can you please share the code of NewCustomerComponent as well.
edits
Looks like you have not defined the dialogInit method in the NewCustomerComponent and it didn't pop up before as you have not implemented the interface IModalDialog. I would request you to define the dialogInit method in the component class as suggested on the link.

Storing and accessing a global flag/variable in each component, The value keeps changing

I am trying to make a global service class which will store few variables which will influence behaviour on HTML components base on flags.
My current only flag is a BehaviourSubject which navbar component subscribes to update a navbar with different buttons. The issue is when I refresh the page in a browser the flag reverse to the original value and forgets what has set before. The current scenario is when user log in the flag is being set to true and should stay true until a user logs out. It may not be a right way to do it so if there is a better way of approaching it; then I am happy to implement it.
Data sharing class:
import {
Injectable
} from '#angular/core';
import {
BehaviorSubject
} from 'rxjs';
#Injectable()
export class ServiceClassDatasharing {
public isUserLoggedIn: BehaviorSubject < boolean > = new BehaviorSubject < boolean > (false);
public setUserLoggedInStatus(status) {
this.isUserLoggedIn.next(status);
}
}
Nav Component:
import {
Component,
OnInit
} from '#angular/core';
import {
MatDialog,
MatDialogRef,
MAT_DIALOG_DATA
} from '#angular/material';
import {
Inject
} from '#angular/core';
import {
ServiceClassDatasharing
} from '../service/service-class-datasharing';
import {
ServiceClassAuth
} from '../service/service-class-auth';
import {
SigninComponent
} from './../signin/signin.component';
import {
Router
} from '#angular/router';
#Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css']
})
export class NavComponent implements OnInit {
id_token: Boolean;
username: String;
constructor(public dialog: MatDialog, private dataSharingService: ServiceClassDatasharing,
private authService: ServiceClassAuth, private router: Router) {
this.dataSharingService.isUserLoggedIn.subscribe(res => {
this.id_token = res;
if (this.id_token) {
const user = JSON.parse(localStorage.getItem('user'));
this.username = user['user'].user_username;
}
});
if (!this.id_token) {
router.navigate(['index']);
}
}
ngOnInit() {}
openDialog(): void {
let dialogRef = this.dialog.open(SigninComponent, {
width: '450px',
data: {}
});
}
public logout() {
this.authService.logout().subscribe(res => {
if (res['success']) {
localStorage.clear();
this.dataSharingService.setUserLoggedInStatus(false);
}
});
this.router.navigate(['index']);
}
}
Index Component as an example it should redirect a user to dashboard if the global flag is set to true.
import {
Component,
OnInit
} from '#angular/core';
import {
ServiceClassDatasharing
} from '../service/service-class-datasharing';
import {
Router
} from '#angular/router';
#Component({
selector: 'app-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.css']
})
export class IndexComponent implements OnInit {
constructor(private dataSharingService: ServiceClassDatasharing, private router: Router) {
if (this.dataSharingService.isUserLoggedIn.value) {
this.router.navigate(['dashboard']);
}
}
ngOnInit() {}
}
Try using localStorage variable to achieve the same .
Create a function in service class which will set the variable with token if user logs in and function to get the same token.
const key = 'abcde'
setUseLoggedIn(token){
localStorage.setItem(this.key,token);
}
getUserLoggedIn(){
return localStorage.getItem(this.key);
}
set token as null if user is not logged in and check for the same when retrieving the token.

Angular 4 - (onclick) pass parameter to a service

I am using Angular 4 and I was wondering how to pass a parameter value to a Service.
For example:
<button (onClick)="doSomething('myParameter')">Send this to Service</button>
Then the service would get it.
I currently have this:
import { Injectable } from '#angular/core';
#Injectable()
export class MessageService {
constructor() { }
message() {
return 'This data goes to the component';
}
}
and then get is like this:
export class AppComponent implements OnInit {
constructor(private messageService: MessageService) {}
ngOnInit() {
console.log(this.messageService.message);
}
}
but this only sends data to the component.
How do I do this?
Your template should talk to your component class and your component class should talk to your service.
I see you have a doSomething method in your template that is not defined in your component?
You need something like this:
Component 1
export class AppComponent implements OnInit {
constructor(private messageService: MessageService) {}
ngOnInit() {
}
doSomething(message: string): void {
this.messageService.message = message;
}
}
Service
import { Injectable } from '#angular/core';
#Injectable()
export class MessageService {
message: string;
constructor() { }
}
Component 2
export class AppComponent implements OnInit {
get message(): string {
return this.messageService.message;
}
constructor(private messageService: MessageService) {}
ngOnInit() {
}
}

Angular Server-Side Rendering with Route Resolve

I am attempting to use Server-Side Rendering in Angular (v4) to allow for better SEO.
Things work as expected until I add resolve on my route. Adding resolve causes HTML title to retain it's initial value when viewing source.
My Module:
import {
Injectable,
ModuleWithProviders,
NgModule
} from '#angular/core';
import {
ActivatedRouteSnapshot,
Resolve,
Router,
RouterModule,
RouterStateSnapshot
} from '#angular/router';
import {
Observable
} from 'rxjs/Rx';
import {
ArticleComponent
} from './article.component';
import {
Article,
ArticlesService,
UserService,
SharedModule
} from '../shared';
#Injectable()
export class ArticleResolver implements Resolve < Article > {
constructor(
private articlesService: ArticlesService,
private router: Router,
private userService: UserService
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): any {
return this.articlesService.get(route.params['slug'])
.catch((err) => this.router.navigateByUrl('/'));
}
}
const articleRouting: ModuleWithProviders = RouterModule.forChild([{
path: 'article/:slug',
component: ArticleComponent,
resolve: {
article: ArticleResolver
},
data: {
preload: true
}
}]);
#NgModule({
imports: [
articleRouting,
SharedModule
],
declarations: [
ArticleComponent
],
providers: [
ArticleResolver
]
}) export class ArticleModule {}
My Component:
import {
Component,
OnInit
} from '#angular/core';
import {
ActivatedRoute,
Router,
} from '#angular/router';
import {
Title,
Meta
} from '#angular/platform-browser';
import {
AppComponent
} from '../app.component';
import {
Article,
} from '../shared';
#Component({
selector: 'article-page',
templateUrl: './article.component.html'
})
export class ArticleComponent implements OnInit {
article: Article;
constructor(
private route: ActivatedRoute,
private meta: Meta,
private title: Title
) {}
ngOnInit() {
this.route.data.subscribe(
(data: {
article: Article
}) => {
this.article = data.article;
}
);
this.title.setTitle(this.article.title);
}
}
I am new to Angular SSR so any guidance is greatly appreciated.
Instead of subscribing to route data, retrieve your results from the snapshot like this:
this.route.snapshot.data['article']
You also might need to register ArticlesService in your providers for the module.
As a side note, this import:
import {
Observable
} from 'rxjs/Rx';
is an RxJS antipattern. Please use the following import instead:
import {Observable} from 'rxjs/Observable';
I found that my primary service was referencing a secondary service that was attempting to return an authentication token from window.localStorage.
Attempting to access the client storage caused Angular SSR to omit the generation of source code for my component.
Thanks #Adam_P for helping me walk through it!

Categories

Resources