I am trying to use angular routing to route to a new url when a button is clicked. I also need to use the module HttpClient to call some calls to the backend. However, whenever I create a HttpClient object, the routing doesn't work and it routes to a blank page with no url extension. When I delete the object, the routing works again. Anyone know how to overcome this? Here are some of my code snippets.
agent-page-component.ts (I create a Agent Service in the constructor)
import { Router} from "#angular/router";
import { Agent } from '../../models/agent.model'
import { AgentService } from '../../services/agent.service';
import { Subject, Subscription } from 'rxjs';
#Component({
selector: 'app-agent-page',
templateUrl: './agent-page.component.html',
styleUrls: ['./agent-page.component.css']
})
export class AgentPageComponent implements OnInit {
agents: Agent[] = [];
private agentSub: Subscription;
constructor(private agentService: AgentService){}
ngOnInit() {
this.agentService.getAgents();
this.agentSub = this.agentService.getAgentUpdateListener().subscribe((agents: Agent[]) => {
this.agents = agents;
});
}
ngOnDestroy() {
this.agentSub.unsubscribe();
}
}
agent.service.ts (this is where I import an HttpClient)
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
import { HttpClient, HttpClientModule } from '#angular/common/http';
#Injectable({providedIn: 'root'})
export class AgentService {
private agents: Agent[] = [];
private agentsUpdated = new Subject<Agent[]>();
constructor(private http: HttpClient){}
getAgentUpdateListener() {
return this.agentsUpdated.asObservable();
}
getAgents(){
this.http.get<{message: string, agents: Agent[]}>('http://localhost:3000/agents/Breach').subscribe((agentList) => {
this.agents = agentList.agents;
})
}
}
app-routing.module.ts
import { Routes, RouterModule } from '#angular/router';
import { AgentPageComponent } from './components/agent-page/agent-page.component';
import { HomePageComponent } from './components/home-page/home-page.component';
const routes: Routes = [
{path: 'agents', component: AgentPageComponent},
{path: '', component: HomePageComponent}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Related
I am working on an e-commerce app who's front-end is made in Angular 13.
I use a service to handle the products coming from an API. I run into a problem while ring to display the product details.
See Stackblitz demo HERE.
In app\services\product.service.ts I have:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
import { Product, ProductResponse } from '../models/product';
#Injectable({
providedIn: 'root'
})
export class ProductService {
products: Product[] = [];
apiURL: string = 'https://dummyjson.com';
constructor(private http: HttpClient) {}
// Product List
public getProducts(): Observable<ProductResponse>{
return this.http.get<ProductResponse>(`${this.apiURL}/products`);
}
// Product Details (single product)
public getProductDetails(id: Number): Observable<ProductResponse>{
return this.http.get<ProductResponse>(`${this.apiURL}/products/${id}`);
}
}
In app\app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { CommonModule } from '#angular/common';
import { HttpClientModule } from '#angular/common/http';
import { AppComponent } from './app.component';
import { Routes, RouterModule } from '#angular/router';
import { NavbarComponent } from './components/navbar/navbar.component';
import { FooterComponent } from './components/footer/footer.component';
import { SidebarComponent } from './components/sidebar/sidebar.component';
import { HomeComponent } from './components/home/home.component';
import { ProductItemComponent } from './components/product-item/product-item.component';
import { ProductsListComponent } from './components/products-list/products-list.component';
import { ProductsDetailsComponent } from './components/products-details/products-details.component';
const routes: Routes = [
{ path: '', component: HomeComponent},
{ path: 'products', component: ProductsListComponent},
{ path: 'products/show/:id', component: ProductsDetailsComponent},
];
#NgModule({
declarations: [
AppComponent,
NavbarComponent,
FooterComponent,
SidebarComponent,
ProductsListComponent,
ProductItemComponent ,
ProductsDetailsComponent
],
imports: [
CommonModule,
BrowserModule,
HttpClientModule,
RouterModule.forRoot(routes),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
In app\models\product.ts:
export class Product {
id?: number;
title?: string;
description?: string;
price?: number;
discountPercentage?: number;
rating?: number;
stock?: number;
brand?: string;
category?: string;
thumbnail?: string;
}
export interface ProductResponse {
products: Product[];
total: number;
skip: number;
limit: number;
}
In app\components\products-details\products-details.component.ts I have:
import { Component, OnInit, InputDecorator, Input } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { Product, ProductResponse } from '../../models/product';
import { ProductService } from '../../services/product.service';
#Component({
selector: 'app-products-details',
templateUrl: './products-details.component.html',
styleUrls: ['./products-details.component.css']
})
export class ProductsDetailsComponent implements OnInit {
#Input() product!: Product;
productResponse: any;
constructor(private ProductService: ProductService, private Router: Router, private ActivatedRoute:ActivatedRoute) { }
ngOnInit(): void {
const id = Number(this.ActivatedRoute.snapshot.paramMap.get('id'));
this.ProductService.getProductDetails(id).subscribe((response) => (this.productResponse = response));
}
}
In app\components\products-details\products-details.component.html I have:
<h1>{{ product.title }}</h1>
The problem
When I access a product details route (for instance, http://localhost:4200/products/show/1), the page displays an empty <h1> tag and the Chrome console shows Cannot read properties of undefined (reading 'title').
Where is my mistake?
Unlike the https://dummyjson.com/products endpoint, the https://dummyjson.com/products/{id} returns a plain Product object, so:
// product.service.ts
public getProductDetails(id: Number): Observable<Product>{
return this.http.get<Product>(`${this.apiURL}/products/${id}`);
}
// products-details.component.ts
// the #Input decorator is wrong — the data is not passed to the component from outside
// but instead fetched inside of the component
#Input() product!: Product;
// productResponse: any — this field is unused and should be removed
ngOnInit(): void {
...
this.ProductService.getProductDetails(id).subscribe((product) => (this.product = product));
}
The #Input() property only works for external assignment of values.
The following will probably do what you want:
product: Product | undefined;
this.ProductService.getProductDetails(id).subscribe((response) => (this.product = response));
<h1>{{ product?.title }}</h1>
Furthermore I believe handling raw responses should happen in the service instead of the component, but that may be a matter of personal preference.
I am looking for ways of redirecting a page to the maintenance page in angular but i am new and am research different methods for turning on maintenance mode
i found a possible solution here: # the approved answer
Angular JS redirect to page within module config
however i don't know how to implement it
if there someone who could explain it, i would appreciate it greatly
using an authGuard will solve this problem
auth-guard.service.ts file:
import { Injectable } from '#angular/core';
import { CanActivate, Router, RouterStateSnapshot, ActivatedRouteSnapshot } from '#angular/router';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs';
#Injectable()
export class AuthGuardMaintenance implements CanActivate {
constructor(
private authService: AuthService, private router: Router
) {}
canActivate(): Observable<boolean> | Promise<boolean> | boolean {
if (this.authService.inMaintenance()) {
alert('This Site Is Still Under Maintenance')
this.router.navigate(['/maintenance']);
return false;
} else {
this.router.navigate(['/']);
return true;
}
}
}
auth.service file:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class AuthService {
constructor() { }
inMaintenance() {
return false;
}
}
then import it in the app.module.ts file and add it to providers
then import the auth guard to the app-routing.module.ts file add the property
canActivate: [AuthGuardMaintenance]
to the the root route
eg
export const routes: Routes = [
{ path: '', component: MainComponent, canActivate: [AuthGuardMaintenance] },
{ path: 'maintenance', component: MaintenanceViewComponent },
{ path: '**', component: PageNotFoundComponent },
];
This is my Angular App. My app will get data from API (temporarily in JSON file) and show in many another sibling component. So I decide to create a category.service.ts that I get and store data in. I using APP_INITIALIZER to run this service first when my app started. But there is a problem that: This service is running first, AppComponent runs before service get data done. So my view have empty of data.
If I click button routing to this component, everything run perfect. But when I go to this component by url path or F5(refresh page), nothing is shown
category.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class CategoryService {
DATA_CATEGORIES = 'assets/categories.json';
private _categories = [];
constructor(private http: HttpClient) {
}
get categories() {
return this._categories;
}
Init(): Promise<any> {
return new Promise<void>(resolve => {
this.http.get(this.DATA_CATEGORIES).subscribe(data => {
this._categories = Array.from(Object.keys(data), k => data[k]);
console.log("load data...");
});
resolve();
});
}
}
app.module.ts
export function initializeCategoryService(catService: CategoryService) {
return (): Promise<any> => {
return catService.Init();
}
}
#NgModule({
declarations: [
AppComponent,
HomeComponent,
StoriesFilterPipe,
ViewStoryComponent,
ViewCatComponent,
FrontEndComponent,
SearchComponent,
BackEndComponent,
CrudStoryFormComponent,
CrudStoryComponent,
JwPaginationComponent,
CrudCatComponent,
CrudCatFormComponent,
CrudCatSearchResultComponent,
CatListComponent
],
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
HttpClientModule,
],
providers: [
StoryService,
CategoryService,
{
provide: APP_INITIALIZER, useFactory: initializeCategoryService, deps: [CategoryService], multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
What I will suggest is to use Observable
like in your category service
import { Observable, Subject } from 'rxjs';
export class CategoryService {
private loadDataSub = new Subject<any>();
loadDataObservable$ = this.loadDataSub.asObservable();
emitLoadDataSuccess() {
this.loadDataSub.next();
}
Init(): Promise<any> {
return new Promise<void>(resolve => {
this.http.get(this.DATA_CATEGORIES).subscribe(data => {
this._categories = Array.from(Object.keys(data), k => data[k]);
console.log("load data...");
this.emitLoadDataSuccess(); // here we are emitting event
});
resolve();
});
}
}
And In your component
export class AppComponent implements OnInit {
constructor(private categoryService: CategoryService) {
this.categoryService.loadDataObservable$.subscribe(() => {
// here you can get data, this will only trigger when data is loaded from API
});
}
}
This is common case - i.e. you shows page while data is not avaliable yet - at slow and bad connections for instance, and it can do even more - connection was broken and data was nto recieved.
So, your page should be able to show not only data recieved, but also two another states: loading and error.
(So the advise is "add loader").
// data.service.ts
import { Injectable } from "#angular/core";
import { HttpClient, HttpClientModule } from "#angular/common/http";
#Injectable()
export class DataService {
private _categories = [];
constructor(private http: HttpClient) {}
get categories() {
return this._categories;
}
getData(): Promise<any[]> {
return new Promise<any[]>(resolve => {
this.http.get('https://api.myjson.com/bins/18qku4').subscribe(data => {
this._categories = Array.from(Object.keys(data), k => data[k]);
console.log("load data...");
resolve(this._categories);
});
});
}
}
// app.module.ts
import { NgModule, APP_INITIALIZER } from "#angular/core";
import { BrowserModule } from "#angular/platform-browser";
import { FormsModule } from "#angular/forms";
import { RouterModule } from "#angular/router";
import { ListDataComponent } from "./list-data/list-data.component";
import { AppComponent } from "./app.component";
import { DataService } from "./data.service";
import { HttpClientModule } from "#angular/common/http";
import {DetailComponent} from './detail/detail.component'
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
RouterModule.forRoot([
{ path: "", component: ListDataComponent },
{ path: "detail", component: DetailComponent }
])
],
declarations: [AppComponent, ListDataComponent,DetailComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
//list-data.component.ts
import { Component, OnInit } from "#angular/core";
import { DataService } from "../data.service";
#Component({
selector: "app-list-data",
templateUrl: "./list-data.component.html",
styleUrls: ["./list-data.component.css"],
providers: [DataService],
})
export class ListDataComponent implements OnInit {
categories = [];
constructor(service: DataService) {
service.getData().then(data => {
debugger;
this.categories = data;
});
}
ngOnInit() {}
}
There are alternatives to resolve this issue:
One is you can use a loader which you can display until the service call finishes.
Second is you can use *ngIf="categories?.length" which will keep your component hides until your service call finishes.
I hope it will resolve your issue.
I am trying to create a reusable component that serves as a processing overlay when making asynchronous calls across my site. I have a service in place but the OverlayComponent doesn't seem to get invoked when showOverlay is invoked:
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HashLocationStrategy, LocationStrategy } from '#angular/common';
import { HttpModule } from '#angular/http';
import { AppRoutingModule } from './app-routing.module';
import { MainComponent } from './app.mysite.component';
import { OverlayComponent } from './app.mysite.overlay.component';
import { TrackerComponent } from './pages/tracker/mysite.tracker.component';
import { OverlayService } from "./overlay.service";
#NgModule({
imports: [ BrowserModule, AppRoutingModule, HttpModule ],
declarations: [
MainComponent,
OverlayComponent,
NavbarComponent,
TrackerComponent,
],
providers: [{provide: LocationStrategy, useClass: HashLocationStrategy}, OverlayService],
bootstrap: [ MainComponent ]
})
export class AppModule { }
TrackerComponent.ts
import { Component, OnInit } from '#angular/core';
import { OverlayService } from '../../overlay.service.js';
#Component({
moduleId: module.id,
selector: 'tracker-component',
templateUrl: '/public/app/templates/pages/tracker/mysite.tracker.component.html',
providers: [ OverlayService]
})
export class TrackerComponent implements OnInit{
constructor(private http: Http, private overlayService: OverlayService) {
}
ngOnInit(): void {
this.overlayService.showOverlay('Processing...'); //This kicks everything off but doesn't show the alert or overlay
this.overlayService.test(); //does exactly what i'd expect
}
}
overlay.service.ts
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class OverlayService {
private message: string;
private subject: Subject<any> = new Subject<any>();
showOverlay(msg: string) : void { //When this gets invoked, shouldn't it be invoking a change to this.subject and therefore invoking getMessage()
this.message = msg;
this.subject.next(msg);
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
test() {
return 'test good'; //if I call this function, it works
}
}
app.mysite.overlay.component
import { Component, OnInit } from '#angular/core';
import { OverlayService } from './overlay.service';
#Component({
selector: 'overlay-component',
templateUrl: '/public/app/templates/mysite.overlay.component.html',
styleUrls: ['public/app/scss/overlay.css'],
providers: [OverlayService]
})
export class OverlayComponent implements OnInit {
private processingMessage: string;
constructor(private overlayService: OverlayService) {}
ngOnInit() {
this.overlayService.getMessage().subscribe((message: string) => { //since i'm subscribed to this, i'm expecting this to get called. It doesn't
this.processingMessage = message;
alert(this.processingMessage); //never gets hit
$('.overlay-component-container').show(); // never gets hit
},
error => {
alert('error');
})
}
}
Specifying providers in the Component metadata actually creates a new injectable, scoped to that component tree.
If you want to share the overlay service across the app, you'll need to declare the overlay provider in the NgModule, and not in the components. Alternatively, you can declare it only as a provider on the top-level entry component (eg. AppComponent), though it may cause confusion when used in other entry components/lazy-loaded modules.
See https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html for a better explanation
I have a route called home and it has three child routes, documents, mail and trash. In the home route component it has a variable called 'myUser'. I created a service so they can all share the myUser value, but for some reason the service variable value doesn’t change.
Service
import { Injectable } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
#Injectable()
export class HomeService {
// Mock user, for testing
myUser = {name:"John", loggedIn:true};
setUser(name:string){
this.myUser.name = name ;
}
isLogged():boolean {
if(this.myUser.loggedIn == true){
return true ;
}
return false ;
}
}
Home (parent route)
import { Component } from '#angular/core';
import { Router, ROUTER_DIRECTIVES } from '#angular/router';
import { CORE_DIRECTIVES, FORM_DIRECTIVES } from '#angular/common';
import { Http, Headers } from '#angular/http';
import { contentHeaders } from '../common/headers';
import { HomeService } from '../../home.service';
const template = require('./home.component.html');
const styles = require('./home.component.css');
#Component({
selector: 'home',
directives: [ CORE_DIRECTIVES, FORM_DIRECTIVES ],
template: template,
styles: [ styles ],
providers: [HomeService]
})
export class HomeComponent {
constructor(public router: Router, private homeService: HomeService) {
}
myUser = this.homeService.myUser ;
setUser(name:string){
this.homeService.setUser(name);
}
// Is logged in
isLogged():boolean {
return this.homeService.isLogged();
}
}
Mail (child route)
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { CORE_DIRECTIVES, FORM_DIRECTIVES } from '#angular/common';
import { HomeService } from '../../home.service';
const template = require('./mail.component.html');
const styles = require('./mail.component.css');
#Component({
selector: 'mail',
directives: [ CORE_DIRECTIVES, FORM_DIRECTIVES ],
template: template,
styles: [ styles ],
providers: [HomeService]
})
export class MailComponent {
constructor(public router: Router, private homeService: HomeService) {
}
myUser = this.homeService.myUser ;
setUser(name:string){
this.homeService.setUser(name);
}
}
You have to inject HomeService into either bootstrap function in Dependency array or MainComponent providers so that service will instantiate only once.
bootstrap(MainComponent, [HomeService, ....other dependency...])
And then remove HomeService from providers array of both the components metadata.