Prime-NG Confirm Dialog does not work in a service - javascript

I just noticed a strange behavior of PrimeNG's Confirm Dialog. In test.component.html, there is an Input Field. It checks if the typed in value is greater than 150. If it is greater, then a Confirm Button shows up below the Input Field ("Please confirm"). Clicking it shows a Dialog with Yes and No.
The Confirm Button shall vanish after choosing either Yes or No.
Now here's the problem:
Vanishing only works if the confirm method is called directly in test.component.ts. I'd like to extract it into a service (customConfirmation.service.ts), but the vanishing does not work there. Do you know why? I have got absolutely no idea. ("this.messagesWeightTest" and the button vanishing do not work.)
test.component.html
<div class="p-col-12 p-md-6 p-lg-5">
Weight:
<div class="ui-inputgroup">
<input pInputText type="number" id="weight" name="weight" [(ngModel)]="newTest.testWeight"
placeholder="---">
<span class="ui-inputgroup-addon">kg</span>
</div>
<div *ngIf="validateIfWeightOutsideRange()">
<div>
<p-confirmDialog key="confirmWeightTest"></p-confirmDialog>
<button type="button" (click)="confirmWeightTest()" pButton icon="pi pi-check"
label="Please confirm!">
</button>
<p-messages [value]="messagesWeightTest"></p-messages>
</div>
</div>
</div>
It seems that the whole "accept" and "reject" do not work:
customConfirmation.service.ts
import {Injectable} from '#angular/core';
import {SessionService} from "./session.service";
import {ConfirmationService, Message} from "primeng/api";
#Injectable()
export class CustomConfirmationService {
messagesWeightTest: Message[] = [];
weightConfirmed: boolean = false;
constructor(private confirmationService: ConfirmationService) {}
confirmWeightTest() {
this.confirmationService.confirm({
message: 'Are you sure?',
header: 'Confirmation',
icon: 'pi pi-exclamation-triangle',
key: 'confirmWeightTest',
accept: () => {
this.messagesWeightTest = [{
severity: 'info', summary: 'Confirmed', detail: 'The input is correct.'}];
this.weightConfirmed = true;
},
reject: () => {
this.sessionService.newTest.testWeight = null;
this.weightConfirmed = true;
}
});
}
}
test.component.ts just calls the confirmation method from the service:
test.component.ts
import {Component, Injectable, Input, OnInit} from '#angular/core';
import {ConfirmationService, Message, SelectItem} from "primeng/api";
import {trigger, state, style, transition, animate} from '#angular/animations';
import {FormBuilder, FormControl, FormGroup, Validators} from "#angular/forms";
import {CustomConfirmationService} from "../services/customConfirmation.service";
import {ValidationService} from "../services/validation.service";
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
#Injectable()
export class TestComponent implements OnInit {
constructor(private fb: FormBuilder,
private customConfirmationService: CustomConfirmationService,
private confirmationService: ConfirmationService,
private validationService: ValidationService) {
}
ngOnInit() {}
// Confirmations for ConfirmDialogs
confirmWeightTest() {
this.customConfirmationService.confirmWeightTest();
}
// Validations for ConfirmDialogs --> work!
validateIfWeightOutsideRange() {
return !!this.validationService.validateIfWeightOutsideRange();
}
Again, if I copy and paste confirmWeightTest() from customConfirmation.service.ts into test.component.ts, everything works fine. I also tested this with another project.
I would be glad if you could tell me whats going on here.
Please also refer to Prime-NG Confirm Dialog: Hide the Button after Confirmation
I implemented everything as recommended in the answer to this question. Thanks!

I created a demo and everything work as well
You need to declare CustomConfirmationService in providers and update
<p-messages [(value)]="messagesWeightTest"></p-messages>
to
<p-messages [(value)]="customConfirmationService.messagesWeightTest"></p-messages>

Related

How to save Dynamic Form Answer in Angular?

I'm trying to save some data that I display dynamic in the browser.This is my HTML where I iterate trough a list of exam questions dragged from the database and display them dynamic in browser.
<div mat-dialog-content [formGroup]="examform">
<div *ngFor="let exam_question of exam_questions;let i = index">
<mat-label >{{exam_question.questionTitle}}</mat-label><br>
<mat-radio-group aria-label="Image Position">
<mat-radio-button id="{{'examItem1'+i}}" value="1" >{{exam_question.questionItem1}}</mat-radio-button>
<mat-radio-button id="{{'examItem2'+i}}" value="2">{{exam_question.questionItem2}}</mat-radio-button>
<mat-radio-button id="{{'examItem3'+i}}" value="3">{{exam_question.questionItem3}}</mat-radio-button>
<mat-radio-button id="{{'examItem4'+i}}" value="4">{{exam_question.questionItem4}}</mat-radio-button>
</mat-radio-group>
</div>
<div>
<button class="button-course" mat-raised-button color="primary" (click)="submitAnswer()">Submit</button>
How can I use Form from Angular Typescript to save what the user responds to this form? For example what was the answer for the first question, for the second question and so on. I tried to put on every Form Controls and id, but I don t know how to go further.This is the form from Typescript.
import { registerLocaleData } from '#angular/common';
import { Component, OnInit } from '#angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
import { ActivatedRoute, Router } from '#angular/router';
import { Questions } from '../models/questions';
import { SharedService } from '../service/shared.service';
#Component({
selector: 'app-quiz-exam-page',
templateUrl: './quiz-exam-page.component.html',
styleUrls: ['./quiz-exam-page.component.css']
})
export class QuizExamPageComponent implements OnInit {
examID:any;
examform!: FormGroup;
examData: any;
userDetails:any;
examFormArray=new FormArray([new FormControl('',Validators.required)]);
constructor(private formBuilder:FormBuilder,private router:Router,private sharedService:SharedService,private route:ActivatedRoute) { }
exam_questions:Questions[]=[];
ngOnInit(): void {
this.route.queryParams
.subscribe(params => {
this.examID = params['examID'];
}
);
this.examform=this.formBuilder.group({
examTitle:this.formBuilder.array([],Validators.required),
examItem1:this.formBuilder.array([],Validators.required),
examItem2:this.formBuilder.array([],Validators.required),
examItem3:this.formBuilder.array([],Validators.required),
examItem4:this.formBuilder.array([],Validators.required)
});
if(localStorage.getItem('token')==null)
this.router.navigate(['login'])
this.sharedService.getUserProfile().subscribe(res=>{
this.userDetails=res;
this.sharedService.getExamQuestionsByID(this.examID).subscribe(data=>{
this.exam_questions=data;
},err=>{
console.log(err)
});
},(err:any)=>{
console.log(err);
},
);
}
submitAnswer(submitData:any)
{
this.examData = submitData;
console.log(this.examData)
}
}
This is the generated form. And as you can see, I need the answer for every question and after that I click submit, it will save the answer for every question, but I need to know to what question it was the answer, that is why I put the Id in the HTML.
This is my question array. It is an interface:
export interface Questions {
questionID:number;
questionCourseID:number;
questionTitle:string;
questionTopic:string;
questionPoints:string;
questionDifficulty:string;
questionItem1:string;
questionItem2:string;
questionItem3:string;
questionItem4:string;
qustionAnswers:string;
}
To your HTML-File I would add an NgSubmit and also add the type submit to your button. In the ngSubmit you give the value of your form (examForm) to the function (saveForm) in the Typescript-File. Instead I would delete the (click) event on your button.
HTML-File
<div mat-dialog-content [formGroup]="examform" (ngSubmit)="saveForm(examform.value)">
<div *ngFor="let exam_question of exam_questions;let i = index">
<mat-label >{{exam_question.questionTitle}}</mat-label><br>
<mat-radio-group aria-label="Image Position">
<mat-radio-button id="{{'examItem1'+i}}" value="1" >{{exam_question.questionItem1}}</mat-radio-button>
<mat-radio-button id="{{'examItem2'+i}}" value="2">{{exam_question.questionItem2}}</mat-radio-button>
<mat-radio-button id="{{'examItem3'+i}}" value="3">{{exam_question.questionItem3}}</mat-radio-button>
<mat-radio-button id="{{'examItem4'+i}}" value="4">{{exam_question.questionItem4}}</mat-radio-button>
</mat-radio-group>
</div>
<div>
<button type="submit" class="button-course" mat-raised-button color="primary">Submit</button>
If you want, that your button only is pressable if the inputs are valid (for example every required fields are filled out). You can add following code to your button:
<button [disabled]="!examForm?.valid" type="submit" class="button-course" mat-raised-button color="primary">Submit</button>
In your Typescript-File, you should write a function with the same name as you specified in (ngSubmit)="functionName". In my example, the function Name is saveData(). Then you write the value from the Form to a variable with the datatype any.
So your TS-File would look something like:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators, FormControl } from '#angular/forms';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
examForm!: FormGroup;
examData: any;
constructor(
private formBuilder: FormBuilder,
) { }
ngOnInit(): void {
this.examForm=this.formBuilder.group({
examTitle:this.formBuilder.array([],Validators.required),
examItem1:this.formBuilder.array([],Validators.required),
examItem2:this.formBuilder.array([],Validators.required),
examItem3:this.formBuilder.array([],Validators.required),
examItem4:this.formBuilder.array([],Validators.required)
});
}
showPreview(submitData: any) {
this.examData = submitData;
console.log(this.examData)
}
}

