I'm trying to passing the data from one page to another routing page using [queryParams]="{'usd':usd, 'count' :country, 'name' :name}" from the routing button. it's perfectly working but there is an error in my console.
ERROR in src/app/hairstylist-profile/hairstylist-profile.component.ts(18,40): error TS2339: Property 'value' does not exist on type 'Observable<Params>'.
src/app/hairstylist-profile/hairstylist-profile.component.ts(20,42): error TS2339: Property 'value' does not exist on type 'Observable<Params>'.
src/app/hairstylist-profile/hairstylist-profile.component.ts(22,41): error TS2339: Property 'value' does not exist on type 'Observable<Params>'.
this is my code:
import {Component, OnInit} from '#angular/core';
import {ActivatedRoute} from '#angular/router';
#Component({
selector: 'app-hairstylist-profile',
templateUrl: './hairstylist-profile.component.html',
styleUrls: ['./hairstylist-profile.component.css'],
})
export class HairstylistProfileComponent implements OnInit {
name;
usd;
count;
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
const usd = this.route.queryParams.value.usd;
this.usd = usd;
const count = this.route.queryParams.value.count;
this.count = count;
const name = this.route.queryParams.value.name;
this.name = name;
}
}
there is an error in value keyword. So how can solve?
This may Help you
ngOnInit() {
this.route
.queryParams
.subscribe(params => {
// Defaults to 0 if no query param provided.
this.usd = params['usd'] || 0;
this.count = params['count'] || 0;
this.name = params['name'] || ';
});
}
You cannot use .value,you must subscribe query parmas
UPDATE 4.0.0
See Angular docs for more details https://angular.io/guide/router#fetch-data-before-navigating
ORIGINAL
Using a service is the way to go. In route params you should only pass data that you want to be reflected in the browser URL bar.
See also https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service
The router shipped with RC.4 re-introduces data
constructor(private route: ActivatedRoute) {}
and ..
const routes: RouterConfig = [
{path: '', redirectTo: '/heroes', pathMatch : 'full'},
{path : 'heroes', component : HeroDetailComponent, data : {some_data : 'some value'}}
];
and ..
class HeroDetailComponent {
ngOnInit() {
this.sub = this.route
.data
.subscribe(v => console.log(v));
}
ngOnDestroy() {
this.sub.unsubscribe();
}
}
See also the Plunker at https://github.com/angular/angular/issues/9757#issuecomment-229847781
Related
I'm getting this error when I want to display firebase subcollections in HTML. It does display in console but not in UI.
I'm getting this error. Console image
TS file
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../services/auth.service';
import { Router } from '#angular/router';
import { AngularFirestore } from '#angular/fire/compat/firestore';
#Component({
selector: 'app-my-reservations',
templateUrl: './my-reservations.page.html',
styleUrls: ['./my-reservations.page.scss'],
})
export class MyReservationsPage implements OnInit {
user: any;
userId: string;
storedData: any = [];
constructor(
private auth: AuthService,
private router: Router,
private afs: AngularFirestore
) {}
ngOnInit() {
this.auth.user$.subscribe((user) => {
this.userId = user.userId;
});
}
fetchBookings() {
this.afs
.collection('user')
.doc(this.userId)
.collection('BookingHistory')
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.id, ' => ', doc.data());
this.storedData = querySnapshot;
});
});
}
}
HTML file
<ion-item *ngFor="let data of storedData">
<ion-label>{{data.TimeSlot}}</ion-label>
<ion-label>{{data.BookedAt}}</ion-label>
<ion-label>{{data.BookingDate}}</ion-label>
</ion-item>
I'd try something like this,
As long as the querySnapshot returns array of storedData without any nested objects, you can set it directly if not you have to read it through the correct entry from the response structure and read the values from the list accordingly in your HTML template
fetchBookings() {
this.afs
.collection('user')
.doc(this.userId)
.collection('BookingHistory')
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => { // or you can remove this forEach() and set the querySnapshot (if it returns an array of storedData) directly
console.log(doc.id, ' => ', doc.data());
this.storedData.push(doc);
});
});
}
}
I am using ag-grid license edition and want to bring record set to the grid using a service. I am using Ag-Grid gridoption property and initializing rowdata
in the constructor of the component.ts class. But the data is not rendering. No error either. Please have a look of my component.ts class.
Please note: the web api is returning the json data correctly and the service in Angular is tested sucessfully without Ag-Grid
import {Component, OnInit} from "#angular/core";
import {GridOptions} from "ag-grid";
import {RedComponentComponent} from "../red-component/red-component.component";
import { person } from "../Shared/models/person.model";
import {personservice} from '../service/person.service';
#Component({
selector: 'app-my-grid-application',
templateUrl: './my-grid-application.component.html'
})
export class MyGridApplicationComponent implements OnInit{
private gridOptions: GridOptions;
private persons: person[];
constructor(private personservice: personservice) {
this.gridOptions = <GridOptions>{};
this.gridOptions.columnDefs = [
{
headerName: "ID",
field: "PersonId",
width: 100
},
{
headerName: "Name",
field: "PersonNm",
cellRendererFramework: RedComponentComponent,
width: 100
},
{
headerName: "Email",
field: "PersonEmail",
cellRendererFramework: RedComponentComponent,
width: 100
}
];
this.gridOptions.rowData = this.persons
}
ngOnInit() {
this.personservice.findAll().subscribe(
persons => {
this.persons = persons
},
error => {
console.log(error);
}
)
}
}
Instead of providing columnDefs and rowData with gridOptions, try providing them directly like this.
<ag-grid-angular
[gridOptions]="gridOptions"
[rowData]="rowData"
[columnDefs]="columnDefs"
>
</ag-grid-angular>
Also, declare columnDefs and rowData as component variables, and then assign them after getting the response.
rows: any[] = [];
columnDefs: ColDef[];
constructor(private personservice: personservice) {
this.gridOptions = <GridOptions>{};
this.columnDefs = [
// your column definition goes here
];
}
ngOnInit() {
this.personservice.findAll().subscribe(
persons => {
this.rows = persons
},
error => console.log(error)
)
}
Let me know if you face any issue after this.
For some reason the response JSON is not mapping correctly
Here is my html.
profile-search.component.html
<h3>Enter Username</h3>
<input (keyup)="search($event.target.value)" id="name" placeholder="Search"/>
<ul>
<li *ngFor="let package of packages$ | async">
<b>{{package.name}} v.{{package.repos}}</b> -
<i>{{package.stars}}</i>`enter code here`
</li>
</ul>
Here is component that the html pulls from.
profile-search.component.ts
import { Component, OnInit } from '#angular/core';
import { Observable, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { NpmPackageInfo, PackageSearchService } from './profile-search.service';
#Component({
selector: 'app-package-search',
templateUrl: './profile-search.component.html',
providers: [ PackageSearchService ]
})
export class PackageSearchComponent implements OnInit {
withRefresh = false;
packages$: Observable<NpmPackageInfo[]>;
private searchText$ = new Subject<string>();
search(packageName: string) {
this.searchText$.next(packageName);
}
ngOnInit() {
this.packages$ = this.searchText$.pipe(
debounceTime(500),
distinctUntilChanged(),
switchMap(packageName =>
this.searchService.search(packageName, this.withRefresh))
);
}
constructor(private searchService: PackageSearchService) { }
toggleRefresh() { this.withRefresh = ! this.withRefresh; }
}
Service that component pulls from.
profile-search.service.ts
import { Injectable, Input } from '#angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { HttpErrorHandler, HandleError } from '../http-error-handler.service';
export interface NpmPackageInfo {
name: string;
}
export const searchUrl = 'https://api.github.com/users';
const httpOptions = {
headers: new HttpHeaders({
'x-refresh': 'true'
})
};
function createHttpOptions(packageName: string, refresh = false) {
// npm package name search api
// e.g., http://npmsearch.com/query?q=dom'
const params = new HttpParams({ fromObject: { q: packageName } });
const headerMap = refresh ? {'x-refresh': 'true'} : {};
const headers = new HttpHeaders(headerMap) ;
return { headers, params };
}
#Injectable()
export class PackageSearchService {
private handleError: HandleError;
constructor(
private http: HttpClient,
httpErrorHandler: HttpErrorHandler) {
this.handleError = httpErrorHandler.createHandleError('HeroesService');
}
search (packageName: string, refresh = false): Observable<NpmPackageInfo[]> {
// clear if no pkg name
if (!packageName.trim()) { return of([]); }
// const options = createHttpOptions(packageName, refresh);
// TODO: Add error handling
return this.http.get(`${searchUrl}/${packageName}`).pipe(
map((data: any) => {
return data.results.map(entry => ({
name: entry.any[0],
} as NpmPackageInfo )
)
}),
catchError(this.handleError('search', []))
);
}
}
I have tried to alter
return this.http.get(`${searchUrl}/${packageName}`).pipe(
map((data: any) => {
return data.results.map(entry => ({
name: entry.any[0],
} as NpmPackageInfo )
)
to
login: data.login, and login: entry.login but keep getting the below error.
http-error-handler.service.ts:33 TypeError: Cannot read property 'map'
of undefined
at MapSubscriber.project (profile-search.service.ts:49)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next
(map.js:75)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next
(map.js:81)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterSubscriber._next
(filter.js:85)
at FilterSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
at MergeMapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber.notifyNext
(mergeMap.js:136)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/InnerSubscriber.js.InnerSubscriber._next
(InnerSubscriber.js:20)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next
(Subscriber.js:93)
results in data.results is probably undefined, check that the data object matches the schema you're expecting it to.
map working on array but this.http.get(${searchUrl}/${packageName}) return object not array.
so data.results is undefined.
This is how I converted my object into an array, if anyone has a better way of doing please let me know.
return this.http.get(`${searchUrl}/${packageName}`).pipe(
map((data: any) => {
console.log(data);
var profile = Object.keys(data).map(function(key) {
return [(key) + ': ' + data[key]];
}
);
console.log(profile);
data = profile;
return data;
}),
catchError(this.handleError<Error>('search', new Error('OOPS')))
);
}
}
I fixed this issue by eliminating ".results"
from
.map((data: any) => this.convertData(data.results))
to
.map((data: any) => this.convertData(data))
To avoid the error, change
map((items) => items.map
to
map((items) => items?.map
Then set your result set as an empty array:
this.list = data ?? [];
PS: Used with Angular 14. In older versions you may need to change last one to data ? data : []
I'm trying to make a request from an external API in angular 2.
I want to manage the data request in 2 files and display the result as json.
My data-output component looks like this:
import {Component} from '#angular/core'
import {DataService} from './datavisualisation.service'
#Component({
selector: 'app-datavisualisation-output',
template: `
`
})
export class DatavisualisationOutput {
constructor(dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
}
My second file for the service looks like this:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class DataService {
constructor(http:Http) {
this.data = http.get('http;//...API')
.map(response => response.json());
}
}
...but the console shows the following error:
components/datavisualisation/dataservices/datavisualisation.output.service.ts:12:26
Property 'data' does not exist on type 'DataService'.
components/datavisualisation/dataservices/datavisualisation.output.service.ts:14:29
Property 'data' does not exist on type 'DatavisualisationOutput'.
components/datavisualisation/dataservices/datavisualisation.output.service.ts:16:43 Cannot find name 'data'.
components/datavisualisation/dataservices/datavisualisation.service.ts:8:13
Property 'data' does not exist on type 'DataService'.
What am i doing wrong here?
You should define the dataproperty on your DatavisualisationOutput component:
export class DatavisualisationOutput {
public data: any; //this one
constructor(dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
}
and on your DataService:
#Injectable()
export class DataService {
public data: any;
constructor(http:Http) {
this.data = http.get('http;//...API')
.map(response => response.json());
}
}
and on DatavisualisationOutput... Just.. always define any property you access with this.
As #PierreDuc already said you should define that variable inside component class to make it available inside Class context.
Also you should create a method inside a service which would be responsible for data. Just make an call same method from another component and it will return the data which has retrieved last time.
Code
#Injectable()
export class DataService {
data: any;
constructor(http:Http) {
}
getData(){
if(this.data) return Observable.of(this.data)
else
return http.get('http;//...API')
.flatMap(response => {
this.data = response.json();
return Observable.of(this.data)
);
}
}
Component
export class DatavisualisationOutput {
myData: any;
constructor(private dataservice: DataService) {
dataservice.data
.subscribe(
data => this.data = data,
console.error,
() => console.log('Look at this:' + data)
);
}
ngOnInit(){
dataservice.getData().subscribe(
data => myData = data
)
}
}
I need to generate sanitized css property to use with my component template to set the background image of the div:
<div *ngFor="let Item of Items"
[style.background-image]="Item.imageStyle
(click)="gotoDetail(Item.iditems)">
</div>
using data obtained through a data service. The component is:
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { DomSanitizer } from '#angular/platform-browser';
import { OnInit } from '#angular/core';
import { Item } from '../models/Item';
import { CollectionDataService } from '../services/CollectionData.service';
#Component({
selector: 'mainpage',
templateUrl: 'app/mainpage/mainpage.component.html',
styleUrls: ['app/mainpage/mainpage.component.css']
})
export class MainpageComponent implements OnInit {
Items: Item[];
ngOnInit() {
this.collectionDataService.getItems().subscribe(
Items => this.Items = Items
);
// Generates and sanitizes image links
this.Items.map(
(LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
)
}
constructor(
private router: Router,
private sanitizer: DomSanitizer,
private collectionDataService: CollectionDataService
) {
}
gotoDetail($iditems: number): void {
this.router.navigate(['/viewer', $iditems]);
}
}
But it doesn't work because the statement that generates the sanitized property
this.Items.map(
(LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
)
doesn't find the loaded data. The error that I'm seeing in the browser console is:
core.umd.js:3070 EXCEPTION: Uncaught (in promise): Error: Error in ./MainpageComponent class MainpageComponent_Host - inline template:0:0 caused by: Cannot read property 'map' of undefined
TypeError: Cannot read property 'map' of undefined
The data service is:
import { Injectable } from '#angular/core'
import { Http } from '#angular/http'
import { Item } from '../models/Item';
import { DomSanitizer } from '#angular/platform-browser';
#Injectable()
export class CollectionDataService {
constructor(
private http: Http,
private sanitizer: DomSanitizer
) { }
getItems() {
return this.http.get('app/mocksdata/items.json').map(
response => <Item[]>response.json().items
)
}
}
And the provided items.json:
{
"items": [{
"iditems": 1,
"imageStyle": ""
}, {
"iditems": 2,
"imageStyle": ""
}]
}
If I set static data in the component, instead of using the data service, everything works:
export class MainpageComponent implements OnInit {
Items: Item[];
ngOnInit() {
this.Items = [{
"iditems": 1,
"imageStyle": ""
}, {
"iditems": 2,
"imageStyle": ""
}]
// Generates and sanitizes image links
this.Items.map(
(LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
)
}
How can I force the sanitizer statement to wait that the async data are fully loaded? Alternatively how can I generate sanitized properties directly in the service?
EDIT
The best answer comes from PatrickJane below:
Items: Item[] = [];
ngOnInit() {
this.collectionDataService.getItems().subscribe(Items => {
this.Items = Items;
this.Items.map(LItem => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)"))}
});
}
I also solved this problem working directly in the service method (credits), but it is more verbose:
return this.http.get('app/mocksdata/items.json')
.map( (responseData) => {
return responseData.json().items;
})
.map(
(iitems: Array<any>) => {
let result:Array<Item> = [];
if (iitems) {
iitems.forEach((iitem) => {
iitem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+iitem.iditems+".jpg)");
result.push(<Item>iitem);
});
}
return result;
}
)
The subscribe function is async so your map function called before the subscribe function run. So in this phase the array is undefined because you doesn't set any initial value.
The solution is to do this inside the subscribe function and to initialize the Items with empty array.
Items: Item[] = [];
ngOnInit() {
this.collectionDataService.getItems().subscribe(Items => {
this.Items = Items;
this.Items.map(LItem => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)"))}
});
}