Sharing data between two pages upon clicking button Angular - html

Im trying to get the hero details to show on hero-details page upon clicking on the button but I cant seem to figure out how to make it work. When I click on the button Im able to show the details on hero-list page but if I try to reroute it using routerLink I cant get the data to show. Right now Im just using <app-hero-details [hero]="selectedHero" > so I can have the data display when the hero button is clicked. I guess it has something to do with this [hero]="selectedHero".
heroes-list.components.html
<h1>Hero List</h1>
<ul>
<!--Displays a list of hero names-->
<li *ngFor="let hero of heroes">
<button type="button" (click)="onSelect(hero)" [class.selected]="hero === selectedHero" >
<span class="name">{{hero.name}}</span >
</button>
</li>
</ul>
<!-- just used to test hero-details page routing -->
<!-- <p><a routerLink="/hero-details" routerLinkActive="active">Click here</a></p> -->
<!-- just used to test 404/wildcard page routing -->
<!-- <p><a routerLink="/fakeLink" routerLinkActive="active">Click here</a></p> -->
<!-- this will show the data on this page-->
<app-hero-details [hero]="selectedHero" ></app-hero-details>
heroes-list.component.ts
import { Component, OnInit } from '#angular/core';
import { BackendService } from '../services/backend.service';
import { Hero } from '../types/Hero';
#Component({
selector: 'app-heroes-list',
templateUrl: './heroes-list.component.html',
styleUrls: ['./heroes-list.component.css']
})
export class HeroesListComponent implements OnInit {
selectedHero?: Hero;
heroes: Hero[] = [
];
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
constructor(private backend: BackendService) { }
async ngOnInit(): Promise<void> {
// Gets a list of heroes to display
this.heroes = await this.backend.getHeroes();
}
}
hero-details.components.html
<p>Hero Details section</p>
<!-- <p><a routerLink="" routerLinkActive="active">Click here to go back to Hero List</a></p>
<h1>Hero Details page</h1> -->
<div *ngIf="hero" >
<!-- the hero details that will be displayed upon clicking on name -->
<h2 >{{hero.name | uppercase}} Details</h2>
<div><span>Id: </span>{{hero.id}}</div>
<div><span>Level: </span>{{hero.level}}</div>
<div><span>Class: </span>{{hero.class}}</div>
</div>
hero-details.components.ts
import { Component, Input, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router'
import { Hero } from '../types/Hero';
#Component({
selector: 'app-hero-details',
templateUrl: './hero-details.component.html',
styleUrls: ['./hero-details.component.css']
})
export class HeroDetailsComponent implements OnInit {
#Input() hero: Hero | undefined;
constructor(
private route: ActivatedRoute,
) { }
ngOnInit(): void {
}
}

Does your backend return anything? Show us the backend and show what it is returning. Also I would advise to refactor ngOnInit() to not be async function. Refactor logic away from ngOnInit to a new function and have ngOnInit call out the said new function.
Otherwise it looks like it should work.
ngOnInit(): void {
this.getHeroes();
}
async getHeroes() {
// Gets a list of heroes to display
this.heroes = await this.backend.getHeroes();
}
Example: https://stackblitz.com/edit/heroes-list-fjdq6g?file=src%2Fapp%2Fheroes%2Fheroes.component.ts

Related

How can I route from a component to a section of another component in Angular?

I have two components and they are situtated in the different routes. In the Second component There is a div. I want to route from first component to the div of the second component. Yes, It is simple to route the second component. But I want to scroll be top of the div.
Thank you for answers.
This tag is declared in component1
<a [routerLink] = "['/c2']" fragment="c2id"> Link </a>
Here are the component2 changes
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-c2',
templateUrl: './c2.component.html',
styleUrls: ['./c2.component.css']
})
export class C2Component implements OnInit {
private fragment: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.fragment.subscribe(fragment => {
this.fragment = fragment;
});
}
ngAfterViewInit(): void {
try {
document.querySelector('#' + this.fragment).scrollIntoView();
} catch (e) {}
}
}
And your component2 html will be like this
<p style="height: 800px;">
c2 works!
</p>
<hr>
<div id="c2id" style="height: 500px;">
The div with c2id
</div>
Here is the updated and working stackblitz
https://angular-fragment-example.stackblitz.io
I think you are looking for Fragments.
Official Docs : Angular Docs- Query Params and Fragments
Examples:
Manual Navigation
in c1.html
<a [routerLink] = "['/c2']" [fragment]="c2id"> Link </a>
in c2.html
<div id="c2id">content</div>
Programatic Navigation
in c1.ts
private fragmentSetDynamically: string;
constructor(private router: Router){}
onClickButton(){
this.router.navigate(['/c2'], {fragment: fragmentSetDynamically});
}
Getting the fragment :
in c2.ts
private fragment: string;
constructor(private activatedRoute: ActivatedRoute){}
ngOnInit(){
this.fragment = this.activatedRoute.snapshot.fragment;
}

How to make an HTTP request on page load in Angular (works with a button)

