I have a mock json file that provides the list of products to be displayed on a Product page. I'd like to be able to select Product 1 and then only display Product 1 information on the Product Detail page but currently the Product Detail page just displays everything for all products and won't display the product name of the selected div. I am self-taught so not sure if I'm making the mistake in the typescript or json file or what. Can anyone point me to what I am overlooking to make this work?
The code:
product.component.html
<h4>Department 1</h4>
<div class="application-card" routerLink="/productDetails"
*ngFor="let product of products; let i = index">
{{ product.product_name }}
</div>
product.component.ts
import { Component, OnIt } from '#angular/core';
import {HttpClient} from "#angular/common/http";
#Component ({
selector: 'app-product',
templateURL: './product.component.html',
styleURLs: [./product.component.css']
})
export class ProductComponent implements OnInit {
products: any ""
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.allProducts();
}
allProducts() {
this.http.get<ProductList>(url: 'http://localhost:3000/productList).subscribe(next: data => {
this.products = data;
})
}
}
interface ProductList {
product_name: string;
product_detail_1: string;
product_detail_2: string;
product_detail_3: string;
}
productdetails.component.html
<h1>Product Details - {{ product.product_name }}</h1>
<p *ngFor="let product of products">
{{ product.product_details_1 }}
{{ product.product_details_2 }}
{{ product.product_details_3 }}
</p>
productdetails.component.ts
import { Component, OnIt } from '#angular/core';
import {HttpClient} from "#angular/common/http";
#Component ({
selector: 'app-productdetails',
templateURL: './productdetails.component.html',
styleURLs: [./productdetails.component.css']
})
export class ProductDetailsComponent implements OnInit {
products: any ""
constructor(private http: HttpClient) {}
ngOnInit(): void {
this.allProducts();
}
allProducts() {
this.http.get<ProductList>(url: 'http://localhost:3000/productList).subscribe(next: data => {
this.products = data;
})
}
}
interface ProductList {
product_name: string;
product_detail_1: string;
product_detail_2: string;
product_detail_3: string;
}
products.json
{
"productList": [
{
"product_name": "Product 1",
"product_details_1": "1st detail about Product 1",
"product_details_2": "2nd detail about Product 1",
"product_details_3": "3rd detail about Product 1"
},
{
"product_name": "Product 2",
"product_details_1": "1st detail about Product 2",
"product_details_2": "2nd detail about Product 2",
"product_details_3": "3rd detail about Product 2"
},
{
"product_name": "Product 3",
"product_details_1": "1st detail about Product 3",
"product_details_2": "2nd detail about Product 3",
"product_details_3": "3rd detail about Product 3"
},
In your product.component.html file, you are looping through the products liste and getting each products index but your are doing nothing with it.
When you navigate to product details component, that particular component doesn’t know what to show.
You can fixe that by adding mock endpoint. For exemple: /productDetails/0 for thé first product in the array.
You can add the endpoint in your routing file. I suppose your’s is still the app.component-routing.ts?
If so, here how to going to proceed:
Change your productDetails route to something like this:
{ path: '/productDetails/:id,component: ProductDetailsComponent}
Inside your productdetails.component.ts, follow those steps.
Step 1: import ActivatedRoute
import { ActivatedRoute } from '#angular/router';
Step 2: Inject ActivatedRoute in constructor.
constructor(private activatedRoute: ActivatedRoute) { }
Step 3: Get id on a local variable in ngOnInit() or in the constructor{}
const productId = this.activatedRoute.snapshot.params['id'];
Filter you products by id and you done.
Related
I have created mock service file and I want to display in my html but not really sure how to make it display properly so I'll be really appreciated If I can get any help or suggestion.
<div class="container2">
<div class="header" style="height: 400px">
<div class="content3">
<div>
<h1 class="kpi-title">90,346</h1>. // I'm trying to remove this hard code html
<p class="kpi-des">Users Right Now</p> // and display it from my mock data file.
</div>
<div>
<h1 class="kpi-title">250++l</h1>
<p class="kpi-des">Saved</p>
</div>
<div>
<h1 class="kpi-title">$34.5 mill</h1>
<p class="kpi-des">New User per Week</p>
</div>
</div>
</div>
</div>
TS
import { ProductService } from '../../data/product-suite.service';
export class MaxisProductSuiteComponent {
productService: ProductService[];
ngOnIT(){
}
product-suite.service.ts
export class ProductService {
productsuite: ProductSuite[] = [
{
id: 1,
title: '90,346',
description: 'Users',
},
{
id: 2,
title: '$34.5 mill',
description: 'Saved',
},
{
id: 3,
title: '250++',
description: 'New User per Week',
},
];
}
Please find the below code for your solutions:
create a json file assets folder with name output.json.
{
"result" : [
{
"id": 1,
"title": "90,346",
"description": "Users at Ford"
},
{
"id": 2,
"title": "$34.5 mill",
"description": "Saved for Ford"
},
{
"id": 3,
"title": "250++",
"description": "New User per Week"
},
{
"id": 4,
"title": "64%",
"description": "Users At Ford"
}
]
}
in service file write below code:
import { observable, Observable } from "rxjs";
import { MaxisProductSuite } from "src/Model/model";
import { HttpClient } from '#angular/common/http';
import { Injectable } from "#angular/core";
#Injectable()
export class MaxisProductService {
constructor(private http: HttpClient){}
getAllMaxisps():Observable<MaxisProductSuite> {
return this.http.get<MaxisProductSuite>("./assets/output.json");
}
}
then component file add below code:
import { DOCUMENT } from '#angular/common';
import { Component, Inject, OnInit } from '#angular/core';
import { MaxisProductSuite } from 'src/Model/model';
import { MaxisProductService } from 'src/service/MaxisProductService';
#Component({
selector: 'app-temp',
templateUrl: './temp.component.html',
styleUrls: ['./temp.component.scss']
})
export class TempComponent implements OnInit {
maxisps: MaxisProductSuite[];
public resultData:MaxisProductSuite=null;
constructor(#Inject(DOCUMENT) private document: Document, private service : MaxisProductService) {}
ngOnInit() {
this.service.getAllMaxisps().subscribe((res:MaxisProductSuite) => {
console.log(res);
this.resultData =res;
});
}
}
then HTMl file add below code:
<div *ngFor="let item of resultData?.result">
<div class="header" style="height: 400px">
<h1>{{item.id}}</h1>
<h2>{{item.title}}</h2>
<h3>{{item.description}}</h3>
</div>
add below model in model file
export interface MaxisProductSuite {
result : Result[]
}
export interface Result{
id?: number;
title: string;
description: string;
}
I hope it will help you to get the solution.
happy to HELP!
make your service in module provider or make it injectable "root".
inject the service in your component you want to display data in constructor as a dependency injection.
assign your component variable array with productService.
in your HTML loop about your data array using *ngFor=" let product of products".
use you product value in the interpolation {{ product.id }}.
I'm having real trouble converting a JSON array to collection of objects in Angular. I've not used Angular for some time, please forgive me if any of my terminology is incorrect.
I have the following products.json file in my project:
[
{
"id": 1,
"name": "Pen",
"description": "The finest blue pen.",
"relatedProducts": [2]
},
{
"id": 2,
"name": "A4 Paper",
"description": "A4 sized printer paper.",
"relatedProducts": [1]
}
]
This product.ts file:
export class Product {
Id: number;
Name: string;
Description: string;
RelatedProducts: Array<number>;
}
My app.component.ts file:
import * as ProductsJson from '../../json-data/products.json';
import { Component, OnInit } from '#angular/core';
import { Product } from 'src/app/models/product';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
productsJson: any = ProductsJson;
products: Array<Product>;
ngOnInit() {
this.products = <Product[]>this.productsJson;
console.log(this.products);
}
}
And finally my app.component.html:
<ul>
<li *ngFor="let p of products">
{{p.Id}} {{p.Name}}
</li>
</ul>
My console log shows the JSON data but it seems as though I'm making some error trying to convert it to a list of Product objects. This is also preventing me from successfully using ngFor in my view as the console error shows, so nothing shows on the page. How can I perform this conversion correctly so the loop works and this data is shown on the view?
you can use class-transformer which helps you to convert plain javascript objects to real es6 classes. With this real instances you can also use class methods.
import { Component, OnInit } from "#angular/core";
import ProductsJson from "./products.json";
import {plainToClass} from "class-transformer";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
productsJson: any = ProductsJson;
products: Array<Product>;
constructor() {}
ngOnInit() {
//this.products = <Product[]>this.productsJson;
this.products = plainToClass(Product, this.productsJson as []);
console.log(this.products);
}
}
export class Product {
id: number;
name: string;
description: string;
relatedProducts: Array<number>;
getId(){
return this.id + "_ID";
}
}
template:
<ul>
<li *ngFor="let p of products">
{{p.getId()}} {{p.id}} {{p.name}}
</li>
</ul>
**- Use interface instead of class for Product.**
export interface Product {
id: number;
name: string;
description: string;
relatedProducts: number[];
}
**- If you need a class, then specify the parameterised constructor to assign variable values.**
export class Product {
id: number;
name: string;
description: string;
relatedProducts: number[];
constructor(product) {
this.id = product.id;
this.name: product.name;
this.description: product.description;
this.relatedProducts: product.relatedProducts;
}
}
**and then inside the component you can use**
this.products = new Product(this.productsJson); // if Product is a class
OR
this.products = this.productsJson; // if Product is an interface
look at stackblitz
Let me know still you have an issue..
thanks
Your model should look like below,
export interface Product {
id: number;
name: string;
description: string;
relatedProducts: number[];
}
Change the products as
products: Product[];
and then,
this.products = this.productsJson;
Adding to Kushal Shah answer
this also works.
import { Component, OnInit } from "#angular/core";
import ProductsJson from "./products.json";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
products = <Product[]>ProductsJson;;
constructor() {}
ngOnInit() {
console.log(this.products);
}
}
export class Product {
id: number;
name: string;
description: string;
relatedProducts: Array<number>;
}
step by step:
Component.ts
The first, you have unuseless variables you have reduce keeping just one 'products' and initializing just like that the following code.
In ngOnInit() you must to load the array initialized before. The way to do that is casting with type any cause importing from local json generates another data model, you have to access to the property 'default' and that's all.
import * as ProductsJson from '../../json-data/products.json';
import { Component, OnInit } from '#angular/core';
import { Product } from 'src/app/models/product';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
products: Array<Product> = new Array<Product>();
ngOnInit() {
this.products = (ProductsJson as any).default;
console.log(this.products);
}
}
Component.html
Another point to change is properties naming you have 'Id' & 'Name' instead of 'id' and 'name'.
<ul>
<li *ngFor="let p of products">
{{p.id}} {{p.name}}
</li>
</ul>
Entities
Product Class have to change the properties naming without Capital letters to have a correspondence with the Json Data or change Json data model to the Class anyway.
If I were you I will change also Product, using interface instead Class
export interface Product {
id: number;
name: string;
description: string;
relatedProducts: Array<number>;
}
UPDATE 1 [ You can go declarative way of rxjs too ]..
Use of operator of rxjs to create new stream of observable from your json file.
import {of} from 'rxjs'
productsJson = of <Product []>(ProductsJson);
export interface Product{
id: number;
name: string;
description: string;
relatedProducts: Array<number>;
}
and then in your template you can simply use async pipe ...
<ul>
<li *ngFor="let p of productsJson | async">
{{p.id}} {{p.name}}
</li>
</ul>
Update 2 [ You can simply use JSON.parse() and JSON.stringify() ]..
productsJson: any = JSON.parse(JSON.stringify(ProductsJson));
and then use this in template like this...
<div *ngFor = "let item of productsJson">
<span> {{item.id }} </span>
</div>
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css'],
})
export class HelloWorldComponent implements OnInit {
jsonString: string = '[{"title":"test"},{"title":"test2"}]';
jsonObj: Array<object>;
jsonObj2: Array<object> = [{ title: 'test' }, { title: 'test2' }];
constructor() {
this.jsonObj = JSON.parse(this.jsonString);
this.jsonString = JSON.stringify(this.jsonObj2);
}
ngOnInit() {}
}
I am pretty new to Angular 2. I am trying to achieve the same task as Highlight the search text - angular 2 what is mentioned in above post.
I have created the pipe filter my question is where should i keep the pipe filter and where should i place the inner html div.
Copying the problem:
A messenger displays the search results based on the input given by the user. Need to highlight the word that is been searched, while displaying the result. These are the html and component that is been used.
component.html
<div *ngFor = "let result of resultArray">
<div>Id : result.id </div>
<div>Summary : result.summary </div>
<div> Link : result.link </div>
</div>
Component.ts
resultArray : any = [{"id":"1","summary":"These are the results for the searched text","link":"http://www.example.com"}]
This resultArray is fetched from hitting the backend service by sending the search text as input. Based on the search text, the result is fetched. Need to highlight the searched text, similar to google search.
How should i apply the search filter and where should i keep the inner html?
There are some tweaks to be made to the regex replacement regarding case, but here's a starting point:
//our root app component
import {Component, NgModule, VERSION, Pipe, PipeTransform} from '#angular/core'
import {BrowserModule, DomSanitizer} from '#angular/platform-browser'
#Pipe({
name: 'highlight'
})
export class HighlightSearch implements PipeTransform {
constructor(private sanitizer: DomSanitizer){}
transform(value: any, args: any): any {
if (!args) {
return value;
}
// Match in a case insensitive maneer
const re = new RegExp(args, 'gi');
const match = value.match(re);
// If there's no match, just return the original value.
if (!match) {
return value;
}
const result = value.replace(re, "<mark>" + match[0] + "</mark>");
return this.sanitizer.bypassSecurityTrustHtml(result);
}
}
#Component({
selector: 'my-app',
template: `
<input (input)="updateSearch($event)">
<div *ngFor="let result of resultArray" [innerHTML]="result.summary | highlight: searchTerm"></div>
`,
})
export class App {
results: string[]
searchTerm: string;
constructor() {
this.resultArray = [
{
"id": "1",
"summary": "These are the results for the searched text",
"link": "http://www.example.com"
},
{
"id": "2",
"summary": "Here are some more results you searched for",
"link": "http://www.example.com"
}
]
}
updateSearch(e) {
this.searchTerm = e.target.value
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App, HighlightSearch ],
bootstrap: [ App ]
})
export class AppModule {}
Plnkr
Edit: Plnkr seems unhappy. StackBlitz
you can easily use this directive
usage:
<label [jsonFilter]="search">{{text}}</label>
directive
import {
AfterViewInit,
Directive,
ElementRef,
Input,
OnChanges,
SimpleChanges
} from '#angular/core';
#Directive({
selector: '[jsonFilter]'
})
export class FilterDirective implements OnChanges, AfterViewInit {
#Input() jsonFilter = '';
constructor(
private el: ElementRef,
) {
}
ngOnChanges(changes: SimpleChanges) {
this.ngAfterViewInit();
}
ngAfterViewInit() {
const value = this.el.nativeElement?.innerText.split('\n').join('');
if (!value) return;
const re = new RegExp(this.jsonFilter, 'gi');
const match = value.match(re);
if (!match || match.some(x => !x)) {
this.el.nativeElement.innerText = value;
} else {
this.el.nativeElement.innerHTML = value.replace(re, "<mark>" + match[0] + "</mark>")
}
}
}
How do I display this specific JSON using *ngFor?
{
"status": 0,
"dallases": [{
"vehicle_id": 17954,
"dallassettings": "3",
"dallasupdated": "False",
"dallas_list": [{
"number": 666111222,
"auth": 3
}, {
"number": 666777888,
"auth": 4
}, {
"number": 123454321,
"auth": 4
}]
}
}
vehicle.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Jsonp, Response } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class VehicleService {
constructor(private http: Http) { }
getVehicle() {
return this.http.get('myURL')
.map(res => res.json());
}
}
vehicle.component.ts
import { Component, enableProdMode } from '#angular/core';
import { VehicleService } from './vehicle.service';
enableProdMode();
#Component({
//moduleId: module.id,
selector: 'vehicle-json',
templateUrl: './vehicle.html',
providers: [VehicleService]
})
export class VehicleComponent {
//vehicles: Vehicle[];
vehicles: GeneralVehicle[];
constructor(private vehicleService: VehicleService) {
this.vehicleService.getVehicle().subscribe(vehicle => {
this.vehicles = vehicle;
});
}
}
/*interface Vehicle {
id: number;
title: string;
body: string;
}*/
interface GeneralVehicle {
status: number;
dallases: Vehicle[];
}
interface Vehicle {
vehicle_id: number;
dallassettings: number;
dallasupdated: string;
dallas_list: DallasList[];
}
interface DallasList {
number: number;
auth: number;
}
When I worked on dummy data it was simply, but this JSON structure is more complex. I tried using Pipe, but I'm not sure if I'm using it correctly.
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({ name: 'keys' })
export class VehiclePipe implements PipeTransform {
transform(value, args: string[]): any {
let keys = [];
for (let key in value) {
keys.push(key);
}
return keys;
}
}
That's the *ngFor
*ngFor="let vehicle of vehicles | keys"
I'd like to dislay status once, and then repeat all the dallases (Vehicle[]).
At least you have one error here:
dallassettings: '3' // string
but in your interface you have:
dallassettings: number;
Then if you implement Matthiases implementation, put a *ngIf-statement, since I suspect you get your data async, so view is rendered before data retrieved, which would cause a undefined error.
<div *ngIf="vehicles"> // here!
<h1>{{ vehicles.status }}</h1>
<div *ngFor="let vehicle of vehicles.dallases">
<div>ID: {{vehicle.vehicle_id}}</div>
<div>Settings: {{vehicle.dallassettings}}</div>
<div>Updated: {{vehicle.dallasupdated}}</div>
<div *ngFor="let d of vehicle.dallas_list">
<div>number: {{d.number}}</div>
<div>auth: {{d.auth}}</div>
</div>
</div>
</div>
EDIT your status is undefined, since you you are using the wrong attribute it should be vehicles.status with "s". When I edited this post, I also added the "s" to the answer, so that it would be correct :)
You have to loop through 2 ngFor's:
<div *ngFor="let vehicle of vehicles | keys">
<h1>{{ vehicle.status }}</h1>
<ul *ngFor="let dallas of vehicle.dallases">
<li>{{ dallas | json }}</li> //Show the data you want
</ul>
</div>
vehicles is an object. I see no need to iterate over the keys with the pipe you proposed. You can access the members you need directly. You should iterate over the dalasses property of vehicles - which is an array. Then display the array member as you require. E.g. with a json pipe to get in text format, you you might also implement custom formatting inside the template through properties.
Example:
<div>
<h1>{{ vehicle.status }}</h1>
<div *ngFor="let vehicle of vehicles.dallases">
<div>ID: {{vehicle.vehicle_id}}</div>
<div>Settings: {{vehicle.dallassettings}}</div>
<div>Updated: {{vehicle.dallasupdated}}</div>
<div *ngFor="let d of vehicle.dallas_list">
<div>number: {{d.number}}</div>
<div>auth: {{d.auth}}</div>
</div>
</div>
</div>
How do I display the id and title of the below Hero object?
The Hero interface below is mapped according to Firebase JSON response.
hero.component.ts
import {Component, Input} from 'angular2/core';
import {Hero} from '../model/hero';
#Component({
selector: 'hero-component',
template: `
{{hero | json }} this works. This display the Firebase JSON response as seen below
<br>
{{ Object.keys(hero)[0] }} this DOESN'T work
<br>
{{ hero[Object.keys(hero)[0]].title }}this also DOESN'T work
`
})
export class HeroComponent {
#Input()
hero:Hero;
}
hero.ts
export interface Hero {
[id: string]: {
createdAt: number;
isActive: boolean;
title: string;
updatedAt: number;
}
}
Firebase JSON response
{ "-KEMOfA5KFK98AXNhPY0": { "createdAt": 1459607745222, "isActive": true, "title": "Wind Hero", "updatedAt": 1459607745222 } }
update
instead of pipes: [KeysPipe]
use
#NgModule({
declarations: [KeysPipe],
exports: [KeysPipe],
}
export class SharedModule{}
#NgModule({
...
imports: [SharedModule],
})
original
You can't use Object.keys(hero)[0] in the template. No global references can be used, only members of the component.
Instead create a function on your component
getKeys(obj) {
return Object.keys(obj);
}
and then in the template
{{ getKeys(hero)[0] }}
Alternatively you can build a pipe that extracts the keys like for example https://stackoverflow.com/a/34074484/217408
#Pipe({ name: 'keys', pure: false })
export class KeysPipe implements PipeTransform {
transform(value: any, args: any[] = null): any {
return Object.keys(value);
}
}
then use it like
{{ (hero | keys)[0] }}
Don't forget to add the pipe to pipes: [KeysPipe] on the component where you want to use it or alternatively
bootstrap(App, [provide(PLATFORM_PIPES, {useValue: KeysPipe, multi:true})]);
Your code isn't working since Object isn't available in your component.
You could try something like this:
#Component({
selector: 'hero-component',
template: `
{{hero | json }} this works. This display the Firebase JSON response as seen below
<br>
{{ obj.keys(hero)[0] }} this DOESN'T work
<br>
{{ hero[obj.keys(hero)[0]].title }}this also DOESN'T work
`
})
export class HeroComponent {
#Input()
hero:Hero;
obj = Object;
}
or using a method like Günter suggested in his answer.