How to display specific row data in dialog box using Angular 9? - html

Below I have written on for the open dialog method in the component
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { AuthService } from '../_services/auth.service';
import { AlertifyService } from '../_services/alertify.service';
import { ModalService } from '../_modal';
#Component({
selector: 'app-view-customer',
templateUrl: './ViewCustomer.component.html',
styleUrls: ['./ViewCustomer.component.css']
})
export class ViewCustomerComponent implements OnInit {
bookings: any;
bodyText: string;
constructor(private authService: AuthService, private http: HttpClient, private alertify: AlertifyService
, private modalService: ModalService) { }
ngOnInit() {
this.Bookings();
console.log(this.Bookings());
this.bodyText = 'This text can be updated in modal 1';
}
openModal(id: string) {
this.modalService.open(id);
}
closeModal(id: string) {
this.modalService.close(id);
}
Bookings() {
this.http.get('http://localhost:5000/api/Book/').subscribe(response => {
this.bookings = response;
console.log(this.bookings);
}, error => {
console.log(error);
});
}
}
Below I've included the template.
I'm trying to make the first button show the first data displayed the second button displaying the second data.
Currently, I have the dialog displaying all the data whenever the user clicks on both of the buttons.
<h1>List of reservations</h1>
<div>
<p *ngFor= "let booking of bookings">
{{booking.customers.firstName}},
{{booking.id}}, {{booking.date}},
{{booking.id}},{{booking.tableNumber}}
{{booking.id}},{{booking.additionalInfo}}
</p>
<button (click)="openModal('custom-modal-2')">Open Modal 2</button>
<button (click)="openModal('custom-modal-3')">Open Modal 3</button>
</div>
<p>
</p>
<div>
<p>{{bodyText}}</p>
<button (click)="openModal('custom-modal-1')">Open Modal 1</button>
<button (click)="openModal('custom-modal-2')">Open Modal 2</button>
</div>
<jw-modal id="custom-modal-1">
<h1>A Custom Modal!</h1>
<p>Home page text: <input type="text" [(ngModel)]="bodyText" /></p>
<button (click)="closeModal('custom-modal-1');">Close</button>
</jw-modal>
<jw-modal id="custom-modal-2">
<h1 style="height:100px">A Tall Custom Modal!</h1>
<p *ngFor= "let booking of bookings">
{{booking.id}}, {{booking.date}},
{{booking.id}},{{booking.tableNumber}}
{{booking.id}},{{booking.additionalInfo}}
</p>
<button (click)="closeModal('custom-modal-2');">Close</button>
</jw-modal>
<jw-modal id="custom-modal-3">
<h1 style="height:100px">second one hhaha</h1>
<button (click)="closeModal('custom-modal-3');">Close</button>
</jw-modal>
<!-- <p *ngFor= "let customers of bookings ">
{{booking.id}}, {{bookings.firstName}}
{{booking.customerId}}, {{booking.FirstName}}
</p>
<p>hello</p> -->
<p>{{ bookings[0]?.customers[0]?.Color?.Name }}</p>
what do I need to change below in the modal service?
import { Injectable } from '#angular/core';
#Injectable({ providedIn: 'root' })
export class ModalService {
private modals: any[] = [];
selectedBooking: any;
bookings: any;
add(modal: any) {
// add modal to array of active modals
this.modals.push(modal);
}
remove(id: string) {
// remove modal from array of active modals
this.modals = this.modals.filter(x => x.id !== id);
}
open(id: string) {
// open modal specified by id
const modal = this.modals.find(x => x.id === id);
modal.open();
}
close(id: string) {
// close modal specified by id
const modal = this.modals.find(x => x.id === id);
modal.close();
}
}
if you look at the image it shows that whenver i click on open modal button 2 it should only show emma's detail in the dialog box but it shows Tommy's details with Emma's detail.
image 1
iamge 2

That is because you are showing all the records in the modal. Here you are looping through all of them (bookings) in displaying them:
<jw-modal id="custom-modal-2">
<h1 style="height:100px">A Tall Custom Modal!</h1>
<p *ngFor= "let booking of bookings">
{{booking.id}}, {{booking.date}},
{{booking.id}},{{booking.tableNumber}}
{{booking.id}},{{booking.additionalInfo}}
</p>
<button (click)="closeModal('custom-modal-2');">Close</button>
</jw-modal>
If ModalService is something you created in your project (I mean not from a node module) then you can declare a variable there, set that when the button is clicked and use it in the modal. Something like,
export class ModalService {
selectedBooking: any;
.
.
}
Component HTML
<button (click)="openModal(booking)">Open Modal</button>
Component TS
openModal(booking: any) {
this.modalService.selectedBooking = booking;
this.modalService.open();
}
Modal HTML
<jw-modal id="custom-modal-2">
<h1 style="height:100px">A Tall Custom Modal!</h1>
{{selectedBooking.id}}, {{selectedBooking.date}},
{{selectedBooking.id}},{{selectedBooking.tableNumber}}
{{selectedBooking.id}},{{selectedBooking.additionalInfo}}
</p>
<button (click)="closeModal('custom-modal-2');">Close</button>
</jw-modal>