I am trying to make an HTTP request to an API when my page loads and have the text of the response display on the screen. I am using Angular framework.
I currently have it working as I desire when you press a button. I want the exact functionality I have with the button, but for it to happen automatically on page load.
//TypeScript
import { Component, OnInit } from '#angular/core';
import { Observable, Subscription } from 'rxjs';
import { HttpClient, HttpParams, HttpHeaders } from '#angular/common/http';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
posts: Observable<String[]>;
constructor(private http:HttpClient) {
}
public getPosts() {
this.posts = this.http.get<any[]>('https://jsonplaceholder.typicode.com/posts');
}
ngOnInit() {
}
}
HTML
<button (click) = "getPosts()">GetPosts</button>
<div *ngFor="let post of posts | async">
Name
{{post | json}}
</div>
This gives me a page with a button. When I press the button I get the information from the API. I want the API to give me the information right away when the page is loaded.
Just Invoke the method on ngOnInit
ngOnInit() {
this.getPosts()
}
or You can do like below also
export class ProfileComponent implements OnInit {
posts: Observable<String[]> = this.http.get<any[]>('https://jsonplaceholder.typicode.com/posts');
constructor(private http:HttpClient) {
}
}

How to programmatically open an Accordion in Angular 4

I am working in an Angular 4 application in this I have the same accordion in two components .What I want to do is if a user clicked on the accordion from first component I want to get the index of the selected accordion and pass it to second component there I set the selected accordion to open and show the contents in it on page load (without click on it )
currently I have single accordion in first component ,If multiple accordion binds from API the selected index will changed dynamically.
Here is my code :
https://stackblitz.com/edit/angular-bootstrap-carousel-dynamic2-tsrt1w?file=app%2Fone%2Fone.component.html
You can pass id using route params, look into below sample codes for example:
one.component.html
<h5>One Component</h5>
<h6>Categories</h6>
<div class="accordion col-sm-12" id="accordion1" *ngFor='let data of dropdownData; let i=index'>
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle h6" data-toggle="collapse" routerLink="/two/{{i}}" data-parent="#accordion1" href="#collapseTwo + i">
{{data?.CAMD_ENTITY_DESC}}
</a>
</div>
</div>
</div>
<br>
app routes
const appRoutes: Routes = [
{path:'one',component:OneComponent},
{path:'two/:id',component:TwoComponent}]
two.component.ts
import { Component, OnInit, ViewChildren, QueryList, AfterViewInit, ElementRef } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { CartdataServiceService } from '../cartdata-service.service';
declare var $:any;
#Component({
selector: 'app-two',
templateUrl: './two.component.html',
styleUrls: ['./two.component.css']
})
export class TwoComponent implements OnInit,AfterViewInit {
dropdownData: any;
id:string;
#ViewChildren('accordian') components:QueryList<ElementRef>;
constructor( private route: ActivatedRoute, private CartdataService: CartdataServiceService) {}
ngOnInit() {
this.CartdataService.get_New_Products().subscribe(
data => {
this.dropdownData = data;
console.log(this.dropdownData);
});
this.id = this.route.snapshot.paramMap.get('id');
}
ngAfterViewInit(){
// print array of CustomComponent objects
this.components.changes.subscribe(() => {
let elem=this.components.toArray()[this.id];
$(elem.nativeElement).trigger("click");
console.log(elem);
});
}
}
Now you can use id and select the required index for accordion.
Example Link

Angular - passing data in to component