Remove button on list item/array in Angular 12

I'm trying to remove a list item with the click button, tried various options but it seems to not work. Hope you can help me out with this.
On click i want to remove a list item from my users array. I will link the Typescript code alongside with the HTML.
//Typescript code
import { UsersService } from './../users.service';
import { Component, EventEmitter, Input, OnInit, Output } from '#angular/core';
import { Iuser } from '../interfaces/iuser';
#Component({
selector: 'tr[app-table-row]',
templateUrl: './table-row.component.html',
styleUrls: ['./table-row.component.css']
})
export class TableRowComponent implements OnInit {
#Input() item!: Iuser;
#Output() userDeleted = new EventEmitter();
removeUser(item: any) {
this.userDeleted.emit(item);
}
constructor() {}
ngOnInit(): void {}
}
<th scope="row">{{item.id}}</th>
<td>{{item.name}}</td>
<td>{{item.lastname}}</td>
<td>{{item.city}}</td>
<td> <button class="btn btn-sm" (click)="removeUser(item)">remove</button></td>
As #Priscila answered, when the button is clicked, you should only emit the action and let the parent component control the respective method i.e. delete or add.
Because that way, it will be easy for the data to be manipulated and handle the component's lifecycle.
Never keep the dead ends running on the app.
Happy Coding :)

