Angular json value not displayed in textbox - javascript

Im new to Angular and I can't figure out whats going on here.
I have a MVC Controller which gives me the correct value such as {PostalContactPerson : jeff}
however my Angualar view does not recognise the value on page load in the input box.
How can i get the value into the textbox please? Im perplexed why it's 'blank' when the other values are displayed from the same Form Group.
Form.component.ts
export class FormComponent implements OnInit {
data: any = null;
this.arService.get(id, true)
.subscribe(data => {
this.loading = false;
this.ar = data;
this.attachments = this.ar.attachments.filter(e => e.type == Enums.AttachmentType.Normal);
**this.data = this.ensureData(data.formData);
this.ar.formData = this.data;**
this.stationInformationForm = formBuilder.group({
"businessNumbersGroup": formBuilder.group({
"acn": [this.data.acn],
"icn": [this.data.icn],
"postalcontactperson": [this.data.postaladdress],
}, ),
});
}
ensureData(data: any): any {
if (data == null) {
data = {};
}
if (!data["postalcontactperson"]) {
data["postalcontactperson"] = { state: "" };
}
form.component.html
<div [formGroup]="stationInformationForm.controls['businessNumbersGroup']">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>ACN</label>
<div class="form-description">Specify if applicable</div>
<input type="text" [ngClass]="{'has-error': !stationInformationForm.controls['businessNumbersGroup'].valid && (stationInformationForm.controls['businessNumbersGroup'].controls['acn'].touched || workflowSections[0].submitted)}" formControlName="acn" [(ngModel)]="data.acn"
class="form-control" />
<hr/>
</div>
<div class="form-group">
<label>postalcontactperson</label>
<div class="form-description">Specify if applicable</div>
<input type="text" [ngClass]="{'has-error': !stationInformationForm.controls['businessNumbersGroup'].valid && (stationInformationForm.controls['businessNumbersGroup'].controls['PostalContactPerson'].touched || workflowSections[0].submitted)}" formControlName="PostalContactPerson"
[(ngModel)]="data.postalcontactperson" class="form-control" />
</div>
<hr/>
</div>
</div>
</div>
MVC Model
public static AnnualReturnModel Create(AnnualReturn annualReturn)
{
return new AnnualReturnModel
{
Id = annualReturn.Id,
FormData = annualReturn.FormData,
PostalContactPerson = annualReturn.FormData.PostalContactPerson,
}
}
MVC Form Data
public class AnnualReturnFormData
{
public int? ActiveSectionIndex { get; set; }
#region Station Information
public string LesseeNames { get; set; }
//Postal Address
public string PostalContactPerson { get; set; }
}
json result of above:
{"ActiveSectionIndex":0,"LesseeNames":"aaa","PostalContactPerson":"aa","PostalPosition":"aa","PostalEmail":"aa","PostalPhone":"aa","StationContactPerson":"aaa"}
debugging description here
On page load, my ACN is shown in the textbox but the PostalContactPerson is not. why?
When I put an alert on the site, the contact person does not show my value.
alert(this.data.acn); //shows me a value of 22222
alert("contact person=" + this.data.postalcontactperson); //shows undefined
Any suggestions please? what am I missing, the JSON value is shown correctly when i check the database.
Any suggestions are Much appreciated. what am i missing with the ngModel?
Ive updated the code to the below:
form.component.html
<div [formGroup]="stationInformationForm.controls['postalAddressGroup']">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>Contact Person</label>
<input type="text" formControlName="postalcontactperson"class="form-control" value={{annualReturn.postalcontactperson}} /> <!--value={{annualReturn.postalcontactperson}}-->
</div>
<div class="form-group">
<label>Position</label>
<input type="text" formControlName="postalposition" class="form-control" />
<div class="validation-error" *ngIf="!stationInformationForm.controls['postalAddressGroup'].valid && (stationInformationForm.controls['postalAddressGroup'].get('postalposition').touched || workflowSections[0].submitted)">You must specify Position</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>Email</label>
<input type="text" formControlName="postalemail" class="form-control" />
<div class="validation-error" *ngIf="!stationInformationForm.controls['postalAddressGroup'].valid && (stationInformationForm.controls['postalAddressGroup'].get('postalemail').touched || workflowSections[0].submitted)">You must specify email</div>
</div>
<div class="form-group">
<label>Phone</label>
<input type="text" formControlName="postalphone" class="form-control" />
<div class="validation-error" *ngIf="!stationInformationForm.controls['postalAddressGroup'].valid && (stationInformationForm.controls['postalAddressGroup'].get('postalphone').touched || workflowSections[0].submitted)">You must specify phone</div>
</div>
</div>
</div>
</div>
</div>
form.component.ts
var id = this.route.snapshot.params['param'];
if (id != null) {
this.arService.get(param, true)
.subscribe(data => {
this.loading = false;
this.annualReturn = data;
this.attachments = this.annualReturn.attachments.filter(e => e.type == Enums.AttachmentType.Normal);
this.data = this.ensureData(data.formData);
this.annualReturn.formData = this.data;
this.dateConfig.disableUntil = { year: this.annualReturn.year - 1, month: 6, day: 30 };
this.dateConfig.disableSince = { year: this.annualReturn.year, month: 7, day: 1 };
// this.testprop = this.data.postalcontactperson;
this.stationInformationForm = formBuilder.group({
"survey.region": [this.data.survey.region],
"lesseeNames": [this.data.stationName, Validators.required],
// "somethingfancy": [this.data.postalcontactperson],
"postalcontactperson": [this.data.postalcontactperson],
"postalAddressGroup": formBuilder.group({
"postalcontactperson": [this.data.postalcontactperson, Validators.required],
"postalposition": [this.data.postalposition, Validators.required],
"postalemail": [this.data.postalemail, Validators.required],
"postalphone": [this.data.postalphone, Validators.required],
}, ),

You are referring to the incorrect formControlName in the template file. It's postalcontactperson not PostalContactPerson . Observe the Capitals P...C...P.... And better not to use [(ngModel)] as you already using Reactive Forms. FYI, refer below changes
Working stackblitz
Typescript file
export class AppComponent implements OnInit {
addressForm: FormGroup;
stationInformationForm: FormGroup;
data: any = {
acn: 1,
icn: 2,
postaladdress: {
contactperson: "Michael",
address: "Some Address"
}
};
constructor(private formBuilder: FormBuilder) {}
public ngOnInit() {
this.stationInformationForm = this.formBuilder.group({
businessNumbersGroup: this.formBuilder.group({
acn: [this.data.acn, Validators.required],
icn: [this.data.icn, Validators.required],
postalcontactperson: [this.data.postaladdress.contactperson, Validators.required]
})
});
// Getting inner form group
this.addressForm = this.stationInformationForm.get(
"businessNumbersGroup"
) as FormGroup;
// Getting Form Changes instead of using `[(ngModel)]`
this.addressForm.valueChanges.subscribe(data => console.log(data));
}
}
Template file
<div [formGroup]="addressForm">
<label>ACN</label>
<div class="form-description">Specify if applicable</div>
<input type="text" [ngClass]="{'has-error': !addressForm.valid && addressForm.get('acn').touched }" formControlName="acn" class="form-control" />
<hr />
<label>postalcontactperson</label>
<div class="form-description">Specify if applicable</div>
<input type="text" [ngClass]="{'has-error': !addressForm.valid && addressForm.get('postalcontactperson').touched }" formControlName="postalcontactperson" class="form-control" />
</div>

Related

How can I validate my form and save data in my database? angular php mysql

I'm working on an angular project and I want to develop a form, validate it, and save data in my database with php and mysql. I tried to do this but it doesn't work. I got these errors.
Property 'dataService' does not exist on type 'AppComponent'.
'data' is declared but its value is never read.
Parameter 'data' implicitly has an 'any' type.
Property 'router' does not exist on type 'AppComponent'.
'error' is declared but its value is never read.
Parameter 'error' implicitly has an 'any' type.
register.component.html
<h2 class="text-center">Registration</h2>
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-6 col-md-offset-3">
<div class="jumbotron">
<form [formGroup]="angForm" autocomplete="off" >
<div class="form-group">
<label for="name">Name</label>
<input type="text" name="name" formControlName="name" autocomplete="off" class="form-control input-sm" required minlength="1" maxlength="250" placeholder="Name"
[class.is-invalid]="name.invalid && (name.dirty || name.touched)">
<div *ngIf="name.invalid && (name.dirty || name.touched)" class="invalid-feedback">
<div *ngIf="name.errors?.['required']">
This field is required.
</div>
</div></div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" formControlName="email" autocomplete="off" required minlength="1" maxlength="250" class="form-control input-sm" placeholder="Email"
[class.is-invalid]="email.invalid && (email.dirty || email.touched)">
<div *ngIf="email.invalid && (email.dirty || email.touched)" class="invalid-feedback">
<div *ngIf="email.errors?.['required']">
This field is required.
</div>
<div *ngIf="!email.errors?.['required'] && email.errors?.['emailValidator']">
Invalid email format.
</div>
</div></div>
<div class="form-group">
<label for="Password">Password</label>
<input type="password" name="Password" formControlName="password" required minlength="15" autocomplete="off" class="form-control input-sm" placeholder="Password"
[class.is-invalid]="password.invalid && (password.dirty || password.touched)">
<button type="button" class="btn btn-outline-secondary" (click)="user.showPassword = !user.showPassword">
<i class="bi" [ngClass]="{'bi-eye-fill': !user.showPassword, 'bi-eye-slash-fill': user.showPassword}"></i>
</button>
<div *ngIf="password.invalid && (password.dirty || password.touched)" class="invalid-feedback">
<div *ngIf="password.errors?.['required']">
This field is required.
</div>
<div *ngIf="password.errors?.['minlength']">
This field must have at least 8 characters.
</div>
</div></div>
<button type="submit" class="btn btn-success" (click)="validate(angForm1)">Register</button>
</form>
</div>
</div>
<div class="col-md-3">
</div>
</div>
register.component.ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, FormBuilder, Validators, NgForm, AbstractControl} from
'#angular/forms';
import { first } from 'rxjs/operators';
import { Router } from '#angular/router';
import { ApiService } from '../api.service';
import { emailValidator } from './email-validator.directive';
interface IUser {
name: string;
email: string;
password: string;
}
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
angForm!: FormGroup;
user: IUser;
constructor() {
this.user = {} as IUser;
}
ngOnInit(): void {
this.angForm = new FormGroup({
name: new FormControl(this.user.name, [
Validators.required
]),
email: new FormControl(this.user.email, [
Validators.required,
EmailValidator,
]),
password: new FormControl(this.user.password, [
Validators.required,
Validators.minLength(8),
]),
});
}
get name() {
return this.angForm.get('name')!;
}
get email() {
return this.angForm.get('email')!;
}
get password() {
return this.angForm.get('password')!;
}
public validate(angForm1:any): void {
if (this.angForm.invalid) {
for (const control of Object.keys(this.angForm.controls)) {
this.angForm.controls[control].markAsTouched();
}
return;
}
else{ this.dataService. userregistration(angForm1.value.name,angForm1.value.email,
angForm1.value.password).pipe(first()).subscribe(
data => {
this.router.navigate(['login']);
},
error => {
});
}
this.user = this.angForm.value;
console.info('Name:', this.user.name);
console.info('Email:', this.user.email);
console.info('Password:', this.user.password);
}
}
register.php
<?php
include_once "database.php";
$postdata = file_get_contents("php://input");
if (isset($postdata) && !empty($postdata)) {
$request = json_decode($postdata);
$name = trim($request->name);
$pwd = mysqli_real_escape_string($mysqli, trim($request->pwd));
$email = mysqli_real_escape_string($mysqli, trim($request->email));
$sql = "INSERT INTO users(name,password,email) VALUES ('$name','$pwd','$email')";
if ($mysqli->query($sql) === true) {
$authdata = [
"name" => $name,
"pwd" => "",
"email" => $email,
"Id" => mysqli_insert_id($mysqli),
];
echo json_encode($authdata);
}
}
?>
api.service.ts
import {
Injectable,
Output,
EventEmitter
}
from '#angular/core';
import {
map
}
from 'rxjs/operators';
import {
HttpClient
}
from '#angular/common/http';
import {
Users
}
from './users';
#Injectable({
providedIn: 'root'
})
export class ApiService {
redirectUrl: string;
baseUrl: string = "http://localhost/angular_admin/php";
#Output() getLoggedInName: EventEmitter < any > = new EventEmitter();
constructor(private httpClient: HttpClient) {}
public userlogin(username: any, password: any) {
alert(username)
return this.httpClient.post < any > (this.baseUrl + '/login.php', {
username,
password
})
.pipe(map(Users => {
this.setToken(Users[0].name);
this.getLoggedInName.emit(true);
return Users;
}));
}
public userregistration(name: any, email: any, pwd: any) {
return this.httpClient.post < any > (this.baseUrl + '/register.php', {
name,
email,
pwd
})
.pipe(map(Users => {
return Users;
}));
}
//token
setToken(token: string) {
localStorage.setItem('token', token);
}
getToken() {
return localStorage.getItem('token');
}
deleteToken() {
localStorage.removeItem('token');
}
isLoggedIn() {
const usertoken = this.getToken();
if (usertoken != null) {
return true
}
return false;
}
}
In your constructor in your ts:
constructor(private router: Router,private apiservice: ApiService) {
this.user = {} as IUser;
}
public validate(angForm1:any): void {
if (this.angForm.invalid) {
for (const control of Object.keys(this.angForm.controls)) {
this.angForm.controls[control].markAsTouched();
}
return;
}
else{ this.apiservice. userregistration(angForm1.value.name,angForm1.value.email,
angForm1.value.password).pipe(first()).subscribe(
(data: any) => {
this.router.navigate(['login']);
},
error => {
});
}
this.user = this.angForm.value;
console.info('Name:', this.user.name);
console.info('Email:', this.user.email);
console.info('Password:', this.user.password);
}
In your html:
<h2 class="text-center">Registration</h2>
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-6 col-md-offset-3">
<div class="jumbotron">
<form [formGroup]="angForm" autocomplete="off" >
<div class="form-group">
<label for="name">Name</label>
<input type="text" name="name" formControlName="name" autocomplete="off" class="form-control input-sm" required minlength="1" maxlength="250" placeholder="Name"
[class.is-invalid]="angForm.controls['name'].invalid && (angForm.controls['name'].dirty || angForm.controls['name'].touched)">
<div *ngIf="angForm.controls['name'].invalid && (angForm.controls['name'].dirty || angForm.controls['name'].touched)" class="invalid-feedback">
<div *ngIf="angForm.controls['name'].errors?.required">
This field is required.
</div>
</div></div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" formControlName="email" autocomplete="off" required minlength="1" maxlength="250" class="form-control input-sm" placeholder="Email"
[class.is-invalid]="angForm.controls['email'].invalid && (angForm.controls['email'].dirty || angForm.controls['email'].touched)">
<div *ngIf="angForm.controls['email'].invalid && (angForm.controls['email'].dirty || angForm.controls['email'].touched)" class="invalid-feedback">
<div *ngIf="angForm.controls['email'].errors.required">
This field is required.
</div>
<div *ngIf="!email.errors?.['required'] && email.errors?.['emailValidator']">
Invalid email format.
</div>
</div></div>
<div class="form-group">
<label for="Password">Password</label>
<input type="password" name="Password" formControlName="password" required minlength="15" autocomplete="off" class="form-control input-sm" placeholder="Password"
[class.is-invalid]="angForm.controls['password'].invalid && (angForm.controls['password'].dirty || angForm.controls['password'].touched)">
<button type="button" class="btn btn-outline-secondary" (click)="user.showPassword = !user.showPassword">
<i class="bi" [ngClass]="{'bi-eye-fill': !user.showPassword, 'bi-eye-slash-fill': user.showPassword}"></i>
</button>
<div *ngIf="angForm.controls['password'].invalid && (angForm.controls['password'].dirty || angForm.controls['password'].touched)" class="invalid-feedback">
<div *ngIf="angForm.controls['password'].errors.required">
This field is required.
</div>
<div *ngIf="angForm.controls['password'].errors.minlength">
This field must have at least 8 characters.
</div>
</div></div>
<button type="submit" class="btn btn-success" (click)="validate(angForm1)">Register</button>
</form>
</div>
</div>
<div class="col-md-3">
</div>
</div>

Update Specific Component rather than whole page

I am currently making a small application where users can add contacts to a list. i am able to add the contacts to a list and it shows up in a separate div. at the moment when i click save, i have some code which basically has await and the whole app refreshes. the problem i am currently having is when i insert data for a user and click save it refreshes the whole screen but instead when i click save i want it to only refresh the contact component.
I currently have a piece of code which essentially refreshes all the components which is okay but now i want to try and just refresh the component i have added data too.
i have included the code below:
`
<div class="container">
<div class="main-contact-list">
<h2>Contacts</h2>
<li *ngFor="let contact of contacts;">
<button type="button" (click)="getContact(contact.id)">
<span class="name"> {{contact.firstName}} {{contact.lastName}}</span>
</button>
</li>
</div>
<div class="form">
<form [formGroup]="fg">
<div class="actionBtns">
<button class="btn btn-primary" (click)="saveContact()">Edit/Save</button>
<div class="divider"></div>
<button class="btn btn-primary" (click)="deleteContact(contact.id)">Delete</button>
</div>
<div class="row" class="row-object">
<input type="text" class="form-control" [(ngModel)]="contact.firstName" name="firstName"
formControlName="firstName" placeholder="First Name" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.lastName" name="lastName"
formControlName="lastName" placeholder="Last Name" />
<div class="col">
<input type="text" class="form-control" [(ngModel)]="contact.emailAddress" name="emailAddress"
formControlName="emailAddress" placeholder="Email Address" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.address1" name="address1"
formControlName="address1" placeholder="Address1" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.address2" name="address2"
formControlName="address2" placeholder="Address2" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.city" name="city"
formControlName="city" placeholder="City" />
<div class="divider"></div>
<input type="text" class="form-control" [(ngModel)]="contact.postCode" name="postCode"
formControlName="postCode" placeholder="Post Code" />
<div class="divider"></div>
</div>
</div>
</form>
<div class="activityForm" *ngIf="contact.id">
<app-activities [childItem]="contact"></app-activities>
</div>
</div>
`
`
import { Component, OnInit } from '#angular/core';
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
FormsModule
} from '#angular/forms';
import { AppService } from '../app.service';
//interface for Contact
interface Contact {
id?: number;
firstName: string;
lastName: string;
emailAddress: string;
address1: string;
address2: string;
city: string;
postCode: string;
}
#Component({
selector: 'app-contact-details',
templateUrl: './contact-details.component.html',
styleUrls: ['./contact-details.component.css'],
})
export class ContactDetailsComponent implements OnInit {
constructor(private appService: AppService, private fb: FormBuilder) { }
fg!: FormGroup;
contacts: Contact[] = [];
contact: any = {};
//get the form field as a form control. it will useful for validation and etc
get firstNameField(): FormControl {
return this.fg.get('firstName') as FormControl;
}
get lastNameField(): FormControl {
return this.fg.get('lastName') as FormControl;
}
get emailAddressField(): FormControl {
return this.fg.get('emailAddress') as FormControl;
}
get address1Field(): FormControl {
return this.fg.get('address1') as FormControl;
}
get address2Field(): FormControl {
return this.fg.get('address2') as FormControl;
}
get postCodeField(): FormControl {
return this.fg.get('postCode') as FormControl;
}
async ngOnInit() {
this.initForm();
this.getContacts();
}
//form init. here we create the reactive form. https://angular.io/guide/reactive-forms
initForm(): void {
let id = Date.now() * Math.random();
this.fg = this.fb.group({
id: [id],
firstName: ['', [Validators.required]],
lastName: ['', [Validators.required]],
emailAddress: ['', [Validators.required, Validators.email]],
address1: ['', [Validators.required]],
address2: ['', [Validators.required]],
city: ['', [Validators.required]],
postCode: ['', [Validators.required]],
});
}
//save function
async saveContact() {
console.log(this.fg);
console.log(this.contact);
//if form doesn't have any validation error this if condition will executed
if (this.fg.valid) {
const newContact: Contact = {
firstName: this.fg.value.firstName,
lastName: this.fg.value.lastName,
emailAddress: this.fg.value.emailAddress,
address1: this.fg.value.address1,
address2: this.fg.value.address2,
city: this.fg.value.city,
postCode: this.fg.value.postCode,
};
if (this.contact?.id) {
this.appService
.editContacts(this.contact.id, newContact).subscribe();
} else {
this.appService
.addContacts(newContact).subscribe();
}
this.fg.reset(); //resetting the form array
await this.refresh();
} else {
console.log('this is invalid ');
}
}
refresh(): void {
window.location.reload();
}
async getContacts(): Promise<void> {
await this.appService
.getContacts()
.subscribe((contacts: any) => (this.contacts = contacts));
}
edit(id: number): void {
const data = this.contacts[id];
this.fg.patchValue(data);
this.refresh();
}
getContact(id: number | undefined) {
this.appService
.get(id)
.subscribe((contact: any) => (this.contact = contact));
}
selectContact(contact: Contact) {
this.contact = contact;
}
deleteContact(id: number | undefined) {
this.appService.deleteContact(id).subscribe();
this.refresh();
}
}
`
I did manage to figure this problem out. Most tutorials were stating that you basically use the router route so when something happens to refresh the route and it should show the new page.
Instead I just added #Input in front of my array and object, so when in my main HTML page I am using the *ngFor statement to iterate through the array, every time it is updated it automatically shows the new list

Input Field showing fetched data but not allowed to input anything

I am trying for 3 day and looking for a solution in to fix this problem but I am not success. I have read a problem with the same title but it did not help me in solving because it is different from my problem.
I hope that someone can help me .
Problem Description :
I have a Modal which send from one component to another component , this Modal should allow the user to Update the Student Information after calling the Method onUpdate() .
so I have Component A and Child A and the exchange some Data by using service file .
Component A has the Method onUpdate (userData) --> send to service --> Child A
javaScriptCode for Component A
onUpdate(userId: number, index: number) {
this.isOnDeleteORUpdate = true;
**// here the data will be fetched from DB and send to the Service,
// so that can be shown in Modal Form**
if (this.isOnDeleteORUpdate) {
this.requestService.getUserWithId(userId).subscribe((userData) => {
this.theModalServiceUpdate.setUser(userData);
this.theModalServiceUpdate.setUser(userData);
if (userData.courses.length <= 0 || userData.courses == null) {
this.loadedCourses = [];
this.theModalService.setCourses([]);
} else {
this.loadedCourses = userData.courses;
this.theModalService.setCourses(userData.courses);
}
});
this.modalService
.open(this.theModalServiceUpdate.modalTemplate, { centered: true })
.result.then(
(res) => {
this.closeModal = `Closed with: ${res}`;
},
(res) => {
this.closeModal = `Dismissed ${this.getDismissReason(res)}`;
}
);
}
}
Modal HTML Code:
<ng-template #modalData let-modal>
<div class="modal-dialog-scrollable modal-dialog-centered" id="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-basic-title">Modal title</h5>
<button
type="button"
class="btn-close"
data-dismiss="modal"
aria-label="Close"
(click)="modal.dismiss('Cross click')"
></button>
</div>
<div class="modal-body">
<form [formGroup]="userForm">
<label for="firstName">First Name</label>
<input
type="text"
class="form-control"
name="firstName"
formControlName="firstName"
/>
<label for="lastName">Last Name</label>
<input
type="text"
class="form-control"
name="lastName"
formControlName="lastName"
/>
<label for="email">Email</label>
<input
type="email"
class="form-control"
name="email"
formControlName="email"
/>
<label for="address">Address</label>
<input
type="text"
class="form-control"
name="address"
formControlName="address"
/>
<label for="telNumber">Telfone no.</label>
<input
type="text"
class="form-control"
name="telNumber"
formControlName="telNumber"
/>
<label for="gebDatum">gebDatum</label>
<input
type="text"
class="form-control"
name="gebDatum"
formControlName="gebDatum"
/>
</form>
<div class="card-body">
<div
style="
border-radius: 6px;
overflow: hidden;
border: 0.05em solid rgb(218, 231, 198);
"
>
<table class="table table-hover table-responsive">
<thead style="background-color: gray; text-align: center">
<tr>
<th scope="col">Id</th>
<th scope="col">Course Name</th>
<th scope="col">Serial Number</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let course of loadedCourses"
style="text-align: center"
>
<td>{{ course.id }}</td>
<td>
<input type="text" [value]="course.name" />
</td>
<td>
<input type="text" [value]="course.serialNumber" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="modal-footer">
<button (click)="saveData()" type="button" class="btn btn-danger">
Close
</button>
</div>
</div>
</div>
</ng-template>
its work for me fine and the Student Data are fetched from DataBase in the Input fields .
but I can't inter Any Information , the Input Field is frozen only I can inter the Courses Information because I user the [value] data binding in HTML instead of ngForm.
see photo: as you can see the Modal divided in tow section , in the upper one I get the data from DB but I can't modify it. in the Table Section because I am using the data binding and not the ngForm so I can enter information .
The code in JavaScript look like the following :
import { Component, OnInit, TemplateRef, ViewChild } from '#angular/core';
import { FormControl, FormGroup, Validators } from '#angular/forms';
import { courses } from 'src/app/userInfo/courses.module';
import { userInfo } from 'src/app/userInfo/user.module';
import { modalServiceUpdate } from '../../modal.updateUser.service';
#Component({
selector: 'app-modal-update-student',
templateUrl: './modal-update-student.component.html',
styleUrls: ['./modal-update-student.component.css'],
})
export class ModalUpdateStudentComponent implements OnInit {
#ViewChild('modalData') templateRef: TemplateRef<any>;
userForm: FormGroup;
loadedUser: userInfo;
lodedUserId: number = 0;
loadedCourses: courses[] = [
{
id: 0,
name: 'undefine',
serialNumber: 0,
},
];
user: userInfo = {
id: 0,
firstName: 'undefined',
lastName: 'undefined',
email: '',
gebDatum: 0,
telNumber: 0,
address: '',
courses: [],
};
constructor(private modalServiceUpdate: modalServiceUpdate) {}
loadedUserFromService: userInfo;
ngOnInit(): void {}
ngDoCheck(): void {
this.initForm();
}
private initForm() {
this.modalServiceUpdate.modalTemplate = this.templateRef;
let firstName = '';
let lastName = '';
let email = '';
let address = '';
let telNumber = 0;
let gebDatum = '0';
const user = this.modalServiceUpdate.getUser();
this.loadedCourses = this.modalServiceUpdate.getUser().courses;
firstName = user.firstName;
lastName = user.lastName;
email = user.email;
address = user.address;
telNumber = user.telNumber;
gebDatum = '0';
this.userForm = new FormGroup({
firstName: new FormControl(firstName, Validators.required),
lastName: new FormControl(lastName, Validators.required),
email: new FormControl(email, Validators.required),
address: new FormControl(address, Validators.required),
telNumber: new FormControl(telNumber, Validators.required),
gebDatum: new FormControl(gebDatum, Validators.required),
});
}
saveData() {}
}
the second issue , if I don't use the ngDoCheck() , the data that comes from other Component(Component A) will not be fired before the onUpdate() is called. so with ngOnInit() I will get undefined or empty userData .
is there alternative that can I use , to inform Angular about any changes happen on the userData that saved in Service.ts ?
I hope that some one can help me .
Thank you

How to Interact between a view component's form and a controller in ASP.NET Core?

I'm beginner in web designing with ASP.NET Core. I wrote a view component that has a form with some inputs related to a view model. One of these inputs is a file input (of the IFormFile datatype).
I want to submit this view model to an action of a controller (POST action), check the validity of model, return another view component if the model state is valid, and remain on this view component with this view model if model state is not valid.
This is my View Model: PricingViewModel.cs
public class PricingViewModel
{
[Display(Name = "Select a file")]
public IFormFile formFile { get; set; }
[Display(Name = "ColumnCode")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colCode { get; set; }
[Display(Name = "ColumnName")]
[Required(ErrorMessage = "Enter {0} value, please")]
public string colName { get; set; }
}
My View Component (controller): PricingComponent.cs
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
My View Component (view): PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
My Home Controller: HomeController.cs
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); // 1
}
else if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return ViewComponent("PricingComponent", new { pricing = pricing }); //2
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return ViewComponent("PricingComponent", new { pricing = pricing }); //3
}
}
Plan A
The above approach is my primary plan.
Problem
If I use options of submit input tag (asp-action, asp-controller) like above, the view model sends correctly, but I don't know how to handle the validity of the model and remain on this view component. In the above code, when the ShowPricing action runs, if the model state is valid, the code works correctly, but when model is invalid (1,2,3), the PricingView doesn't show the validation summery, and just loads with current view model.
Plan B
I used AJAX to send the viewModel to the action and instead of showing the validation summary, I send an alert to the user with AJAX. I changed PricingView as following:
My View Component (view): PricingView.cshtml
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId"/>
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId"/>
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="fromFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile"/>
</div>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '#Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
$.ajax({
type: "POST",
url: _url,
data: {
formFile: input,
colCode: $("#colCode").val(),
colName: $("#colName").val(),
},
success: function (result)
{
var IsValid = $('body').find('[name="IsValidPricing"]').val();
if (IsValid)
{
$("#ShowExcelTable").html(result);
}
else {
alert("Invalid Data");
}
},
});
});
});
</script>
<div class="form-group mt-4">
<input type="submit" value="Show" id="ShowPricingBtn" />
</div>
</form>
Problem
In this code:
If the model state is not valid, the alert sends correctly, but
If the model state is valid, the formFile input doesn't send correctly to action and it's null in view model.
I don't know whether I should go with the original or the alternate approach these problems. Do you know where I'm going wrong?
Not sure how do you call view components,here are the working demos:
For PlanA
1.Create ViewComponents/PricingComponent.cs and ViewComponents/ShowPricingExcelComponent.cs.
public class PricingComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("PricingView", pricing));
}
}
public class ShowPricingExcelComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(PricingViewModel pricing)
{
return await Task.FromResult((IViewComponentResult)View("ShowPricingExcel", pricing));
}
}
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml.
#model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
<input type="submit" asp-action="ShowPricing" asp-controller="Home" value="Show" id="ShowPricingBtn" />
</div>
</form>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml.
<h1>Excel....</h1>
Project Structure:
4.Views/Home/Index.cshtml:
#await Component.InvokeAsync("PricingComponent")
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colCode", "Invalid Data");
return View("Index", pricing);
}
if (!int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
ModelState.AddModelError("colName", "Invalid Data");
return View("Index", pricing);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return View("Index", pricing); //3
}
}
}
Result:
For PlanB
1.Create ViewComponents/PricingComponent.cs and ViewComponents/ShowPricingExcelComponent.cs.
2.Create Views/Shared/Components/PricingComponent/PricingView.cshtml.
Firstly,it should be type="button" otherwise it will call twice to the backend.Secondly,what you did in ajax is not correct,more detailed explation you could refer to this answer.At last,you could not judge the modelstate by get the value of IsValidPricing value in your sucess function.Because the value you get is always be the data you first render the page,you cannot get the changed ViewBag value when ajax post back.
#model PricingViewModel
<form class="text-left" method="post" enctype="multipart/form-data">
<input name="IsValidPricing" type="hidden" value="#ViewBag.isValid" />
<div class="form-group text-left">
<label asp-for="colCode" class="control-label"></label>
<input asp-for="colCode" class="form-control" id="colCodeId" />
<span asp-validation-for="colCode" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="colName" class="control-label"></label>
<input asp-for="colName" class="form-control" id="colNameId" />
<span asp-validation-for="colName" class="text-danger"></span>
</div>
<div class="form-group text-left">
<label asp-for="formFile " class="control-label"></label>
<input type="file" accept=".xlsx, .csv" asp-for="formFile" id="MyInputFile" />
</div>
<div class="form-group mt-4">
#*it should be type="button"*#
<input type="button" value="Show" id="ShowPricingBtn" />
</div>
</form>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#ShowPricingBtn').click(function () {
var _url = '#Url.Action("ShowPricing", "Home")';
var input = $("#MyInputFile").get(0).files[0];
var fdata = new FormData();
fdata.append("formFile", input);
$("form input[type='text']").each(function (x, y) {
fdata.append($(y).attr("name"), $(y).val());
});
$.ajax({
type: "POST",
url: _url,
data: fdata,
contentType: false,
processData: false,
success: function (result)
{
console.log(result);
if (result==false)
{
alert("Invalid Data");
}
else {
$("#ShowExcelTable").html(result);
}
},
});
});
});
</script>
3.Create Views/Shared/Components/ShowPricingExcelComponent/ShowPricingExcel.cshtml.
<h1>Excel....</h1>
4.Views/Home/Index.cshtml:
#await Component.InvokeAsync("PricingComponent")
<div id="ShowExcelTable"></div>
5.HomeController:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ShowPricing(PricingViewModel pricing)
{
if (ModelState.IsValid)
{
int temp;
if (!int.TryParse(pricing.colCode, out temp)|| !int.TryParse(pricing.colName, out temp))
{
ViewBag.isValid = 0;
return Json(false);
}
else
{
ViewBag.isValid = 1;
// do something ...
return ViewComponent("ShowPricingExcelComponent"); //Call another view component
}
}
else
{
ViewBag.isValid = 0;
return Json(false);
}
}
}
Result:
I'm not able to reproduce your error. Your code, as presented, works as expected. A validation message is displayed.
To make it a working example, I've added a GET method first.
[HttpGet]
public IActionResult ShowPricing() => ViewComponent("PricingComponent", new { pricing = new PricingViewModel() });
Open the URL Home/ShowPricing
Fill out the form.
Send the form. And the validation message is displayed.

