Refreshing the page after changing the URL - javascript

It seems to be a basic thing but I'm struggling. I have a view "/plugin/{pluginId}" that doesn't update when I hit enter after changing the pluginId in the address bar. I have to manually refresh the page in the browser. What can I do so that it refreshes automatically?
<div>
<div>
<a mat-raised-button href="/plugin">
<span>←</span>
</a>
<a mat-raised-button href="" style="float: right">Send message</a>
</div>
<div>
<ul>
<li *ngFor="let message of (messages$ | async)">
{{message.message}}
<br><br>
</li></ul>
</div>
</div>
export class Foo implements OnInit {
pluginMessagesState$: Observable< fromPluginMessages.State >;
pluginId = '';
messages$: Observable< Message[] >;
constructor(private route: ActivatedRoute,
private store: Store< fromRoot.State >,
) {
if ( this.route.snapshot.params[ 'pluginId' ] ) {
this.pluginId = this.route.snapshot.params[ 'pluginId' ];
}
this.pluginMessagesState$ = this.store.select( fromRoot.getPluginMessagesState );
this.messages$ = this.pluginMessagesState$.pipe(
map( state => state.pluginMessages.get( this.pluginId )?.list || [] )
);
}
ngOnInit(): void {
}
}

import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-user-detail',
templateUrl: 'user-detail.component.html'
})
export class UserDetailComponent implements OnInit {
constructor(private activeRoute: ActivatedRoute) {
}
ngOnInit() {
const queryParams = this.activeRoute.snapshot.queryParams
const routeParams = this.activeRoute.snapshot.params;
// do something with the parameters
this.loadUserDetail(routeParams.id);
}
}
or
ngOnInit() {
this.activeRoute.queryParams.subscribe(queryParams => {
// do something with the query params
});
this.activeRoute.params.subscribe(routeParams => {
this.loadUserDetail(routeParams.id);
});
}

Does your router file look like this ?
{path: 'plugin/:id', component: UserDetailComponent}
If so, subscribe to ActivatedRoute (inject it in the constructor parameters) like this:
public pluginId: number;
constructor(private route: ActivatedRoute) {
}
And access the parameter by subscribing to route.params:
ngOnInit() {
this.routeSub = this.route.params.subscribe(params => {
// update the pluginId accordingly
this.pluginId = params['id'];
});
}

Related

Console.log prints the cart but can't bind it to the html. ANGULAR

What am I doing wrong?
As you can see the console.log prints the output fine but I can't seem to see it on the webpage.
get-cart.component.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Cart } from 'src/app/models/cartModel';
import { UsersService } from 'src/app/services/users.service';
#Component({
selector: 'app-get-one-cart',
templateUrl: './get-one-cart.component.html',
styleUrls: ['./get-one-cart.component.css']
})
export class GetOneCartComponent implements OnInit {
cart: Cart;
constructor(
private route: ActivatedRoute,
private userService: UsersService
) { }
ngOnInit(): void {
const id = this.route.snapshot.paramMap.get('id');
this.userService.getCartForUser(id)
.subscribe(data => {
this.cart = data;
console.log(this.cart)
});
}
}
get-cart.html
<div class="content-box">
<a routerLink="/users">
<button>Back</button>
</a>
<div *ngIf="cart">
<h2>{{cart._id}}</h2>
<p>{{cart.userId}}</p>
<div *ngFor="let product of cart.products">
<p>{{product.productId}}</p>
<p>{{product.quantity}}</p>
<p>{{product.price}}</p>
</div>
</div>
</div>
console.log and webpage outputs
Got the answer! I just had to set
await Cart.find
to
await Cart.findOne

Messages disappearing after refreshing the page