Angular 8. My view is not updated on model change

In my application I'm having a big trouble 'refreshing' the view after model for that view was updated. Mainly when API call is resolved and its response's data should be published on that view.
This my component management.component.ts ts file (I've removed code not important to this issue):
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
#Component({
selector: 'app-service',
templateUrl: './service.component.html',
styleUrls: ['./service.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ServiceComponent implements OnInit {
serviceForm: FormGroup;
notificationStatus: boolean;
constructor(
private http: HttpClient
) { }
ngOnInit() {
this.buildServiceForm();
// Example endpoint response: { active: false }
this.getNotificationInfo().subscribe((data: object) => {
this.notificationStatus = data['active'];
})
}
submit()
{
this.notificationStatus = !this.notificationStatus;
}
buildServiceForm()
{
this.serviceForm = this.formBuilder.group({
message_de: ['', { validators: [Validators.required] }],
message_en: ['', { validators: [Validators.required] }]
});
}
getNotificationInfo() {
return this.http.get(this.apiPath + 'service/notifications');
}
}
And this is the part in HTML that is responsible for displaying that model (button tag):
<form [formGroup]="serviceForm" (ngSubmit)="submit()">
<mat-form-field class="service__form__textarea">
<textarea matInput formControlName="message_de"></textarea>
</mat-form-field>
<mat-form-field class="service__form__textarea">
<textarea matInput formControlName="message_en"></textarea>
</mat-form-field>
<button>
<span *ngIf="notificationStatus == false"> Service Off </span>
<span *ngIf="notificationStatus == true"> Service On </span>
</button>
</form>
Whenever the button is clicked a form is submitted and the button's text should be updated (changed with use of ngIf) right away. But it only happens when I click randomly other objects on website.
I tried already with use of changeDetection: ChangeDetectionStrategy.OnPush but with no luck.
Here animation how it looks like in practice - after clicking the button its text changes only after clicking in textarea and not right after clicking the button:
gif animation of the behavior
Any one have any idea how can I refresh my view or what is the reason why my view behaves like that?
When using ChangeDetectionStrategy.OnPush the view will only update if object references change (see angular 2 change detection and ChangeDetectionStrategy.OnPush).
You may need to run the update to notificationStatus inside of zone using zone.run if that service is not already inside zone (the default Http client for example is already run inside zone automatically).
You also need to manually update the view using ChangeDetectorRef, or use the async pipe and observables which do this automatically.
You have two way to solve this:
Use CDR (quick & dirty)
Inject in your costructor ChangeDetectorRef
Call "cdr.markForCheck();" at the end of the submit method.
Transform your notificationStatus in a subject
In the class:
private notificationStatus$:Subject = new Subject(true);
In the submit method:
this.notificationStatus$:Subject.next(false);
In the html:
<span *ngIf="(notificationStatus$ | async ) == false"> Service Off </span>
PS: The $ in the variable name is a convention for obserable/subject

Angular 5.2 - bypass form validation for [routerLink] while keeping focus

I have completely reworded this question and included a complete code sample.
I have an intermittent issue where clicking the button sometimes shows the validation error message, instead of executing the router.nagivate command. Then, I have to click it a second to work. As I said, this is intermittent. The solution needs to include the focus behavior of the sample below, or an alternative way to focus on input html tags. Sometimes, I only have to click once. Why? And, how can I control this behavior so that it is not random?
I am posting two test components to demonstrate the issue. Any help would be greatly appreciated.
test.component.html
<form novalidate #f="ngForm">
<h2>Scan Part</h2>
<input id="partNum" type="number" class="form-control" required [correctPart]="orderShipDetail?.UPC" name="partNum" [(ngModel)]="model.partNum" #partNum="ngModel" #partNumRef />
<div *ngIf="partNum.invalid && (partNum.dirty || partNum.touched)" class="text-danger">
<p *ngIf="partNum.errors.required">PartNum is required.</p>
<p *ngIf="partNum.errors.correctPart">Please scan the correct part. </p>
</div>
<button type="button" (click)="onClickCartonPartButton()">Carton Parts</button>
</form>
test.component.ts
import { Component, OnInit, AfterViewChecked, ViewChild, ElementRef } from '#angular/core';
import { Router } from '#angular/router';
class TestForm {
constructor(
public partNum: string = '') {
}
}
#Component({
selector: 'test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit, AfterViewChecked {
#ViewChild('partNumRef') partNumRef: ElementRef;
model: TestForm = new TestForm();
public focusedElement: ElementRef;
constructor(
private router: Router,
private route: ActivatedRoute
) { }
ngAfterViewChecked() {
this.focusedElement.nativeElement.focus();
}
ngOnInit() {
this.focusedElement = this.partNumRef;
}
onClickCartonPartButton() {
try {
this.router.navigate(['/test2', 1006, 1248273, 1234]);
} catch (ex) {
console.log(ex);
}
}
}
test2.component.html
<a [routerLink]="['/test', 1006, 1248273, 1234, 5 ]">click this</a>
test2.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'test2',
templateUrl: './test2.component.html',
styleUrls: ['./test2.component.scss']
})
export class Test2Component implements OnInit {
constructor() { }
ngOnInit() {
}
}
Add these routes to the app.module.ts
{ path: 'test/:empId/:orderNumber/:licensePlate/:orderLine', component: TestComponent },
{ path: 'test2/:empId/:orderNumber/:licensePlate', component: Test2Component },
Set type="button" on the two hyperlinks to avoid submit
A button element with no type attribute specified represents the same
thing as a button element with its type attribute set to "submit".
Or put the two hyperlinks outside of the form
Remove partNum.touched from *ngIf in the validation message div, like this...
<div *ngIf="partNum.invalid && partNum.dirty" class="text-danger">
<p *ngIf="partNum.errors.required">PartNum is required.</p>
<p *ngIf="partNum.errors.correctPart">Please scan the correct part.</p>
</div>