Angular Unit Testing - How to fix 'Cannot read property "middle_name" of undefined' when initializing a component

I am trying to create a simple 'it should create' unit test for one of my Angular components (the test generated by Angular CLI when generating the component). However, when running the test, I receive a TypeError: Cannot read property 'middle_name' of undefined. I didn't expect this error because the HTML is surrounded in an *ngIf.
I tried to follow this question on stackoverflow: Angular testing Cannot read property 'name' of undefined
However the OP resolved the problem by surrounding his HTML in an *ngIf, which is what my code currently does. I suspect the problem is occurring because this is a child component and is conditionally rendered.
form-submit-individual-success-component.ts:
it('should create', () => {
expect(component).toBeTruthy();
});
add-entity.ts:
onIndividualSubmit(clonedIndividualValues: Individual): void {
this.entityService.addIndividual(clonedIndividualValues)
.subscribe(individual => {
this.formIsSubmitted = true;
this.individualFormIsSubmitted = true;
this.individualId = individual.id;
this.individualFormData = individual;
})
}
add-entity.html:
<app-add-individual-form *ngIf="selectedOption == 'Individual' && !individualFormIsSubmitted" (individualServiceEmitter)="onIndividualSubmit($event)" [individualId]="individualId"></app-add-individual-form>
add-individual-form.ts:
#Output() individualServiceEmitter = new EventEmitter();
onIndividualSubmit(value: any): void {
value.eod_screening === 'Yes' ? value.eod_screnning = 1 : value.eod_screening = 0;
const clonedIndividualValues = Object.assign({ type: 'Individual', screening_status: 'Awaiting Submission' }, value)
this.individualServiceEmitter.emit(clonedIndividualValues);
}
add-individual-form.html:
<div class="form-group">
<label for="middle_name">Middle Name(s)</label>
<input type="text" class="form-control" id="middle_name" name="middle_name" formControlName="middle_name"
#middle_name>
<div *ngFor="let validation of validation_messages.middle_name">
<div *ngIf="individualForm.middle_name.hasError(validation.type) && (individualForm.middle_name.dirty || individualForm.middle_name.touched)"
class="alert alert-danger">
{{validation.message}}
</div>
</div>
</div>
<div class="form-group">
<label for="last_name">Last Name</label>
<input type="text" class="form-control" id="last_name" name="last_name" formControlName="last_name" #last_name
required>
<div *ngFor="let validation of validation_messages.last_name">
<div *ngIf="individualForm.last_name.hasError(validation.type) && (individualForm.last_name.dirty || individualForm.last_name.touched)"
class="alert alert-danger">
{{validation.message}}
</div>
</div>
</div>
<button type="submit" class="btn btn-success" [disabled]="!addIndividualForm.valid">Submit</button>
form-submit-individual-success.ts:
#Input() formData: Individual;
public individualFormData: Individual;
#Input() id: number;
public individualId: number;
public editIndividualBase = "/entity/edit";
constructor() { }
ngOnInit() {
this.individualFormData = this.formData;
}
form-submit-individual-success.html:
<p>First name: <span>{{individualFormData.first_name}}</span></p>
<p *ngIf="individualFormData.middle_name">Middle name(s): <span>{{individualFormData.middle_name}}</span></p>
<p>Last name: <span>{{individualFormData.last_name}}</span></p>
As described in the problem summary, I expect the component toBeTruthy (i.e. create successfully).

Categories