I have a view with a list of messages. I would like them not to disappear after I refresh the browser page. I used this tutorial and created a local storage service:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class LocalStorageService {
localStorage: Storage;
constructor() {
this.localStorage = window.localStorage;
}
get(key: string): any {
if (this.isLocalStorageSupported) {
return JSON.parse(this.localStorage.getItem(key)!);
}
return null;
}
set(key: string, value: any): boolean {
if (this.isLocalStorageSupported) {
this.localStorage.setItem(key, JSON.stringify(value));
return true;
}
return false;
}
remove(key: string): boolean {
if (this.isLocalStorageSupported) {
this.localStorage.removeItem(key);
return true;
}
return false;
}
get isLocalStorageSupported(): boolean {
return !!this.localStorage
}
}
#Component({
selector: 'app-message-receiver',
templateUrl: './message-receiver.component.html',
styleUrls: ['./message-receiver.component.scss']
})
export class MessageReceiverComponent implements OnInit {
pluginId: string = '';
messages$: Observable<Message[] | undefined>;
gatewayMessagesState$: Observable<GatewaysMessagesRegistry>;
sub?: Subscription;
constructor(private route: ActivatedRoute,
private store: Store<fromRoot.State>,
private location: Location,
private localStorageService: LocalStorageService
) {
this.gatewayMessagesState$ = store.select(fromRoot.getGatewayMessages);
this.sub = this.route.paramMap.subscribe(params => {
this.pluginId = params.get('gatewayId') || '';
})
this.messages$ = this.gatewayMessagesState$.pipe(
map(state => state.gatewaysMessagesMap.get(this.pluginId)?.list)
);
}
ngOnInit(): void {
}
ngOnDestroy() {
this.sub?.unsubscribe();
}
persist(key: string, value: any) {
this.localStorageService.set(key, value);
}
getCurrentPath() {
return this.location.path();
}
beautify(message: string) {
return vkbeautify.xml(message);
}
}
<div class="templates">
<a mat-list-item *ngFor="let message of (messages$ | async)?.reverse()">
<mat-expansion-panel class="panel">
<mat-expansion-panel-header>
<mat-panel-title>{{message.date | date:'mediumTime'}}</mat-panel-title>
<mat-panel-description class="description">
<div>{{message.title}}</div>
</mat-panel-description>
</mat-expansion-panel-header>
<div>
<pre>{{beautify(message.content)}}</pre>
</div>
</mat-expansion-panel>
<br>
</a>
</div>
The problem is, I have only basic knowledge of Angular and don't really know how to proceed. To be specific, I don't know how to incorporate the persist method into the HTML file. I'm also not sure if something more is needed.
I've tried using local storage without a separate service as well but didn't manage to succeed either.

Angular how to call Method from Sibling Component that is binded via <router-outlet>?