Angular 4/5 Observable - How to update component template depending on observable property?

Ok I am still a newbie. I have successfully created a 'dashboard' component that has a left sidebar with links. On the right is where I have content/components displayed that I want to change dynamically depending on what link was clicked on the left sidebar (see bootstrap sample of what this dashboard looks like here, click on the toggle button to view the sidebar: https://blackrockdigital.github.io/startbootstrap-simple-sidebar/).
I have created a DashboardService that has a Subject and an Observable to allow for sibling component communication. This works great since I have a console.log() that shows this communication working (when I click on link on sidebar in SidebarComponent, I console.log() a value 'emitted' by the DashboardService that is being listened to by the SidebarComponent's sibling, DashboardSectionComponent).
The problem that I am having is that the template in DashboardSectionComponent loads the correct component section ONLY on initial load of page - once I click on a link on the side bar the content is blank and nothing is rendered.
Here is the service that allows the componenent communication:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class DashboardService {
private selectedComponentAlias = new Subject<string>();
constructor() {}
setSelectedComponentAlias(alias: string) {
this.selectedComponentAlias.next(alias);
}
getSelectedComponentAlias(): Observable<string> {
return this.selectedComponentAlias.asObservable();
}
}
Here is the SidebarComponent:
import { Component, OnInit } from '#angular/core';
import { DashboardService } from '../dashboard.service';
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.css']
})
export class SidebarComponent implements OnInit {
constructor(private dashboardService: DashboardService) { }
ngOnInit() {
}
onShowSection(event) {
event.preventDefault();
const componentAlias = event.target.getAttribute('data-componentAlias');
this.dashboardService.setSelectedComponentAlias(componentAlias);
}
}
here is the DashboardSectionComponent (the one that subscribes to the observable and I want to set property that controls the template views depending on the value that was caught)
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
import { DashboardService } from '../dashboard.service';
#Component({
selector: 'app-dashboard-section',
templateUrl: './dashboard-section.component.html',
styleUrls: ['./dashboard-section.component.css']
})
export class DashboardSectionComponent implements OnInit, OnDestroy {
private subscrition: Subscription;
selectedComponentAlias: string = 'user-profile';
constructor(private dashboardService: DashboardService) {
}
ngOnInit() {
this.subscrition = this.dashboardService.getSelectedComponentAlias()
.subscribe((selectedComponentAlias: string) => {
this.selectedComponentAlias = selectedComponentAlias;
console.log('user clicked: ',this.selectedComponentAlias);
});
}
ngOnDestroy() {
this.subscrition.unsubscribe();
}
}
Finally here is the template for DashboardSectionComponent which might have wrong syntax:
<div *ngIf="selectedComponentAlias == 'my-cards'">
<app-cards></app-cards>
</div>
<div *ngIf="selectedComponentAlias == 'user-profile'">
<app-user-profile></app-user-profile>
</div>
<div *ngIf="selectedComponentAlias == 'user-settings'">
<app-user-settings></app-user-settings>
</div>
Again, this works great (selectedComponentAlias is 'user-profile' on page load by default). But it goes blank after I click on a Sidebar link....
Thanks.
this was easy - like #RandyCasburn pointed out, this was a matter of getting the routing working properly.

Categories