Angular re-fetch data after parameter change GET request - javascript

How to re-fetch data after parameter change from:
oglas/1 to oglas/2 by click, so when put URL and than click ENTER everything works, but when click on oglas/2 button when oglas/1 is rendered URL changes to oglas/2 but data is from oglas/1?
TS
import { Component, OnInit } from "#angular/core";
import { ActivatedRoute } from "#angular/router";
import { Post } from "../post.model";
import { ServerService } from "../server.service";
#Component({
selector: "post",
templateUrl: "./post.component.html",
styleUrls: ["./post.component.css"]
})
export class PostComponent implements OnInit {
post: Post[];
constructor(
private route: ActivatedRoute,
private serverService: ServerService
) {}
ngOnInit(): void {
this.getPost();
}
getPost(): void {
const id = +this.route.snapshot.paramMap.get("id");
this.serverService.getPosts(id).subscribe(post => (this.post = post));
}
}
Service
import { HttpClient } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { Post } from "./post.model";
import { User } from "./user.model";
import { Observable } from "rxjs";
#Injectable({ providedIn: "root" })
export class ServerService {
usersUrl = "http://localhost:3000/users";
postsUrl = "http://localhost:3000/posts";
constructor(private http: HttpClient) {}
getPosts(id: number | string): Observable<Post[]> {
const url = `${this.postsUrl}/${id}`;
return this.http.get<Post[]>(url);
}
getUser(id: number | string): Observable<User[]> {
const url = `${this.usersUrl}/${id}`;
return this.http.get<User[]>(url);
}
}

Since your are making an API call for data in ngOnInit(), requested data may not be available by the time your component loads. And Angular might be reusing the same instance of the component, making ngOnInit() to be called only once.
You can use Angular Resolvers to ensure that you have the required data before loading the component.
1) Create a route resolver to fetch the required data before loading the route.
PostDataResolver.ts:
// ... imports
#Injectable()
export class PostDataResolver implements Resolve<any> {
constructor(private serverService: ServerService) {}
resolve(route: ActivatedRouteSnapshot) {
const id = route.paramMap.get('id');
return this.serverService.getPosts(id);
}
}
2) Add this resolver to your routing module:
{ path: "oglas/:id", component: PostComponent, resolve: { postData: PostDataResolver }}
3) Then access the resolved data in your component.
PostComponent.ts:
export class PostComponent implements OnInit {
post: Post[];
constructor(
private route: ActivatedRoute,
private serverService: ServerService
) {}
ngOnInit(): void {
this.post = this.route.snapshot.data.postData;
}
}
This ensures that you have the latest and appropriate data before the component loads.

Got it...
import { Component, OnInit, OnChanges } from "#angular/core";
import { ActivatedRoute, Router } from "#angular/router";
import { Post } from "../post.model";
import { ServerService } from "../server.service";
#Component({
selector: "post",
templateUrl: "./post.component.html",
styleUrls: ["./post.component.css"]
})
export class PostComponent implements OnInit {
post: Post[];
id: number;
constructor(
private route: ActivatedRoute,
private serverService: ServerService
) {}
ngOnInit(): void {
this.route.paramMap.subscribe(params => {
this.id = parseInt(params.get("id"));
this.getPost(this.id);
});
}
getPost(id: number): void {
this.serverService.getPosts(id).subscribe(post => (this.post = post));
}
}
This code re-fetch data to a component
ngOnInit(): void {
this.route.paramMap.subscribe(params => {
this.id = parseInt(params.get("id"));
this.getPost(this.id);
});
}
Thank you all for your effort!

Related

How do I push data from a Subject observable to an array in the component?