I have a Project that uses HTTP Calls to fetch Data from API Endpoint and than display on Screen.
It's a simple ToDoList. So you can add Items to the list, see all Items in your List, delete items and so on.
The Project structure is this:
Items-Component (Holds the entire App basically)
Item-list-component
Item-detail-component
Item-edit-component
item-add-component
Item.service
The Items.component.html looks like this:
<div class="row">
<div class="col-md-5">
<app-item-list></app-item-list>
</div>
<div class="col-md-7">
<router-outlet></router-outlet>
</div>
So we can see that the item-list-component and the other 3 components (binded via router-outlet) are sibling components, that's what I think.
So my Problem is now:
I want that whenever a new Item is created the items[] in the items.list component should refresh automatically. Now I must click a "Fetch Items" button to refresh the items[].
When I add a new Item, it fires a method from my item.service, it holds a fetchItems Method that just returns an Observable of the API Endpoint, like this:
Item-add component.ts:
#Component({
selector: 'app-item-add',
templateUrl: './item-add.component.html',
styleUrls: ['./item-add.component.css']
})
export class ItemAddComponent implements OnInit {
constructor(private itemService: ItemService, private route: ActivatedRoute, private router: Router) { }
ngOnInit(): void {
}
onCreatePost(item: Item) {
// Send Http request
this.itemService.createAndStorePost(item.description, item.isComplete);
//Here I want that the items[] in the items.list component refreshes when I add new Item
this.onCancel();
}
onCancel() {
this.router.navigate([''], {relativeTo: this.route});
}
}
And the item.service.ts:
#Injectable()
export class ItemService {
constructor(private http: HttpClient, private route: ActivatedRoute, private router: Router) {
}
fetchItems(): Observable<Item[]> {
return this.http.get<Item[]>('https://localhost:44321/api/TodoItems');
}
fetchItem(id: number): Observable<Item> {
return this.http.get<Item>('https://localhost:44321/api/TodoItems' + '/' + id);
}
createAndStorePost(description: string, isComplete: boolean) {
var item = { description: description, isComplete: isComplete };
this.http.post('https://localhost:44321/api/TodoItems', item)
.subscribe(Response => {
});
}
deleteItem(id: number): Observable<Item> {
return this.http.delete<Item>('https://localhost:44321/api/TodoItems' + '/' + id);
}
updateItem(id:number, item: Item) {
this.http.put<Item>('https://localhost:44321/api/TodoItems' + '/' + id, item).subscribe();
}
}
Then the items-list component catches that Observable and subscribes to it and sets the Response from that subscription to and items[] in the component itself:
#Component({
selector: 'app-item-list',
templateUrl: './item-list.component.html',
styleUrls: ['./item-list.component.css']
})
export class ItemListComponent implements OnInit {
items: Item[] = [];
constructor(private route: ActivatedRoute, private router: Router, private itemService: ItemService) { }
ngOnInit(): void {
this.onFetchItems();
}
onFetchItems() {
this.itemService.fetchItems().subscribe(Response => {
this.items = Response;
});
}
onNewItem() {
this.router.navigate(['new'], {relativeTo: this.route});
}
}
What can I do to trigger that the items.list should fetch Items again?
I can't use #ViewChild because it is no Parent-Child relation.
Can I implement and instance of item.list anywhere in the project and just call the onFetchItems Method?
Thanks!
you can use BehaviorSubject to share data between your different components.
Here is an example:
In your ItemService.
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class ItemService {
private _itemsSource = new BehaviorSubject([]);
currentItems = this._itemsSource.asObservable();
constructor() { }
updateItems(items: []): void {
this._itemsSource.next(items)
}
}
In your ItemsComponent, you update the new value in the service after you get all the items,
#Component({
selector: 'app-item',
templateUrl: './item.component.html',
styleUrls: ['./item.component.css']
})
export class ItemComponent implements OnInit {
items: Item[] = [];
constructor(private itemService: ItemService) { }
ngOnInit(): void {
this.onFetchItems();
}
onFetchItems() {
this.itemService.fetchItems().subscribe(Response => {
this.items = Response;
this.updateItems(this.items)
});
}
updateItems(newItems: []): void {
this.itemService.updateItems(newItems)
}
}
And in your ItemListComponent
#Component({
selector: 'app-item-list',
templateUrl: './item-list.component.html',
styleUrls: ['./item-list.component.css']
})
export class ItemListComponent implements OnInit {
items: Item[] = [];
subscription: Subscription;
constructor(private route: ActivatedRoute,
private router: Router,
private itemService: ItemService) { }
ngOnInit(): void {
this.subscription = this.itemService.currentItems.subscribe(items => this.items = items)
}
onNewItem() {
this.router.navigate(['new'], {relativeTo: this.route});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}

Data not passing through with route in Angular 6

I have a application with a table of cars:
This is my code:
Carcomponent.html
<tbody>
<tr *ngFor="let car of allCars; index as carId" \>
<td [routerLink]="['/cars', carId]">{{car.carId}}</td>
<td>{{car.brand}}</td>
<td>{{car.model}}</td>
<td>{{car.color}}</td>
<td>{{car.topSpeed }}</td>
</tr>
</tbody>
I have register the route like this:
{ path: 'cars/:carId', component: CardetailsComponent }
And this is my CarDetails.ts file:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { CarVM } from '../viewmodels/car-vm';
import { CarService } from '../services/car.service';
#Component({
selector: 'app-cardetails',
templateUrl: './cardetails.component.html',
styleUrls: ['./cardetails.component.css']
})
export class CardetailsComponent implements OnInit {
car: any;
carList: any;
constructor(private route: ActivatedRoute, private carservice: CarService) { }
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.car = params.get('carId');
});
}
getCarList() {
this.carList = new CarVM();
this.carservice.getCarById(this.carList.carId).subscribe((res: any) => {
this.carList = res.data;
console.log(this.carList)
})
}
}
And on my Cardetails.html I want to show the selected car like this:
<h2>Car Details</h2>
<div *ngIf="car">
<h3>{{ car.brand }}</h3>
<h4>{{ car.model }}</h4>
<p>{{ car.color }}</p>
</div>
The routing is working fine and fetching the cars is working. Now I want to select one car and see the brand, model, color on the next page. I use a viewmodel for this:
export class CarVM {
CarId: number;
Brand: string;
Model: string;
Color: string;
TopSpeed: number;
}
How can I see the selected car on the next page?
I have followed this tutorial:
https://angular.io/start/routing
Ok, you seem to be bit confused. In cardetails component you want to process carId from route parameters and use it to get car details. You can either get them from server, or have the service return already loaded details of all cars.
Let's say we are trying to make it happen getting the first way, it might look like this:
import { map, switchMap } from 'rxjs/operators';
ngOnInit() {
this.getCar();
}
private getCar(): void {
this.route.paramMap.pipe(
map(params => params.get('carId')),
switchMap(carId => {
return this.carservice.getCarById(carId);
})
).subscribe(
res => {
this.car = res;
console.log('#My car:', this.car);
}
);
}
First, you'll get the carId from route.paramMap, map it using rxjs map, then use switchMap to call you carservice.getCarById(carId) and have it return Observable to which you can subscribe. This should do the trick. Don't forget to properly map it/create CarVM object from it.
The problem is, you don't have CarVM object properly on CardetailsComponent. You are only getting carId into CarVM here: this.car = CarVM[+params.get('carId')];
First you need to create CarVM properly with your class variables. And the you can call your index.
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { CarVM } from '../viewmodels/car-vm';
#Component({
selector: 'app-cardetails',
templateUrl: './cardetails.component.html',
styleUrls: ['./cardetails.component.css']
})
export class CardetailsComponent implements OnInit {
car: any;
carList: any;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.car = params.get('carId');
});
}
getCarList(){
this.carList = new CarVM();
//call your service here to fill your carList variable and once you get car list, you will be able to access variable using with your index (this.car).
}
}

