I have an app where students can register and login and having a field called city.
I'm trying to make a dropdown of cities so the student can pick which city he wants from there. It's a long list so I made a function in StudentService which will return array of strings and I'm trying to send it to the form using model.attribute but it's not working.
I tried using ajax and JQuery with external json file of the cities and it still didn't work even after I looked several questions here. If you have any idea how it is best to handle it please let me know.
here is the code so far and what I tried:
Service:
public class StudentService implements StudentInterfaceService {
// regular function of get, create, delete, update
public String [] getCities() {
String [] cities = { "lots of cities here };
return cities;
}
}
Controller:
public class RegistrationController {
#Autowired
private StudentService studentService;
public RegistrationController(StudentService studentService){
super();
this.studentService = studentService;
}
#ModelAttribute("student")
public StudentRegistration studentInfo() {
return new StudentRegistration();
}
#GetMapping
public String showRegistrationForm(Model model) {
String [] cities = studentService.getCities();
model.addAttribute("cities", cities);
return "registration";
}
#PostMapping
public String registerStudent(#ModelAttribute("student") StudentRegistration studentInfo) {
studentService.saveStudent(studentInfo);
return "redirect:/registration?success";
}
}
Html:
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="ISO-8859-1">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<!-- success message -->
<div th:if="${param.success}">
<div class="alert alert-info">You've successfully registered
to our awesome app!</div>
</div>
<h1>Registration</h1>
<form th:action="#{/registration}" method="post" th:object="${student}">
<div class="form-group">
<label class="control-label" for="username"> Username </label>
<input id="username" class="form-control" th:field="*{username}"
required autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label" for="studentName"> Student Name </label> <input
id="studentName" class="form-control" th:field="*{studentName}"
required autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label" for="password"> Password </label> <input
id="password" class="form-control" type="password"
th:field="*{password}" required autofocus="autofocus" />
</div>
<div class="form-group">
<select th:field="*{city}" th:each="city: ${cities}">
<option th:value="city" th:text="city"></option>
</select>
</div>
<div class="form-group">
<label class="control-label" for="phoneNum"> Phone Number </label> <input
id="phoneNum" class="form-control" th:field="*{phoneNum}" required
autofocus="autofocus" />
</div>
<div class="form-group">
<label class="control-label"> Gender: </label>
<select th:field="*{gender}">
<option th:value="'Male'" th:text="Male"></option>
<option th:value="'Female'" th:text="Female"></option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Register</button>
<span>Already registered? <a href="/" th:href="#{/login}">Login
here</a></span>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
I also tried using ajax in this way and it didn't work:
<script>
$.ajax({
url:'/cities',
type:'GET',
dataType: 'json',
success: function( json ) {
$.each(json, function(i, value) {
$('#cities').append($('<option>').text(value).attr('value', value.value));
});
}
});
</script>
EDIT:
Student model have these fields:
private String username;
private String studentName;
private String password;
private String city;
private String phoneNum;
private String gender;
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'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.
I have a normal input fields like this:
When i click on that "+" button this action happens and the service class is called as I have simple Json data arriving.I want to assign the selectionCustomOffice.custOfficeName=json data's.custOffcName; but I am getting the undefined result.
addedO(selectionCustomOffice:SelectionCustomOffice){
this.scoe=true;
this.selectionCustomOfficeService.getSingleCustomOffice(selectionCustomOffice.customOfficeId).subscribe((data)=>{
console.log("entered");
selectionCustomOffice.custOfficeName=data.custOffcName;
console.log( selectionCustomOffice.custOfficeName);
},(error)=>{
console.log(error);
});
this.empList.push(selectionCustomOffice);
this.selectionCustomOffice=new SelectionCustomOffice();
console.log(this.empList);
}
this.selectionCustomOfficeService.getSingleCustomOffice(selectionCustomOffice.customOfficeId).subscribe((data)=>{
console.log("entered");
selectionCustomOffice.custOfficeName=data.custOffcName;
console.log( selectionCustomOffice.custOfficeName);
},(error)=>{
console.log(error);
});
SelectionCustomOffice.ts
export class SelectionCustomOffice {
id: number;
fromDate: string;
toDate: string;
consignmentNo: number;
selectionId: number;
customOfficeId: number;
custOfficeName: string;
}
The form to send data is: I have used this field custom office as select field.
<div class="form-group row">
<div class="col-md-4">
<label>Custom Office</label>
</div>
<div class="col-md-2">
<label>From Date</label>
</div>
<div class="col-md-2">
<label>To Date</label>
</div>
<div class="col-md-4">Consignment No</div>
<div class="col-md-4">
<select class="form-control" id="customOfficeId" required [(ngModel)]="selectionCustomOffice.customOfficeId" name="customOfficeId"
>
<option *ngFor="let title of customoffices" [value]="title.custOfficeId">{{title.custOffcName}}</option>
</select>
</div>
<div class="col-md-2">
<input type="date" class="form-control" id="fromDate" required [(ngModel)]="selectionCustomOffice.fromDate" name="fromDate" />
</div>
<div class="col-md-2">
<input type="date" class="form-control" id="toDate" required [(ngModel)]="selectionCustomOffice.toDate" name="toDate" />
</div>
<div class="col-md-3">
<input type="number" class="form-control" id="consignmentNo" required [(ngModel)]="selectionCustomOffice.consignmentNo" name="consignmentNo">
</div>
<div class="col-md-1">
<button type="button" (click)="addedO(selectionCustomOffice)">+</button>
</div>
</div>
service class
getSingleCustomOffice(id: number): Observable<any> {
return this.http.get(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
I think you have a small problem - You are reading your response as text
getSingleCustomOffice(id: number): Observable<any> {
return this.http.get(`${this.baseUrl}/${id}`, { responseType: 'text' });
}
Remove the { responseType: 'text' } from the http call or use JSON.parse(data) when you are reading data - { responseType: 'text' } this will return your response as string
Try like this if you want your response as text
this.selectionCustomOfficeService.getSingleCustomOffice(selectionCustomOffice.customOfficeId).subscribe((data)=>{
console.log("entered");
let outPut = JSON.parse(data);
selectionCustomOffice.custOfficeName=outPut.custOffcName;
console.log( selectionCustomOffice.custOfficeName);
},(error)=>{
console.log(error);
});
I think this might solve your problem - Happy coding !!
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>