The user has the ability to add a row of fields if he would like to request multiple dates. However I am having an issue: when the row is added it is pre-filled with the values from the initial row. Additionally, any changes made are applied to all the fields so in the end all the fields have the same values. How can I fix this?
Here is my controller file:
import { Component, OnInit, ViewChild } from '#angular/core';
import { SelectItem } from 'primeng/api';
import { Router } from '#angular/router';
import { NgForm } from '#angular/forms';
let date = new Date();
interface Supervisor {
name: string;
code: string;
}
interface LeaveCode {
name: string;
code: string;
}
#Component({
selector: 'app-fill-request',
templateUrl: './fill-request.component.html',
styleUrls: ['./fill-request.component.css']
})
export class FillRequestComponent implements OnInit {
#ViewChild('testForm') public test1234Form: NgForm;
ngOnInit() {
let today = new Date();
let month = today.getMonth();
let year = today.getFullYear();
let nextMonth = (month === 11) ? 0 : month + 4;
this.minDate = new Date(Date.now() - 12096e5);
this.maxDate = new Date(new Date().setFullYear(new Date().getFullYear() + 1));
this.maxDate.setMonth(nextMonth);
}
// dates: SelectItem[];
supervisor2: Supervisor[];
selectedSupervisor2: Supervisor;
leaveCodes2: LeaveCode[];
selectedLeaveCode2: LeaveCode;
// clicks: number = 0;
dates = [1] //Initiazling date array
minDate: Date;
maxDate: Date;
fromDate: Date;
toDate: Date;
fromDateMin: Date;
toDateMin: Date;
fromDateMax: Date;
toDateMax: Date;
fromTime: Date;
toTime: Date;
// Method to add dates on button click for up to 4 dates
onAddClicked() {
if (this.dates.length < 8)
this.dates.push(1);
}
//Method to remove dates on button click. Will always have one date field displayed
onRemoveClicked() {
if (this.dates.length > 1) this.dates.pop();
}
submit(): void {
this.router.navigate(["submitted"]);
}
// Array of Supervisors
constructor(private router: Router, ) {
this.supervisor2 = [
{ name: 'Joe Rogan', code: 'JR' },
{ name: 'Alex Jones', code: 'AJ' },
{ name: 'Sam Smith', code: 'SS' },
];
//Array of Leave Codes
this.leaveCodes2 = [
{ name: 'Personal Leave', code: 'PL' },
{ name: 'Sick Leave', code: 'SL' },
{ name: 'Vacation Leave', code: 'VL' },
];
}
}
Here is the relevant HTML code:
<div class="Dates" *ngFor="let dateline of dates">
<div class="ui-g form-group">
<div class="ui-g-12 ui-md-1" id="test">
<button pButton type="button" id="deleteButton" icon="pi pi-times" class="ui-button-danger" (click)="onRemoveClicked()"></button>
</div>
<div class="ui-g-12 ui-md-2" id="test">
<!-- Leave code field -->
<p-dropdown [options]="leaveCodes2" [(ngModel)]="selectedleaveCodes2" name="selectedleaveCodes2" placeholder="Leave Code*" optionLabel="name" required></p-dropdown>
</div>
<div class="ui-g-12 ui-md-2" id="test">
<!-- Start date & time fields -->
<p-calendar [showIcon]="true" [(ngModel)]="fromDate" name="fromDate" [minDate]="minDate" [readonlyInput]="true" placeholder="From Date*" id="setter" required>
</p-calendar>
</div>
<div class="ui-g-12 ui-md-2" id="test">
<input type="text" [(ngModel)]="fromTime" name="fromTime" placeholder="Input Time*" style="height: 2.186em" size="7" maxlength="8" pInputText required>
<!-- <p-inputMask mask="99:99 aa" placeholder="Select Time"></p-inputMask> -->
</div>
<div class="ui-g-12 ui-md-2" id="test">
<!-- End date & time fields -->
<p-calendar [showIcon]="true" [(ngModel)]="toDate" name="toDate" [minDate]="minDate" [maxDate]="maxDate" [readonlyInput]="true" placeholder="To Date*" id="setter" required></p-calendar>
</div>
<div class="ui-g-12 ui-md-2" id="test">
<input type="text" [(ngModel)]="toTime" name="toTime" placeholder="Input Time*" style="height: 2.186em" size="7" maxlength="8" pInputText required>
</div>
</div>
</div>
<button pButton type="button" id="addButton" icon="pi pi-plus" class="ui-button-success" (click)="onAddClicked()"></button>
<br><br><br>
<!-- Action buttons to add or remove dates. Submit button available -->
<div class=modButtons>
<div class="ui-g-12">
<!-- <button pButton type="button" label="Add Dates +" class="ui-button-success" (click)="onAddClicked()"></button> -->
<!--<button pButton type="submit" label="Submit" [disabled]="testForm.invalid" (click)="submit()"
class="ui-button-success" id="righter"></button>-->
<button pButton type="submit" class="ui-button-primary" style="white-space:nowrap;margin:10px;" label="Submit" [disabled]="testForm.invalid||!(testForm.valid&&testForm.dirty)" (click)="submit()"></button>
</div>
You are binding on single properties to all the elements like
<p-calendar [showIcon]="true" [(ngModel)]="fromDate" name="fromDate" [minDate]="minDate" [readonlyInput]="true" placeholder="From Date*" id="setter" required>
</p-calendar>
Here you are binding fromDate and which will be the same for all elements added by the button.
I suggest you use an object array like dates: {date:string, fromDate: string}[]; an populate your DOM.
Then in onAddedClicked()
onAddClicked() {
if (this.dates.length < 8)
this.dates.push({date:"", fromDate:""});
}
Related
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
am sending an put request based on these files.
Venue.ts file
export class Venue {
id: number;
venueName: string;
cityName: string;
emailContact: string;
fighter1: string;
fighter2: string;
dateOfFight: Date;
active: boolean;
}
My Angular Component files:
create-venue.component.html
<h3>Create Event</h3>
<div [hidden]="submitted" style="width: 400px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">Venue Name</label>
<input type="text" class="form-control" id="venueName" required [(ngModel)]="venue.venueName" name="venueName">
</div>
<div class="form-group">
<label for="name">City Name</label>
<input type="text" class="form-control" id="cityName" required [(ngModel)]="venue.cityName" name="cityName">
</div>
<div class="form-group">
<label for="name">Email Contact</label>
<input type="text" class="form-control" id="emailContact" required [(ngModel)]="venue.emailContact" name="emailContact">
</div>
<div class="form-group">
<label for="name">Fighter 1 Contact</label>
<input type="text" class="form-control" id="fighter1" required [(ngModel)]="venue.fighter1" name="fighter1">
</div>
<div class="form-group">
<label for="name">Fighter 2 Contact</label>
<input type="text" class="form-control" id="fighter2" required [(ngModel)]="venue.fighter2" name="fighter2">
</div>
<div class="form-group">
<label for="name">Choose a time for your Event:</label>
<input type="datetime-local" class="form-control" id="dateOfFight" min="2021-01-01T00:00" max="2023-06-14T00:00" required [(ngModel)]="venue.dateOfFight" name="dateOfFight">
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>
<div [hidden]="!submitted">
<h4>You submitted successfully!</h4>
<!-- <button class="btn btn-success" (click)="newVenue()">Add</button> -->
</div>
create-venue.component.ts
import { VenueService } from '../venue.service';
import { Venue} from '../venue';
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-create-venue',
templateUrl: './create-venue.component.html',
styleUrls: ['./create-venue.component.css']
})
export class CreateVenueComponent implements OnInit {
venue: Venue = new Venue();
submitted = false;
constructor(private venueService: VenueService,
private router: Router) {}
ngOnInit() {
}
newVenue(): void {
this.submitted = false;
this.venue = new Venue();
}
save() {
this.venueService.createVenue(this.venue)
.subscribe(data => console.log(data), error => console.log(error));
this.venue = new Venue();
this.gotoList();
}
onSubmit() {
this.submitted = true;
this.save();
}
gotoList() {
this.router.navigate(['/venues']);
}
}
My current sent data in chrome:
I am quite new to javascript and angular, maybe this was answered before but I have no idea how to get the input data into the Venue object...
Edit:
This is my header tab:
I
using a string instead of Date type for the dateOfFight property will let you post to the backend without issue.
You can then generate the date with new Date(datestring) on your server if needed. On the front end you can look into date pipes which will help you format the string accordingly.
You also seem to not be capturing any values in your date input. Notice venue.dateOfFight is not even there. Perhaps try logging out your data before posting
You can use a string instead of date type dateOfFight: string;, and before saving, trasform it into a date format with Moment js.
moment(Date.now()).format('YYYY-MM-DD').toString()
I have used vue-date-picker and v-model for two way data binding. Initially I have set value to date(i.e. startDate in this case) and in console I am printing that passed value(i.e. startDate). At first, that passed value to startDate (i.e. 2019-09-17 is printed) but when I chose new Date, startDate value didn't get updated rather value remained same as it was when it was initially passed.
<div class="col-md-3">
<label for="startDate" class>Start Date</label>
<datepicker v-model="startDate" :readonly="true" format="YYYY-MM-DD" name="startDate">
</datepicker>
</div>
<p>Start Date: {{startDate}}</p>
<div class="col-md-2">
<div class="md-form mb-0">
<button type="button" class="btn btn-primary" #click="showDateValues">Apply</button>
</div>
</div>
import datepicker from "vue-date-picker";
<script>
import datepicker from "vue-date-picker";
export default {
name: "Example",
components: {
datepicker
},
data() {
return {
startDate: "2019-09-17"
};
},
methods:{
showDateValues(){
console.log("Start Date: "+this.startDate)
}
}
};
</script>
try this:
data() {
return {
startDate: new Date("2019-09-17")
};
},
var elm = new Vue({
el: '.app8',
mounted () {
var vm = this
$('#datedate').datepicker ({
onSelect: function(dateText) {
vm.date = dateText
}
})
});
<div class="row mt-5">
<div class="col">
<div class="app8">
<input v-model="date" name="date" class="input" type="date" id="datedate">
</div>
</div>
</div>
Hope this will help
I am trying to create a simple application to request a car key for a service department. Obviously the code could be written better, but this is my third day with Vue.js. The time function that is called in the first p tag in the code updates every minutes to keep count of an elapsed time. The problem I am having is when I request a new key the time function doesn't follow the array items as intended. For example, if there are no other requests the first request I submit works perfectly. However, when I submit a new request the elapsed time from my first request goes to my second request. I am sure it could have something to do with the glued together code, but I have tried everything I can think of. Any help would be appreciated.
<template>
<div class="row">
<div class="card col-md-6" v-for="(key, index) in keys" :key="index">
<div class="card-body">
<h5 class="card-title">Service Tag: {{ key.service_tag }}</h5>
<p class="card-text"> {{time}} {{key.reqTimestamp}}min</p>
<p class="invisible">{{ start(key.reqTimestamp) }}</p>
<p class="card-text">Associates Name: {{key.requestor_name}}</p>
<p class="card-text">Model: {{key.model}}</p>
<p class="card-text">Color: {{key.color}}</p>
<p class="card-text">Year: {{key.year}}</p>
<p class="card-text">Comments: {{key.comments}}</p>
<p class="card-text">Valet: {{key.valet}}</p>
<input class="form-control" v-model="key.valet" placeholder="Name of the person getting the car...">
<button
#click="claimedKey(key.id, key.valet)"
type="submit"
class="btn btn-primary"
>Claim</button>
<button v-if="key.valet !== 'Unclaimed'"
#click="unclaimedKey(key.id, key.valet)"
type="submit"
class="btn btn-primary"
>Unclaim</button>
<button class="btn btn-success" #click="complete(key.id)">Complete</button>
</div>
</div>
<!-- END OF CARD -->
<!-- START OF FORM -->
<div class="row justify-content-md-center request">
<div class="col-md-auto">
<h1 class="display-4">Operation Tiger Teeth</h1>
<form class="form-inline" #submit="newKey(service_tag, requestor_name, comments, model, year, color, valet, reqTimestamp)">
<div class="form-group col-md-6">
<label for="service_tag">Service Tag: </label>
<input class="form-control form-control-lg" v-model="service_tag" placeholder="ex: TB1234">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Associates Name: </label>
<!-- <input class="form-control form-control-lg" v-model="requestor_name" placeholder="Your name goes here..."> -->
<div class="form-group">
<label for="exampleFormControlSelect1">Example select</label>
<select v-model="requestor_name" class="form-control" id="requestor_name">
<option>James Shiflett</option>
<option>Austin Hughes</option>
</select>
</div>
</div>
<div class="form-group col-md-6">
<label for="service_tag">Model: </label>
<input class="form-control form-control-lg" v-model="model" placeholder="What is the model of the vehicle?">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Color: </label>
<input class="form-control form-control-lg" v-model="color" placeholder="What is the color of the vehicle?">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Year: </label>
<input class="form-control form-control-lg" v-model="year" placeholder="What year is the car?">
</div>
<div class="form-group col-md-6">
<label for="service_tag">Comments: </label>
<input class="form-control form-control-lg" v-model="comments" placeholder="Place any additional comments here...">
</div>
<div class="form-group col-md-6 invisible">
<label for="service_tag">Valet: </label>
<input v-model="valet">
</div>
<div class="form-group col-md-6 invisible">
<label for="service_tag">Timestamp: </label>
<input v-model="reqTimestamp">
</div>
<div class="col-md-12">
<button class="btn btn-outline-primary" type="submit">Request A Key</button>
</div>
</form>
</div>
</div>
</div>
</template>
<script>
import { db } from "../main";
import { setInterval } from 'timers';
export default {
name: "HelloWorld",
data() {
return {
keys: [],
reqTimestamp: this.newDate(),
service_tag: "",
requestor_name: "",
comments: "",
color: "",
model: "",
year: "",
inputValet: true,
valet: "Unclaimed",
state: "started",
startTime: '',
currentTime: Date.now(),
interval: null,
};
},
firestore() {
return {
keys: db.collection("keyRequests").where("completion", "==", "Incomplete")
};
},
methods: {
newKey(service_tag, requestor_name, comments, model, year, color, valet, reqTimestamp, completion) {
// <-- and here
db.collection("keyRequests").add({
service_tag,
requestor_name,
comments,
color,
model,
year,
valet,
reqTimestamp,
completion: "Incomplete",
});
this.service_tag = "";
this.requestor_name = "";
this.comments = "";
this.color = "";
this.model = "";
this.year = "";
this.reqTimestamp = this.newDate()
},
complete(id) {
db.collection("keyRequests").doc(id).update({
completion: "Complete"
})
},
// deleteKey(id) {
// db.collection("keyRequests")
// .doc(id)
// .delete();
claimedKey(id, valet) {
console.log(id);
this.inputValet = false
db.collection("keyRequests").doc(id).update({
valet: valet,
claimTimestamp: new Date()
})
},
moment: function () {
return moment();
},
newDate () {
var today = new Date()
return today
},
updateCurrentTime: function() {
if (this.$data.state == "started") {
this.currentTime = Date.now();
}
},
start(timestamp) {
return this.startTime = timestamp.seconds * 1000
}
},
mounted: function () {
this.interval = setInterval(this.updateCurrentTime, 1000);
},
destroyed: function() {
clearInterval(this.interval)
},
computed: {
time: function() {
return Math.floor((this.currentTime - this.startTime) /60000);
}
}
}
</script>
Ideally I am looking for the time lapse to follow each request.
So the problem lines in the template are:
<p class="card-text"> {{time}} {{key.reqTimestamp}}min</p>
<p class="invisible">{{ start(key.reqTimestamp) }}</p>
The call to start has side-effects, which is a major no-no for rendering a component. In this case it changes the value of startTime, which in turn causes time to change. I'm a little surprised this isn't triggering the infinite rendering recursion warning...
Instead we should just use the relevant data for the current iteration item, which you've called key. I'd introduce a method that calculates the elapsed time given a key:
methods: {
elapsedTime (key) {
const timestamp = key.reqTimestamp;
const startTime = timestamp.seconds * 1000;
return Math.floor((this.currentTime - startTime) / 60000);
}
}
You'll notice this combines aspects of the functions start and time. Importantly it doesn't modify anything on this.
Then you can call it from within your template:
<p class="card-text"> {{elapsedTime(key)}} {{key.reqTimestamp}}min</p>
I've been battling with these block of codes for some days, and even when i try initializing it as an object i get errors.
this is my restaurantForm.ts file
import { Component, OnInit } from '#angular/core';
import {RestaurantService} from '../../restaurant.service';
import {ProductService} from '../../product.service';
import {ActivatedRoute, Router} from '#angular/router';
import 'rxjs/add/operator/take';
import {Product} from '../../model/product';
#Component({
selector: 'app-product-form',
templateUrl: './restaurant-form.component.html',
styleUrls: ['./restaurant-form.component.css']
})
export class RestaurantFormComponent implements OnInit {
restaurants$;
id;
product: Product;
constructor(private restaurantService: RestaurantService,
private productService: ProductService,
private route: ActivatedRoute,
private router: Router) {
this.restaurants$ = this.restaurantService.getAll();
this.id = this.route.snapshot.paramMap.get('id');
if (this.id) {
this.productService.get(this.id).take(1).subscribe(p => this.product = p);
}
this.product = new Product();
}
save(product) {
if (this.id) {
this.productService.update(this.id, product);
} else {
this.productService.create(product);
}
this.router.navigate(['/admin/restaurants']);
}
delete() {
if (!confirm('Are you sure you want to delete this product?')) { return ; }
this.productService.delete(this.id);
this.router.navigate(['/admin/restaurants']);
}
ngOnInit() {
}
}
this is my product model
export interface Product {
$key: string;
title: string;
price: number;
restaurant: string;
imageUrl: string;
}
My restaurantForm.html
<div class="container">
<div class="row">
<div class="col-md-6">
<form #f="ngForm" (ngSubmit)="save(f)">
<div class="form-group">
<label for="title">Title</label>
<input #title="ngModel" [(ngModel)]="product.title" name="title" id="title" type="text" class="form-control" required>
<div class="alert-danger alert" *ngIf="title.touched && title.invalid">
Title is required.
</div>
</div>
<div class="form-group">
<label for="price">Delivery Price</label>
<div class="input-group">
<span class="input-group-addon">₦</span>
<input #price="ngModel" [(ngModel)]="product.price" name="price" id="price"
type="number" class="form-control" required [min]="0">
</div>
<div class="alert alert-danger" *ngIf="price.touched && price.invalid">
<div *ngIf="price.errors.required">
Delivery Price is required
</div>
<div *ngIf="price.errors.min">
Delivery Price should be 0 or higher.
</div>
</div>
</div>
<div class="form-group">
<label for="restaurant">Restaurant</label>
<select #restaurant="ngModel" [(ngModel)]="product.restaurant" name="restaurant" id="restaurant" class="form-control" required>
<option value=""></option>
<option *ngFor="let r of restaurants$ | async" [value]="r.$key">
{{ r.name }}
</option>
</select>
<div class="alert alert-danger" *ngIf="restaurant.touched && restaurant.invalid">
Please select a restaurant.
</div>
</div>
<div class="form-group">
<label for="imageUrl">Image Url</label>
<input #imageUrl="ngModel" [(ngModel)]="product.imageUrl" name="imageUrl"
id="imageUrl" type="text" class="form-control" required url>
<div class="alert alert-danger" *ngIf="imageUrl.touched && imageUrl.invalid">
<div *ngIf="imageUrl.errors.required">Image Url is required.</div>
<div *ngIf="imageUrl.errors.url">Please enter a valid Url.</div>
</div>
</div>
<button class="btn btn-primary">Save</button>
<button type="button" (click)="delete()" class="btn btn-danger">Delete</button>
</form>
</div>
<div class="col-md-6">
<app-product-card [product]="product" [showActions]="false"></app-product-card>
</div>
</div>
</div>
I get the same errors with price, $key, restaurant and imageUrl.
thanks in advance. Although Ive looked up some solutions saying i should use elvis Operator e.g 'product?.title' this method isnt still working.
Because product is undefined, you need to declare and initialize it with empty object inside the constructor or ngOninit.
EDIT:
You need to have the Product declared in the component as,
const product : Produce = { $key: "", title: "",price:0,restuarant :"" ,imageurl:"" };
When the template get initialized, the product is undefined and it continues until the response return from API. Add a check for the template where you bind the object properties.Do the same for price etc.
<input *ngIf="product.title" #title="ngModel" [(ngModel)]="product.title" name="title" id="title" type="text" class="form-control" required>