Trigger cloned component when click on original in Angular

I have a loop on a component which represents a list of graph cards on my real app.
I have copied this component ( and loop it ) as the original
Hello Component
export class HelloComponent {
message:string;
printedMessage:string
#Input() elm:string;
constructor(private data: DataService, private router : Router) { }
ngOnInit() {
this.message = this.data.messageSource.value;
this.data.messageSource.subscribe(message => this.message = message)
}
updateService(){
this.data.changeMessage(this.message);
this.printedMessage=this.data.messageSource.value
}
navigateToSibling(){
this.router.navigate(['/sibling']);
}
}
app component
<div *ngFor="let elm of [1,2,3,4]">
<hello [elm]= "elm"></hello>
</div>
<h1>Copy </h1>
<div *ngFor="let elm of [1,2,3,4]">
<hello [elm]= "elm"></hello>
</div>
DataService component
export class DataService {
messageSource = new BehaviorSubject<string>("default message");
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
}
Expected behaviour
What I would is when change the input value on the component 1 for example , only the value on the input of the copied component 1 changes.
Actual behaviour
Actually when I change a value inside an input all the other inputs are changings.
Here's a stackblitz example
Below is a solution that will solve you issue. This may not be a perfect solution but you need something similar.
hello.html
<h1>App component {{elm}}</h1>
<input type="text" [(ngModel)]="message">
<button (click)="updateService()" type="button">Save</button> {{printedMessage}}
Data Service
import {
Injectable
} from '#angular/core';
import {
BehaviorSubject
} from 'rxjs/BehaviorSubject';
#Injectable()
export class DataService {
messageSource = new BehaviorSubject < any > ("default message");
constructor() {}
changeMessage(message: string, elem: any) {
this.messageSource.next({
message: message,
elem: elem
});
}
}
HelloComponent
import {
Component,
Input
} from '#angular/core';
import {
DataService
} from "./dataService";
import {
Router
} from '#angular/router';
#Component({
selector: 'hello',
templateUrl: './hello.html',
styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent {
message: string;
printedMessage: string
#Input() elm: string;
constructor(private data: DataService, private router: Router) {}
ngOnInit() {
this.message = this.data.messageSource.value;
this.data.messageSource.subscribe(message => this.message = message.elem === this.elm ? message.message : this.message);
}
updateService() {
debugger
this.data.changeMessage(this.message, this.elm);
this.printedMessage = this.data.messageSource.value.message;
}
navigateToSibling() {
this.router.navigate(['/sibling']);
}
}
Have also updated the Stackblitz Demo. Hope this helps :)

Categories

Resources