I'm still somewhat new to Angular development.
I'm building a website for a construction company, where I'm using three different components on one page. Here's a basic outline of what the page currently has, and what I want it to do:
The projects component contains a list of projects on the right side
of the page. Out of all those projects, the one that is selected
shows up on the left side of the page (projectdetail.component.html)
The user may click on any of the projects on the right side of the page to change the selected project. This new one will show up on the left side of the page in projectdetail.component.html
In the projectdetail component, there is a mini gallery of other
images that the user can click on in order to change the main image
that displays (like a typical photo slideshow).
THE PROBLEM I'M HAVING RIGHT NOW: The main image in projectdetail.component no longer changes whenever I click on any of the projects in the right panel (all of the other data, including the address, images in the slideshow, etc. do update as they should); they only time it changes is when I click on any of the images in the gallery in the projectdetail component. A solution I've proposed is to try to make updates to 'mainImage' (See code below) whenever you click on a project on the right panel, which it does not do. I am not sure how to accomplish this.
My Project class:
export class Project {
id: number;
address: string;
images: string[];
price: string;
featured: boolean;
description: string;
}
My Project Service:
import { Injectable } from '#angular/core';
import { Project } from '../shared/project';
import { PROJECTS } from '../shared/projects';
#Injectable()
export class ProjectService {
constructor() { }
getProjects(): Project[] {
return PROJECTS;
}
getProject(id: number): Project {
return PROJECTS.filter((project) => (project.id === id))[0];
}
}
Not including one of the components, which is irrelevant to the question, they are as follows:
projects.component.html
<div class="container"
fxLayout.sm="row"
fxLayout.xs="column">
<div fxFlex=60>
<app-projectdetail [project]="selectedProject" ></app-projectdetail>
</div>
<div fxFlex=40 *ngIf="projects">
<mat-grid-list cols="1" rowHeight="300px">
<mat-grid-tile *ngFor="let project of projects" (click)="onSelect(project)">
<img src="{{ project.images[0] }}">
<mat-grid-tile-footer>
<h1 mat-line ngDefaultControl>{{ project.address | uppercase }}</h1>
</mat-grid-tile-footer>
</mat-grid-tile>
</mat-grid-list>
</div>
</div>
projects.component.ts
import { Component, OnInit, Inject, Optional } from '#angular/core';
import { MatDialog } from '#angular/material';
import { ProjectDialogComponent } from '../project-dialog/project-dialog.component';
import { Project } from '../shared/project';
import { ProjectService } from '../services/project.service';
#Component({
selector: 'app-projects',
templateUrl: './projects.component.html',
styleUrls: ['./projects.component.scss']
})
export class ProjectsComponent implements OnInit {
projects: Project[];
selectedProject: Project;
image: String;
mainImage: String;
constructor(private projectService: ProjectService, private dialog: MatDialog) { }
ngOnInit() {
this.projects = this.projectService.getProjects();
this.selectedProject = this.projectService.getProject(0);
}
onSelect(project: Project) {
this.selectedProject = project;
this.mainImage = project.images[0];
}
}
projectdetail.component.html
<div class="container"
fxLayout="row"
fxLayoutGap="10px">
<div fxFlex=100>
<mat-card *ngIf="project">
<img src="{{ mainImage }}" (click)="openDialog(project)">
<mat-card-header>
<mat-card-title><h1>{{ project.address | uppercase }}</h1></mat-card-title>
</mat-card-header>
<div id="preWrapper">
<div id="imageWrapper">
<div id="imageContainer" *ngFor="let image of project.images">
<img src="{{ image }}" (click)="changeImage(image)" />
</div>
</div>
</div>
<mat-card-content>
<p>{{ project.description }}</p>
</mat-card-content>
</mat-card>
</div>
</div>
projectdetail.component.ts
import { Component, OnInit, Inject, Input } from '#angular/core';
import { MatDialog } from '#angular/material';
import { ProjectDialogComponent } from '../project-dialog/project-dialog.component';
import { Project } from '../shared/project';
import { ProjectService } from '../services/project.service';
#Component({
selector: 'app-projectdetail',
templateUrl: './projectdetail.component.html',
styleUrls: ['./projectdetail.component.scss']
})
export class ProjectdetailComponent implements OnInit {
#Input()
project: Project;
projects: Project[];
selectedProject: Project;
image: String;
mainImage: String;
constructor(private projectService: ProjectService, private dialog: MatDialog) { }
ngOnInit() {
this.mainImage = this.projectService.getProject(0).images[0];
}
openDialog(project: Project): void {
let dialogRef = this.dialog.open(ProjectDialogComponent, {
data: {
address: this.project.address,
images: this.project.images
}
})
}
changeImage(image: String) {
this.mainImage = image;
}
}
you write ngOnChange event in you detail component and then raise event for changing image in you main page ,
ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
if (propName === 'project') {
Promise.resolve(null).then(() =>
{raise event or write code toc hange main page image});
}
}
}

Angular 4 SideNav does not display

I am trying to insert a Material Sidenav into my component. For some reason, when I try to insert a basic sidenav it does not show anything -- main content, button to trigger sidenav, nor side content. I have imported both { MatSidenavModule } and { MatButtonModule} . However, if I comment out the mat-sidenav portion of the code, then the button will appear, so I think it may be an issue with the way I am setting up mat-sidenav.
Here is my HTML code:
<!--product.component.html-->
<h2>Store Inventory</h2>
<div class = "products">
Remaining: {{displayRemain}}
Requested: {{displayRequest}}
{{title}}
Total: {{cartTotal}}
</div>
<div>
<mat-sidenav-container class="example-container">
<mat-sidenav #sidenav class="example-sidenav">
Sidenav opened!
</mat-sidenav>
<div class="example-sidenav-content">
<button type="button" mat-button (click)="sidenav.open()">
Open sidenav
</button>
</div>
</mat-sidenav-container>
</div>
Here is the corresponding product.component.ts file: (I took out some functions which I commented out in the HTML)
import { Component, OnInit } from '#angular/core';
import { Product } from '../product';
import { ProductService } from '../product.service';
import {element} from 'protractor';
#Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
products: Product[];
displayRemain: number;
displayRequest: number;
title: string;
prevRemain: number;
cartTotal: number;
constructor(private productService: ProductService) {
}
ngOnInit() {
this.title = 'Not Hello';
this.displayRemain = 0;
this.displayRequest = 0;
this.prevRemain = 10;
this.cartTotal = 0;
this.getProducts();
}
EDIT: got the answer, I didn't install the angular material animation library. Leaving this up in case others have the same problem.
Try to use sidenav.toggle() instead sidenav.open()
Regards