I am trying to push a message into an array that is already declared as a variable in the component. I am using a service and have created a subject observable to take data from one component and inject it into another component. When I try to push the data onto the array after subscribing to the variable, it's updated temporarily but when I open that component, the data is not pushed. The array updates when I console log from inside the subscribe method but it's reset once I open that component. I don't know what is the problem. This is the code:
Service.ts
import { Injectable } from '#angular/core';
import { User } from './user';
import { Subject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class SerService {
private message = new Subject<string>();
sourceMessage$ = this.message.asObservable();
constructor() { }
sendMessage(message: string) {
this.message.next(message);
}
}
Receiver component
import { Component, OnInit } from '#angular/core';
import { SerService } from '../ser.service';
import { User } from "../user";
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
public messages = ['hi', 'hello', 'bye'];
constructor(private _service: Service) { }
ngOnInit() {
this._service.message$
.subscribe(
message => {
this.messages.push(message);
}
);
}
}
Sender Component
import { Component, OnInit } from '#angular/core';
import { SerService } from '../ser.service';
import { User } from '../user';
#Component({
selector: 'app-sign-up',
templateUrl: './sign-up.component.html',
styleUrls: ['./sign-up.component.css']
})
export class SignUpComponent {
userModel = new User('', '', '', '', false);
constructor (private _service : SerService) {}
onSubmit(){
this._service.sendMessage(this.userModel.message);
}
}
I can't update the message array. How do I do this with minimal changes?
You can create a service to send data from one component to another by using BehaviourSubject
Service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable({
providedIn: 'root'
})
export class DataService {
private userDetails = new BehaviorSubject<any>('');
currentUserDetails = this.userDetails.asObservable();
constructor() { }
sendUserDetails(message){
this.userDetails.next(message)
}
}
Sender Component:
import { DataService } from '/services/data.service';
export class SignupComponent implements OnInit {
public userDetails;
constructor(private _dataService: DataService) {}
ngOnInit(){
userDetails = new User('', '', '', '', false);
this._dataService.sendUserDetails(this.userDetails);
}
}
Receiver Component
import { DataService } from '/services/data.service';
export class LoginComponent implements OnInit {
public userDetails;
constructor(private _dataService: DataService) {}
ngOnInit(): void {
this._dataService.currentUserDetails.subscribe(userDetails => this.userDetails = userDetails);
}
Blockquote

Inject data from service to component

Currently, I'm using Rx.JS and Angular 7. My problem contains with the issue in Observable.
The problem is I can't fetch data from service to form-code.component.
After I've used setShortCode() there isset data, but in form-code.component.ts i can't see seebscribe() data
shortcode.service.ts
import { Injectable, NgZone } from '#angular/core';
import { Subject, Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class ShortcodeService {
public shortcode = new Subject<any>();
constructor(private zone: NgZone) {}
setShortCode(code) {
this.zone.run(() => {
this.shortcode.next(code);
});
}
getShortCode(): Observable<any> {
return this.shortcode.asObservable();
}
}
dnd.component.ts
this.textAreaText = `<iframe src="${window.location.origin +
'/form/' +
project.id}/design" width="100%" height="500px" frameborder="0"></iframe>`;
this.shortCodeService.setShortCode(this.textAreaText);
this.router.navigate(['/form-copy']);
form-code.components.ts
import { Component, OnInit, OnDestroy, AfterViewInit } from '#angular/core';
import { ShortcodeService } from '../../services/shortcode.service';
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-form-code',
templateUrl: './form-code.component.html',
styleUrls: ['./form-code.component.scss']
})
export class FormCodeComponent implements OnInit, OnDestroy {
constructor(
private sanitizer: DomSanitizer,
private shortCodeService: ShortcodeService
) {}
shortText: string;
sub: Subscription;
ngOnInit() {
this.sub = this.shortCodeService.getShortCode().subscribe(
shortcode => {
console.log(shortcode);
this.shortText = shortcode;
},
error => console.log(error),
() => {}
);
}
ngOnDestroy(): void {
//Called once, before the instance is destroyed.
//Add 'implements OnDestroy' to the class.
this.sub.unsubscribe();
}
}
Working, When I changed Subject to BehaviorSubject

Angular: Access this.id declared in an "OnInit" function