Related

Sharing data between two pages upon clicking button Angular

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

How to get respective modal on click of a link comes from loop using ng bootstrap in angular 8

I have few li tags whose data comes from loop. There is also a link 'images', When you click it, it should open respective modal like For 'Cat' row cat image should come,For 'Architecture' row Architecture image should come,For 'baboon' row baboon image should come. For now only cat link is coming on click of 'image' link.you can use these link for particular image
Architecture - https://homepages.cae.wisc.edu/~ece533/images/arctichare.png
Baboon - https://homepages.cae.wisc.edu/~ece533/images/baboon.png , Here is the code below with demo url
https://stackblitz.com/edit/angular-327axj?file=src%2Fapp%2Fapp.component.ts
app.component.html
<hello name="{{ name }}"></hello>
<div>
<pre>
</pre>
<ul>
<li *ngFor="let item of statusdata" (click)="toggleActive(item, !item.active)">
<span>{{item.id}}</span>
<span>{{item.name}}</span>
<button class="btn btn-lg btn-outline-primary" (click)="open(content)">Image</button>
</li>
</ul>
</div>
<ng-template #content let-modal>
<div class="modal-header">
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<img style="width:100%" src="https://homepages.cae.wisc.edu/~ece533/images/cat.png" />
</div>
</ng-template>
<hr>
app.component.ts
import { Component } from '#angular/core';
import {NgbModal, ModalDismissReasons} from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
statusdata: any;
closeResult: string;
constructor(private modalService: NgbModal) {}
ngOnInit() {
this.statusdata = [
{ id: 1, name: "Cat"},
{ id: 2, name: "Architecture"},
{ id: 3, name: "baboon" },
];
this.statusdata.forEach(item => {
this.getCacheItemStatus(item);
});
}
toggleActive(item, activeStatus = true) {
item.active = activeStatus;
localStorage.setItem(`item:${item.id}`, JSON.stringify(item));
}
getCacheItemStatus(item) {
const cachedItem = localStorage.getItem(`item:${item.id}`);
if (cachedItem) {
const parse = JSON.parse(cachedItem); // Parse cached version
item.active = parse.active; // If the cached storage item is active
}
}
open(content) {
this.modalService.open(content, {ariaLabelledBy: 'modal-basic-title'}).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
});
}
private getDismissReason(reason: any): string {
if (reason === ModalDismissReasons.ESC) {
return 'by pressing ESC';
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
return 'by clicking on a backdrop';
} else {
return `with: ${reason}`;
}
}
}
Right now, you're hard coding the image url in the modal to use the cat image as follows:
<img style="width:100%" src="https://homepages.cae.wisc.edu/~ece533/images/cat.png" />
which causes the same image to be displayed in all modals.
You could maintain a variable for the image name and set it to the required image when you open the modal.
While calling the open method, pass the item name which will act as the image source:
<button class="btn btn-lg btn-outline-primary" (click)="open(content, item.name)">Image</button>
and handle it in the typescript class:
open(content, source) {
this.imageSource = source;
...
where imageSource is just a variable:
imageSource: any;
And now the updated image URL will be:
<img style="width:100%" src="https://homepages.cae.wisc.edu/~ece533/images/{{imageSource}}.png" />
Here is the updated stackblitz:
https://stackblitz.com/edit/angular-bslf3q

Trigger button click from another component button in angular 4

I have component A button which displays form on click and component B button to show name. I want to trigger ComponentA button and display form when componentB button is clicked
componentA HTML
<section>
<button (click)="toggle()">Click Here To Search</button>
<div *ngIf="!showInput">
<input type="text"/><br/>
<button type="submit">Submit</button>
<button>Cancel</button>
</div>
</section>
componentA TS
showInput = true;
//...
toggle() {
this.showInput = !this.showInput;
}
componentB HTML
<button (click)="toggleText()">Add Fruit</button>
<div *ngIf="showText">Apple</div>
I have created an example.Please use this link
Example Link
Well in that case make use of rxjs BehaviorSubject in a service, so that your entire application can make use of the variable and will update accordingly, like below
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class Service {
toggle: BehaviorSubject<boolean> = new BehaviorSubject(false);
toggle$ = this.toggle.asObservable();
}
and in your my text component
toggleText() {
this.showText = !this.showText;
this.service.toggle.next(this.showText)
}
and in your FormsComponent
showInput;
ngOnInit() {
this.service.toggle$.subscribe(
toggle => this.showInput = toggle
)
}
Working demo

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

How to show a modal window with *ngFor for multiple items in Angular4

I am experimenting with a simple project in order to learn Angular and I have currently a problem:
I am loading some dummy people with a service and then displaying boxes with the persons' names. And when I click on a box, a modal popup comes up and displays more info about this person - currently just a string of a short bio.
The problem is that I have an *ngFor to itterate over the persons and then what I suspect happens is that I also create a modal window for each and every one. Then the modal window does not know which is the currently selected person, so it just shows me the bio of the the first person from the list ...
So the question is how do I make it work for each currently selected person; i.e. when I click on person with id = 3, the modal displays the bio of that same person.
I guess this needs to be done programatically, that's why I am not using stuff like href="#modal" data-toggle="modal" to bind the modal window to an event.
Any better ideas?
Here is what I have: PersonComponent
#Component({
selector: 'person',
templateUrl: './person.component.html'
})
export class PersonComponent implements OnInit {
people: Person[];
selectedPerson: Person;
personBio: string;
#ViewChild('personModal') private personModal;
constructor(private router: Router,
private activatedRoute: ActivatedRoute,
private stateService: StateService,
private personService: PersonService) {
}
ngOnInit() {
this.loadAllPeople();
}
private loadAllPeople() {
this.personService.getPeople()
.subscribe(result => {
this.people = result;
}, error => {
this.console.log(error);
});
}
goToPersonEditComponent(person: Person) {
this.stateService.person = this.selectedPerson;
this.router.navigate(['../' + FrontendRoute.EDIT], {relativeTo: this.activatedRoute});
}
loadPersonBioModal(person: Person) {
if (this.personModal) {
this.selectedPerson = person;
jQuery(this.personModal.nativeElement).modal('show');
}
this.personService.getPersonBio(person.id)
.subscribe((bio) => this.personBio = bio);
}
closeModal() {
if (this.personModal) {
jQuery(this.personModal.nativeElement).on('hide.bs.modal', (evt) => {
this.selectedPerson = null;
});
}
}
}
Person edit component: I am not showing the whole thing, to keep it simple, but the idea is that I get the selected person and from the id attribute I can edit it's bio.
#Component({
selector: 'person-edit',
templateUrl: './person-edit.component.html'
})
export class PersonEditComponent implements OnInit {
person: Person;
constructor(private router: Router,
private activatedRoute: ActivatedRoute,
private packService: PackService,
private orderService: PackOrderService,
private stateService: StateService,
private eventBusService: EventBusService,
private loggingService: LoggingService) {
}
ngOnInit() {
this.person = this.stateService.person;
}
}
A simple service for passing stuff between components (I wanted it this way instead of with #Input):
#Injectable()
export class StateService {
private _person: Person;
constructor() {
}
get person() {
return this._person;
}
set person(person: Person) {
this._person = person;
}
clear() {
this._person = null;
}
}
And here is my template where I have the modal:
<div>
<h2 class="h2">People</h2>
<div class="item-collection item-collection-3-columns item-collection-tablet item-collection-wide">
<div class="items">
<article *ngFor="let person of people"
(click)="loadPersonBioModal(person)">
<div class="content">
<div class="headline">
<span>{{person.name}}</span>
<span data-original-title="Show Person's Bio"></span>
</div>
</div>
</article>
</div>
</div>
</div>
<div #personModal tabindex="-1" role="dialog" class="modal fade" style="display: none;"
aria-hidden="true">
<div role="document" class="modal-dialog modal-lg modal-has-header modal-has-footer">
<div class="modal-content">{{personBio}}</div>
<div class="modal-footer">
<div class="section-btns">
<button type="button" data-dismiss="modal"
(click)="closeModal()">Close
</button>
<button type="button" data-dismiss="modal"
(click)="goToPersonEditComponent(selectedPerson)">Edit Bio
</button>
</div>
</div>
</div>
</div>
And here is the PersonService, which makes a http call:
#Injectable()
export class PersonService {
constructor(private http: HttpClient, private router: Router) {
}
getPeople(): Observable<Person[]> {
return this.http.get<Person[]>(BackendRoute.PERSON_DATA)
.catch(error => {
return Observable.throw(new Error('An unexpected error occurred' + error));
});
}
getPersonBio(): Observable<Person> {
return this.http.get<Person>(BackendRoute.PERSON_BIO)
.catch(error => Observable.throw(new Error(error)));
});
}
}
Okay, given your comments, I think what's happening likely is you are showing your modal before the getPersonBio() returns the person information and assigns it to personBio property.
A likely easy way to tackle this would be to use the async pipe to asynchronously update the values for you. Try this,
in your PersonComponent,
personBio: Observable<Person>;
loadPersonBioModal(person: Person) {
if (this.personModal) {
this.selectedPerson = person;
// directly assigning the returned observable of Person type and let async pipe do its magic in the template
this.personBio = this.personService.getPersonBio(person.id);
jQuery(this.personModal.nativeElement).modal('show');
}
}
In your template,
<div class="modal-content">{{ personBio | async }}</div>
You have to import CommonModule for this to work as probably seen from the docs. Also, using jQuery methods directly in angular is not recommended unless there is no other supported way of doing it.
Let me know if that helped.