Update 1
After I read Alexanders suggestions, I updated the code and got no error back. But Angular doesn't do a request to the server anymore, which make me curious. And also the pageTitle does not update.
appointmentDetail.component.html
{{appointmentDetail.time}}
appointmentDetail.component.ts
import { Component, OnInit, OnDestroy, Injectable } from '#angular/core';
import { ActivatedRoute, ParamMap } from '#angular/router';
import { Title } from '#angular/platform-browser';
import { APIService } from './../../../api.service';
import { Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
#Component({
selector: 'app-appointmentdetail',
templateUrl: './appointmentDetail.component.html',
styleUrls: ['./appointmentDetail.component.scss']
})
export class AppointmentDetailComponent implements OnInit {
id: any;
appointmentDetail$: Observable<Object>; // I'd really create an interface for appointment or whatever instead of object or any
pageTitle = 'Some Default Title Maybe';
constructor(
private route: ActivatedRoute,
private title: Title,
private apiService: APIService
) {}
ngOnInit() {
this.appointmentDetail$ = this.route.paramMap.pipe(
tap((params: ParamMap) => {
this.id = params.get('id');
// Or this.id = +params.get('id'); to coerce to number maybe
this.pageTitle = 'Termin' + this.id;
this.title.setTitle(this.pageTitle);
}),
switchMap(() => this.apiService.getAppointmentDetailsById(this.id))
);
}
public getData() {
this.apiService
.getAppointmentDetailsById(this.id)
.subscribe((data: Observable<Object>) => {
this.appointmentDetail$ = data;
console.log(data);
});
}
}
api.service.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class APIService {
API_URL = 'http://localhost:5000';
constructor(private httpClient: HttpClient) {}
getAppointments() {
return this.httpClient.get(`${this.API_URL}/appointments/`);
}
getAppointmentDetailsById(id) {
return this.httpClient.get(`${this.API_URL}/appointments/${id}`);
}
getAppointmentsByUser(email) {
return this.httpClient.get(`${this.API_URL}/user/${email}/appointments`);
}
getCertificatesByUser(email) {
return this.httpClient.get(`${this.API_URL}/user/${email}/certificates`);
}
}
As you can see, I want to grab that parameter id from the router parameters and want to pass it into my API call, which will do a Angular HTTP request. Hope I'm right, haha.
Original Question
Currently, I ran into a nasty problem. The thing is, I want to read the params, which are given to me by ActivatedRouter and the Angular OnInit function. I subscribe them params and log them in the console. Until here, everything is working fine. But I want to access "this.id" outside from my OnInit function, so I can use it on pageTitle for example.
But, this.id is undefined. So the page title is Termineundefined.
Source code:
import { Component, OnInit, OnDestroy, Injectable } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Title } from '#angular/platform-browser';
import { APIService } from './../../api.service';
#Component({
selector: 'app-appointment-details',
templateUrl: './appointment-details.component.html',
styleUrls: ['./appointment-details.component.scss']
})
#Injectable()
export class AppointmentDetailsComponent implements OnInit, OnDestroy {
private routeSub: any;
id: any;
private appointmentDetail: Array<object> = [];
constructor(
private route: ActivatedRoute,
private title: Title,
private apiService: APIService
) {}
pageTitle = 'Termin' + this.id;
ngOnInit() {
this.title.setTitle(this.pageTitle);
this.getData();
this.routeSub = this.route.params.subscribe(params => {
console.log(params);
this.id = params['id'];
});
}
ngOnDestroy() {
this.routeSub.unsubscribe();
}
public getData() {
this.apiService
.getAppointmentDetailsById(this.id)
.subscribe((data: Array<object>) => {
this.appointmentDetail = data;
console.log(data);
});
}
}
The issue here really comes down to async availability of route params and observable streams. You simply cannot use the value until it has resolved for all practical purposes. You can use RxJS operators such as switchMap and tap in line with the official Routing & Navigation documentation to ensure route param id is available prior to use. tap can be used to introduce side effects such as setting class id property from route params and/or setting title. You could even create a class property of an Observable<YourObject[]> and utilize Angular Async Pipe to avoid subscribing and unsubscribing to display the data.
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Title } from '#angular/platform-browser';
import { APIService, MyFancyInterface } from './../../api.service';
import { Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
#Component({
selector: 'app-appointment-details',
templateUrl: './appointment-details.component.html',
styleUrls: ['./appointment-details.component.scss']
})
export class AppointmentDetailsComponent implements OnInit {
id: any;
appointmentDetail$: Observable<MyFancyInterface>;
appointmentDetail: MyFancyInterface;
pageTitle = 'Some Default Title Maybe';
constructor(
private route: ActivatedRoute,
private title: Title,
private apiService: APIService
) {}
ngOnInit() {
this.appointmentDetail$ = this.route.paramMap.pipe(
tap((params: ParamMap) => {
this.id = params.get('id')
// Or this.id = +params.get('id'); to coerce to type number maybe
this.pageTitle = 'Termin' + this.id;
this.title.setTitle(this.pageTitle);
}),
switchMap(() => this.apiService.getAppointmentDetailsById(this.id))
);
/* Or
this.route.paramMap.pipe(
tap((params: ParamMap) => {
this.id = params.get('id')
// Or this.id = +params.get('id'); to coerce to type number maybe
this.pageTitle = 'Termin' + this.id;
this.title.setTitle(this.pageTitle);
}),
switchMap(() => this.apiService.getAppointmentDetailsById(this.id))
).subscribe((data: MyFancyInterface) => {
this.appointmentDetail = data;
});
*/
}
}
Template:
<div>{{(appointmentDetail | async)?.id}}</div>
I'd recommend to create an interface to represent your data model and type the return of your api service method:
import { Observable } from 'rxjs';
// maybe put this into another file
export interface MyFancyInterface {
id: number;
someProperty: string;
...
}
export class APIService {
...
getAppointmentDetailsById(id): Observable<MyFancyInterface> {
return this.httpClient.get<MyFancyInterface>(`${this.API_URL}/appointments/${id}`);
}
...
}
If you really must, you can save the observable as you do now for the route params and subscribe as needed in the various parts of the class, but this demonstrated way you almost absolutely know that route param id will be available for use and can explicitly set the things you need to set.
I'd also remove #Injectable() as there is no reason to have it here with a #Component() decorator.
Note* the async pipe operator in this example ensures the Http call is executed. Otherwise a subscribe() is needed (search SO for Angular http not executing to see similar issues)
Hopefully that helps!
Instead of
id: any;
You could try using a getter, like so
public get id(): any {
this.route.params.subscribe(params => {
return params['id'];
}
}
In your template, just
{{ id }}

Http call as service in Angular

I am brand new to Angular and I am attempting to make a simple http request for data. How do I access this data in my component? I am getting the error 'Cannot read property 'get' of undefined'
data.service.ts
import { Injectable } from '#angular/core'
import { HttpClient } from '#angular/common/http'
#Injectable()
export class FetchData {
private url: string = 'https://jsonplaceholder.typicode.com/users'
constructor(private http: HttpClient){}
get(){
return this.http.get(this.url).subscribe(data => {
console.log(data)
})
}
}
table.component.ts
import { FetchData } from './datatable.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-datatable',
templateUrl: './datatable.component.html',
styleUrls: ['./datatable.component.css']
})
export class DatatableComponent implements OnInit {
Data: FetchData
constructor() { }
ngOnInit() {
this.Data.get()
}
}
You need to "Inject" the service in your component and also make the subscription in your component.
In your service you should "map" your response.
import { Injectable } from '#angular/core'
import { HttpClient } from '#angular/common/http'
import 'rxjs/add/operator/map';
#Injectable()
export class FetchData {
private url: string = 'https://jsonplaceholder.typicode.com/users'
constructor(private http: HttpClient){}
get(){
return this.http.get(this.url).map(data => {
return data.json();
})
}
}
Your component:
import { FetchData } from './datatable.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-datatable',
templateUrl: './datatable.component.html',
styleUrls: ['./datatable.component.css']
})
export class DatatableComponent implements OnInit {
constructor(private fetchDataService: FetchData) { }
ngOnInit() {
this.fetchDataService.get().subscribe(res => {
console.log(response);
});
}
}
try this:
export class DatatableComponent implements OnInit {
constructor(private dataService: FetchData) { }
ngOnInit() {
this.dataService.get()
}}

Unable to retrieve JSON file in Angular 2 project

I am trying to display static JSON data in my angular 2 project. I am getting a console error 'GET http://localhost:4200/app/data/MOCK_DATA.json 404 (Not Found)' I have added my services.ts and component.ts pages.
service.ts
import { Injectable } from '#angular/core';
import { ConfigurationService } from '../../configuration.service';
import { Http, Response, RequestOptions, Headers } from '#angular/http';
import { Observable } from 'rxjs';
import { ListItem } from './list-item';
#Injectable()
export class DataService {
constructor(
private _http: Http,
private _configurationService: ConfigurationService
) {}
get() : Observable<ListItem[]> {
return this._http.get("app/data/MOCK_DATA.json")
.map((response: Response) => <ListItem[]> response.json())
}
}
app.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { DataService } from '../data.service';
import { ListItem } from './list-item';
import { Subscription } from 'rxjs';
#Component({
selector: 'app-component',
templateUrl: 'component.html',
styleUrls: ['component.css']
})
export class Component implements OnInit {
busy:Subscription;
datas: ListItem[] = [];
constructor(
private _dataService: DataService,
private _confirmationService: ConfirmationService,
private _authService: AuthService,
private _router: Router,
) {
}
ngOnInit(){
}
getdatas() {
this.busy =
this._dataService.get()
.subscribe(data => this.datas = data)
}
Since it is static. there is no need to http.get.
Create a json.ts file
export your JSON file as
export const json={
"key":"value"
}
then import it where required
import { json } from './json.ts'
then console.log(json) inside the class to check the file/json.

Categories