I have a form that adds a branch, the form has a drop down to select the company name.
I created a viewModel that has the SelectListItem of the companies and the Branch Model
When submit a form, modelState.IsValid equals to false.
reason for that is because CompaniesList is required.
any idea why is it required? how can i overcome this?
Branch model:
public class Branch
{
public int Id { get; set; }
public int CompanyId { get; set; }
[MaxLength(50)]
public string? City { get; set; }
[MaxLength(50)]
public string? BranchName { get; set; }
public DateTime CreatedAt { get; set; }
[MaxLength(100)]
public string? CreatedBy { get; set; }
}
ViewModel:
public class BranchVM
{
public Branch branch { get; set; }
[AllowNull]
public IEnumerable<SelectListItem> CompaniesList { get; set; }
}
Create.cshtml:
#model Click2Lock.Models.BranchVM
<form method="post" enctype="multipart/form-data">
<div class="border p-3 mt-4">
<div class="row pb-2">
<h2 class="text-primary">Create Branch</h2>
<hr/>
</div>
<div class="col-8">
<label asp-for="branch.BranchName">Branch Name</label>
<input asp-for="branch.BranchName" class="form-control"/>
<span asp-validation-for="branch.BranchName" class="text-danger"></span>
</div>
<div class="col-8">
<label asp-for="branch.City">City</label>
<input asp-for="branch.City" class="form-control"/>
<span asp-validation-for="branch.City" class="text-danger"></span>
</div>
<div class="col-8 pb-4">
<div class="form-group row">
<div class="col-4">
<label asp-for="branch.CompanyId">Company</label>
</div>
<div class="col-8">
#Html.DropDownListFor(m => m.branch.CompanyId, Model.CompaniesList , "Select Order",
new { #class = "form-control" })
<span asp-validation-for="branch.CompanyId" class="text-danger"></span>
</div>
</div>
</div>
<div class="col-8">
<input type="hidden" asp-for="branch.CreatedAt" class="form-control" value="#DateTime.Now" />
</div>
<div class="col-8">
<input type="hidden" asp-for="branch.CreatedBy" class="form-control" value=#ViewBag.userName />
</div>
<button type="submit" class="btn btn-primary" style="width:200px">Add New Branch</button>
<a asp-controller="Company" asp-action="Index" class="btn btn-secondary" style="width:150px">
Back To List
</a>
</div>
</form>
create on Controller :
public IActionResult Create()
{
ViewBag.userName = (_unitOfWork.ApplicationUser.GetAll().
Where(q => q.UserName == User.Identity.Name).Select(q => q.FullName)).FirstOrDefault();
BranchVM branchVM = new BranchVM()
{
branch = new Branch(),
CompaniesList = _unitOfWork.Company.GetAll().OrderBy(a=>a.CompanyName).
Select(i => new SelectListItem
{
Text = i.CompanyName,
Value = i.Id.ToString()
})
};
return View(branchVM);
}
//POST
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(BranchVM branchVM)
{
ViewBag.msgCreate = 0;
ViewBag.msgGeneralException = 0;
if (ModelState.IsValid)
{
try
{
_unitOfWork.Branch.Add(branchVM.branch);
_unitOfWork.Save();
ViewBag.msgCreate = 1;
return View(branchVM);
}
catch (Exception ex)
{
ViewBag.msgGeneralException = 1;
return View(branchVM);
}
}
ViewBag.msgGeneralException = 1;
return View(branchVM);
}
One technique is to make it nullable:
public IEnumerable<SelectListItem>? CompaniesList { get; set; }
Related
I'm trying to get the result of my enum for a location as a string but for some reason, it's only showing it as an integer in the index page.
Model
public class UserLicense
{
[Key]
public string Location { get; set; }
}
Enum Class
public enum LocationType
{
Brazil,
USA,
UK
//etc .. here i will add around another 30 items
}
Controller
public async Task<IActionResult> Create(IFormCollection col)
{
if (!User.Identity.IsAuthenticated) RedirectToAction("Index", "Home");
UserLicense uc = new UserLicense();
if (ModelState.IsValid)
{
try
{
DateTime? ends = null;
if (DateTime.TryParse(col["ends"], out DateTime tmp)) ends = tmp;
string username = col["user.username"].ToString().Normalize();
var user = _context.Users.FirstOrDefault(x => x.NormalizedUsername == username);
uc = new UserLicense
{
Ends = ends,
Starts = DateTime.Parse(col["starts"]),
UserId = user.Id,
LicenseId = int.Parse(col["licenseid"]),
Location = Enum.GetName(typeof(LocationType),col["Location"]), //the issue is here.
};
_context.Add(uc);
await _context.SaveChangesAsync();
Create HTML
<div class="form-group">
<label asp-for="Location" class="control-label"></label>
<select asp-for="Location" class="form-control" asp-items="#Html.GetEnumSelectList<LocationType>()"></select>
<span asp-validation-for="Location" class="text-danger"></span>
</div>
Since you are using IFormCollection to transfer the parameters to the controller, not matter using public string Location { get; set; } or public string Location { get; set; }, in the controller the Location value in the IFormCollection will be always an integer. So, you could based on the int value to get string value from the Enum:
var location = ((LocationType)int.Parse(col["location"])).ToString();
The result like this:
Besides, there has another solution, you could use the Strongly typed data (use UserLicense model in the action method, instead of IFormCollection) to transfer the parameter to the controller.
Code like this:
public class UserLicense
{
[Key]
public int License { get; set; }
public DateTime Ends { get; set; }
public DateTime Starts { get; set; }
public int UserId { get; set; }
public LocationType Location { get; set; }
}
public enum LocationType
{
Brazil,
USA,
UK
}
and
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(UserLicense userLicense)
{
if (ModelState.IsValid)
{
var location = userLicense.Location;
return RedirectToAction(nameof(Index));
}
return View();
}
Code in the Create.cshtml:
#model WebApplication1.Models.UserLicense
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Create</h1>
<h4>UserLicense</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="License" class="control-label"></label>
<input asp-for="License" class="form-control" />
<span asp-validation-for="License" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Ends" class="control-label"></label>
<input asp-for="Ends" class="form-control" />
<span asp-validation-for="Ends" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Starts" class="control-label"></label>
<input asp-for="Starts" class="form-control" />
<span asp-validation-for="Starts" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="UserId" class="control-label"></label>
<input asp-for="UserId" class="form-control" />
<span asp-validation-for="UserId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Location" class="control-label"></label>
<select asp-for="Location" class="form-control" asp-items="#Html.GetEnumSelectList<LocationType>()"></select>
<span asp-validation-for="Location" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
The screenshot as below:
Make your Location property a type of LocationType instead of string.
public class UserLicense
{
[Key]
public LocationType Location { get; set; }
}
Pass Location to the view from the Create action like this
uc = new UserLicense
{
Ends = ends,
Starts = DateTime.Parse(col["starts"]),
UserId = user.Id,
LicenseId = int.Parse(col["licenseid"]),
Location = uc.Location
};
Then in the view
<select asp-for="Location" class="form-control" asp-items="#Html.GetEnumSelectList<LocationType>()">
<option selected="selected" value="">Please Select</option>
</select>
I have two models and one join table.
I would like to use the same view to edit and create new purchase order while adding items in the same form.
PurchaseOrder Model:
public class PurchaseOrder
{
public int ID { get; set; }
[Required]
public string Requester { get; set; }
public int WorkOrder { get; set; }
[Required]
public string WorkSite { get; set; }
public string Equipment { get; set; }
public string Operator { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> Items { get; set; }
}
Item Model:
public class Item
{
public int ID { get; set; }
public string PartNumber { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
[Required]
public int Quantity { get; set; }
public string Remarks { get; set; }
public ICollection<PurchaseOrderItem> PurchaseOrders { get; set; }
}
Join Table Entity
public class PurchaseOrderItem
{
public int PurchaseOrderID { get; set; }
public int ItemID { get; set; }
public PurchaseOrder PurchaseOrder { get; set; }
public Item Item { get; set; }
}
PurchaseOrdersController Edit:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var purchaseOrder = await _context.PurchaseOrders
.Include(x => x.Items)
.ThenInclude(x => x.Item)
.Where(x => x.ID == id)
.AsNoTracking()
.SingleOrDefaultAsync();
if (purchaseOrder == null)
{
return NotFound();
}
return View(purchaseOrder);
}
Edit Post Method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, PurchaseOrder order)
{
if (id != order.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
var po = _context.PurchaseOrders.FirstOrDefault(i => i.ID == order.ID);
po.Requester = order.Requester;
po.WorkOrder = order.WorkOrder;
po.WorkSite = order.WorkSite;
po.Equipment = order.Equipment;
po.Operator = order.Operator;
po.Remarks = order.Remarks;
_context.Update(po);
foreach (var i in order.Items)
{
var item = _context.Items.FirstOrDefault(n => n.ID == i.Item.ID);
item.PartNumber = i.Item.PartNumber;
item.Name = i.Item.Name;
item.Description = i.Item.Description;
item.Quantity = i.Item.Quantity;
item.Remarks = i.Item.Remarks;
_context.Update(item);
}
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PurchaseOrderExists(order.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(order);
}
Edit View
#model OrderTrackingApp.Models.PurchaseOrder
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>PurchaseOrder</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Requester" class="control-label"></label>
<input asp-for="Requester" class="form-control" />
<span asp-validation-for="Requester" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkOrder" class="control-label"></label>
<input asp-for="WorkOrder" class="form-control" />
<span asp-validation-for="WorkOrder" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="WorkSite" class="control-label"></label>
<input asp-for="WorkSite" class="form-control" />
<span asp-validation-for="WorkSite" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Equipment" class="control-label"></label>
<input asp-for="Equipment" class="form-control" />
<span asp-validation-for="Equipment" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Operator" class="control-label"></label>
<input asp-for="Operator" class="form-control" />
<span asp-validation-for="Operator" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Remarks" class="control-label"></label>
<input asp-for="Remarks" class="form-control" />
<span asp-validation-for="Remarks" class="text-danger"></span>
</div>
<div class="form-group">
<label class="control-label">Order Items</label>
<table>
<tbody>
#{ int i = 0;}
<tr>
#foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" value="OrderItem[#i].ItemID" asp-for="#OrderItem.ItemID"
class="form-control" />
<input type="text" value="OrderItem[#i].Item.PartNumber" asp-for="#OrderItem.Item.PartNumber" class="form-control" />
<input type="text" value="OrderItem[#i].Item.Name" asp-for="#OrderItem.Item.Name" />
</td>
i++;
}
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I have two problems currently with the Edit:
The label for the item populates as follows: OrderItem[0].Item.Name instead of Value
When the code reaches the foreachloop to iterate over items, it is throwing a null exception.
You are getting name only as for newly added child items, you don't have ids. and getting null values as in returned data for relational table ids of child data are not initialized.
you can solve this issue in tow ways as per suitability:
Add child rows from view directly and update ids in model, so you
will always have ids in data and you will not need face null ids
issue.
Alternatively, You may need to add new values of child table first
then use new ids in relational data manually before saving changes.
The problem is model binding, it is based on the name attribute. Change the code like below:
#{ int i = 0;}
<tr>
#foreach (var OrderItem in Model.Items)
{
<td>
<input type="hidden" name="Items[#i].Item.ID" asp-for="#OrderItem.Item.ID"
class="form-control" />
<input type="text" name="Items[#i].Item.PartNumber" asp-for="#OrderItem.Item.PartNumber" class="form-control" />
<input type="text" name="Items[#i].Item.Name" asp-for="#OrderItem.Item.Name" class="form-control" />
</td>
i++;
}
</tr>
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 3 years ago.
I am trying to create a view model that allows the user to create a new product by typing in the products new name and then select which parent category it belongs to, which then filters the sub category and the user selects the appropriate sub-category. I am currently just trying to have both a drop down list of all parent categories and a drop down list of all subcategories, but I am getting a null refernce for the parent category in the view.
Models
public class Product
{
[Key]
public int ProductID { get; set; }
[Required]
[Display(Name = "Item Name")]
public string ProductName { get; set; }
public int ProductSubcategoryID { get; set; }
[Required]
public ProductSubcategory ProductSubcategory { get; set; }
}
public class ProductSubcategory
{
[Key]
public int ProductSubcategoryID { get; set; }
[Required]
public int ParentCategoryID { get; set; }
public ParentCategory ParentCategory { get; set; }
[Required]
[Display(Name = "Type")]
public string ProductSubcategoryDescription { get; set; }
}
public class ParentCategory
{
[Key]
public int ParentCategoryID { get; set; }
[Required]
public string ParentCategoryDescription { get; set; }
public IEnumerable<ProductSubcategory> ProductSubcategories { get; set; }
}
View Model
public class CreateProductViewModel
{
public IEnumerable<ParentCategory> ParentCategories{ get; set; }
public IEnumerable<ProductSubcategory> ProductSubcategories { get; set; }
public Product Product { get; set; }
}
Controllers
// GET: Products/Create
public IActionResult Create()
{
var parentCategories = _context.ParentCategories.ToList();
var productSubcategories = _context.ProductSubcategories.ToList();
var viewModel = new CreateProductViewModel
{
ParentCategories = parentCategories,
ProductSubcategories = productSubcategories
};
return View(viewModel);
}
// POST: Products/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ProductID,ProductName,VendorID,ProductSubcategoryID,ParentCategoryID,LocationID,QuantityPerUnit,UnitsInStock,UnitsInOrder,ReorderLevel,ProductComment,ProductMedia")]Product product)
{
_context.Add(product);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
view
#model PrototypeWithAuth.ViewModels.CreateProductViewModel
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/View.cshtml";
}
<h1>Create</h1>
<h4>Product</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Product.ProductName" class="control-label"></label>
<input asp-for="Product.ProductName" class="form-control" />
<span asp-validation-for="Product.ProductName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.ProductSubcategoryID" class="control-label"></label>
<!--<input asp-for="Product.ProductSubcategoryID" class="form-control" />-->
#Html.DropDownListFor(s => s.Product.ProductSubcategoryID,
new SelectList(Model.ProductSubcategories, "ProductSubcategoryID", "ProductSubcategoryDescription"),
"Select Subcategory", new { #class = "form-control" })
<span asp-validation-for="Product.ProductSubcategoryID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.ProductSubcategory.ParentCategoryID" class="control-label"></label>
<!--<input asp-for="ProductSubcategoryID" class="form-control" />-->
#Html.DropDownListFor(c => c.Product.ProductSubcategory.ParentCategoryID,
new SelectList(Model.ParentCategories, "ParentCategoryID", "ProductSubcategoryDescription"),
"Select Category", new { #class = "form-control" })
<span asp-validation-for="Product.ProductSubcategory.ParentCategoryID" class="text-danger"></span>
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
When trying to add a new product I get a null reference for ParentCategory, but if I do not us a html helper tag for a dropdown than the form works. Thank you in advance
The SelectList is used like below
public SelectList(IEnumerable items, string dataValueField, string dataTextField)
In you case,ProductSubcategoryDescription is not the field of ParentCategory leading to the error.So change it to
#Html.DropDownListFor(c =>
c.Product.ProductSubcategory.ParentCategoryID,
new SelectList(Model.ParentCategories, "ParentCategoryID", "ParentCategoryDescription"),
"Select Category", new { #class = "form-control" })
create a new product by typing in the products new name and then select which parent category it belongs to, which then filters the sub category and the user selects the appropriate sub-category.
If you want to get the subcategories when you choose the parent category, you could use some JavaScript, for example:
View:
#model PrototypeWithAuth.ViewModels.CreateProductViewModel
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/View.cshtml";
}
<h1>Create</h1>
<h4>Product</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Product.ProductName" class="control-label"></label>
<input asp-for="Product.ProductName" class="form-control" />
<span asp-validation-for="Product.ProductName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Product.ProductSubcategory.ParentCategoryID" class="control-label"></label>
<!--<input asp-for="ProductSubcategoryID" class="form-control" />-->
#Html.DropDownListFor(c => c.Product.ProductSubcategory.ParentCategoryID,
new SelectList(Model.ParentCategories, "ParentCategoryID", "ParentCategoryDescription"),
"Select Category", new { #class = "form-control",#id="parentlist" })
<span asp-validation-for="Product.ProductSubcategory.ParentCategoryID" class="text-danger"></span>
</div>
<div>
<select name="Product.ProductSubcategoryID" id="sublist" class="form-control"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
//Insert default item "Select" in dropdownlist on load
$(document).ready(function () {
var items = "<option value='0'>Select</option>";
$("#sublist").html(items);
});
$("#parentlist").change(function () {
var parentCategoryId = $("#parentlist").val();
var url = "/Products/GetSubCategoryList";
$.getJSON(url, { ParentCategoryId: parentCategoryId }, function (data) {
var item = "";
$("#sublist").empty();
$.each(data, function (i, subCategory) {
item += '<option value="' + subCategory.productSubcategoryID + '">' + subCategory.productSubcategoryDescription + '</option>'
});
$("#sublist").html(item);
});
});
</script>
}
ProductsController:
[HttpGet]
public JsonResult GetSubCategoryList(int ParentCategoryId)
{
var subCategoryList = _context.ProductSubcategories.Where(c => c.ParentCategoryID == ParentCategoryId).ToList();
return Json(subCategoryList);
}
I am trying to add data to the database. I experimenting with Blazor and .NET core:
This is my code in the controller:
[Route("AddCarBlazor")]
[HttpPost]
public IActionResult PostBlazor(Car car)
{
if (car.CarId == 0)
{
// New
car.Created = DateTime.Now;
_context.Cars.Add(car);
_context.SaveChanges();
return Ok();
}
else
{
// Update
var c = _context.Cars.First(e => e.CarId == car.CarId);
c.Brand = car.Brand;
c.Color = car.Color;
c.Model = car.Model;
c.LastChange = DateTime.Now;
c.TopSpeed = car.TopSpeed;
_context.SaveChanges();
return Ok();
}
}
My car model looks like this:
public class Car
{
[Key]
public long CarId { get; set; }
public string Created { get; set; }
public string LastChange { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
public string Color { get; set; }
public long TopSpeed { get; set; }
}
I call this method like this:
private async Task AddCar()
{
await Http.PostJsonAsync(baseUrl + "/AddCarBlazor/", carobject);
await Refresh();
}
When I fill in the form and press add button the car object is always null
This is my form with the databinding:
<form>
<div class="row">
<div class="form-group col-sm-3">
<label>Brand</label>
<input input type="text" #bind="#carobject.Brand" class="form-control" placeholder="Enter brand" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-3">
<label>Model</label>
<input type="text" #bind="#carobject.Model" class="form-control" placeholder="Enter model" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-3">
<label>Color</label>
<input type="text" #bind="#carobject.Color" class="form-control" placeholder="Enter color" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-3">
<label>TopSpeed</label>
<input type="number" #bind="#carobject.TopSpeed" class="form-control" placeholder="Enter speed" />
</div>
</div>
<div class="btn-group mr-2">
<button class="btn btn-danger mr-1" onclick=#AddCar>Save changes</button>
</div>
</form>
I have put a breakpoint on the addCar method. I get the values from the fields but when it goes to the controller it becomes null.
I have following this tutorial:
https://learn.microsoft.com/en-us/aspnet/core/blazor/call-web-api?view=aspnetcore-3.0
How can I save the values from the fields and send it to the database?
I test a demo which works well, you could refer to my code below:
1.Car.cs (namespace Blazor.Models)
public class Car
{
public long CarId { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
}
2. AddCar.razor
#page "/car"
#using System.Net.Http
#inject HttpClient Http
#using Blazor.Models
<Editform Model="carobject">
<div class="row">
<div class="form-group col-sm-3">
<label>Brand</label>
<input #bind="#carobject.Brand" class="form-control" placeholder="Enter brand" />
</div>
</div>
<div class="row">
<div class="form-group col-sm-3">
<label>Model</label>
<input #bind="#carobject.Model" class="form-control" placeholder="Enter model" />
</div>
</div>
<div class="btn-group mr-2">
<button class="btn btn-danger mr-1" onclick="#AddCar">Save changes</button>
</div>
</Editform>
#functions {
[Parameter]
private Car carobject { get; set; } = new Car();
private async Task AddCar()
{
await Http.PostJsonAsync(baseUrl + "/AddCarBlazor/", carobject);
//await Refresh();
}
}
3.Web API CORS configuration:
app.UseCors(corsbuilder => {
corsbuilder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
});
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
4.action:
[Route("AddCarBlazor")]
[HttpPost]
public IActionResult PostBlazor([FromBody]Car car)
After a weekend of research I have the solution!
I have changed my method in CarService.cs like this:
public async Task AddCar(Car car)
{
var client = new HttpClient { BaseAddress = new Uri("https://localhost:44369/api/car/") };
await client.SendJsonAsync(HttpMethod.Post, "AddCar", car);
}
Then I call this method in my razor page like this:
async Task AddCar()
{
await CarService.AddCar(car);
car = new CarService.Car();
await LoadCarData();
}
I also made a new object of the service like this:
CarService.Car car = new CarService.Car();
And I moved the model of Car.cs into CarService.cs
I have a class which has a collection of the activity lines of the heder which is a one to many linked by activitylines id.
public class ActivityHeader
{
public int ActivityHeaderId { get; set; } //(int, null)
public DateTime? ActivityDate { get; set; } //(date, null)
public string Name { get; set; } //(nvarchar(350), null)
public DateTime? ActivityEndDate { get; set; } //(datetime, null)
public string ProblemDescription { get; set; }
public string Description { get; set; } //(nvarchar(max), null)
public int? ActivityLinesId { get; set; } //(int, null)
public int? HoursLeftOnProject { get; set; } //(time(7), null)
public int? Status { get; set; } //(nchar(10), null)
public DateTime? CreatedDate { get; set; } //(date, null)
public string CreatedBy { get; set; } //(nvarchar(50), null)
public bool? isActive { get; set; } //(bit, null)
public bool? isDeleted { get; set; } //(bit, null)
public bool? isArchived { get; set; } //(bit, null)
public int? SOP { get; set; } //(nvarchar(50), null)
public int? OnSite { get; set; }
public int? Remote { get; set; }
public int? DepartmentId { get; set; } //(int, null)
public string EmployeeName { get; set; } //(nvarchar(301), null)
[ForeignKey("StaffId")]
public int? StaffId { get; set; }
public virtual StaffMembers StaffMembers { get; set; }
public ICollection<ActivityLines> ActivityLines { get; set; }
}
Activity Lines classs
public class ActivityLines
{
[Key]
public int ActivityLineId { get; set; } //(int, not null)
public int ActivitiyHeadId { get; set; } //(int, null)
public string Description { get; set; } //(nvarchar(max), null)
public string Notes { get; set; } //(nvarchar(max), null)
public DateTime StartTime { get; set; } //(time(7), null)
public DateTime EndTime { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; } //(time(7), null)
public int? StaffMemeber { get; set; } //(int, null)
public bool? isActive { get; set; } //(bit, null)
public bool? isDeleted { get; set; } //(bit, null)
public string CreatedBy { get; set; } //(nvarchar(50), null)
public DateTime? CreatedDate { get; set; } //(date, null)
public int? Status { get; set; } //(int, null)
}
However as you see it not allowing me to link without going to the first record using linq what is the best practise in being able to reference the lines correclty.
#model ActivityTrackerDal.ViewModels.ActivityEditViewModal
<div class="container py-5">
<div class="row">
<div class="col-md-10 mx-auto">
<form>
<div class="form-group row">
<div class="col-sm-9">
<label for="inputFirstname">Activty Name</label>
<input type="text" class="form-control" id="inputFirstname" placeholder="Activity name">
</div>
</div>
<div class="form-group row">
<div class="col-sm-3">
<label for="inputLastname" class="form-control">Activity Start Date</label>
#Html.Kendo().DateTimePickerFor(model => model.ActivityDate)
</div>
<div class="col-sm-3">
<label for="inputLastname" class="form-control">Activity End Date</label>
#Html.Kendo().DateTimePickerFor(model => model.ActivityEndDate)
</div>
</div>
<div class="form-group row">
<div class="col-sm-3">
<label for="inputLastname" class="form-control">Location</label>
#foreach (var item in (SelectList)ViewBag.Location)
{
#Html.RadioButtonFor(model => model.OnSite, item.Value, false)
<label class="control-label">#item.Text</label>
}
</div>
</div>
<div class="form-group row">
<div class="col-md-10">
<label for="inputLastname" class="form-control">Description</label>
#Html.TextAreaFor(model => model.Description, new { #class = "whatever-class", #cols = 115, #rows = 10 })
</div>
</div>
<div class="form-group row">
<div class="col-sm-6">
<label for="inputCity">Status </label>
<select asp-for="Status"
class="form-control"
asp-items="#(new SelectList(#ViewBag.ProjectStatusTypes,"LookupCode", "LookupDescription"))"></select>
</div>
<div class="col-sm-3">
<label for="inputState">ActivityType </label>
<select asp-for="ActivityType"
class="form-control"
asp-items="#(new SelectList(#ViewBag.ProjectTypes,"LookupCode", "LookupDescription"))"></select>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6">
<label for="inputCity">Staff </label>
<select asp-for="StaffID"
class="form-control"
asp-items="#(new SelectList(#ViewBag.ListOfStaff,"StaffID", "FirstName"))"></select>
</div>
<div class="col-sm-3">
<label for="inputState">Hours Left On Project </label>
<label for="inputState"><div class="badge" style="font-size:18px;">26</div> </label>
<label for="projecthours">If Porject hours fall below ten Contact Charlie.</label>
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
#(Html.Kendo().Grid<FuelActivityTrackerDal.Models.ActivityLines>().Name("activityLines")
.Columns(columns =>
{
columns.Bound(p => p.Description).Filterable(false);
columns.Bound(p => p.StartTime).Filterable(false);
columns.Bound(p => p.EndTime).Filterable(false);
columns.Bound(p => p.Status);
columns.Command(command => command.Custom("ViewDetails").Click("showDetails"));
})
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Model(model => model.Id(p => p.ActivityLineId))
.Read(read => read.Action("ActivityLines_Read", "Activity"))))
</div>
</div>
<div class="form-group row">
<div class="col-sm-6">
</div>
</div>
<button type="button" class="btn btn-primary px-4 float-right">Add Work Item</button>
<button type="button" class="btn btn-primary px-4 float-right">Put Case & Client On Hold</button>
<button type="button" class="btn btn-primary px-4">Cancel</button>
</form>
</div>
</div>
#(Html.Kendo().Window().Name("Details")
.Title("Activity Details")
.Visible(false)
.Modal(true)
.Draggable(true)
.Width(400)
)
<script type="text/x-kendo-template" id="template">
<form method="post" action="#Url.Action("SaveWorkItem", "Activity")">
<div id="details-container">
ActivitiyHeadId
<div class="form-group row">
<div class="col-sm-9">
<label for="inputFirstname">Activty Name</label>
<input type="text" class="form-control" id="inputFirstname" placeholder="Activity name">
</div>
</div>
<div class="form-group row">
<div class="col-md-10">
<label for="inputLastname" class="form-control">Description</label>
#Html.Kendo().TimePickerFor(model => model.ActivityLines.First(), new { #class = "whatever-class", #cols = 115, #rows = 10 })
</div>
</div>
<div class="form-group row">
<div class="col-md-6">
<label for="inputLastname" class="form-control">Start Time</label>
</div>
<div class="col-md-6">
<label for="inputLastname" class="form-control">End Time </label>
</div>
</div>
</div>
<input type="submit" class="btn btn-file px-4" value="Save Work Item" />
<button type="button" class="btn btn-primary px-4">Cancel</button>
</form>
</script>
<script type="text/javascript">
var detailsTemplate = kendo.template($("#template").html());
function showDetails(e) {
e.preventDefault();
var dataItem = this.dataItem($(e.currentTarget).closest("tr"));
var wnd = $("#Details").data("kendoWindow");
wnd.content(detailsTemplate(dataItem));
wnd.center().open();
}
</script>
</div>
Its this popup within I am having the issue with being able to reference the the activity lines
<script type="text/x-kendo-template" id="template">
<form method="post" action="#Url.Action("SaveWorkItem", "Activity")">
<div id="details-container">
ActivitiyHeadId
<div class="form-group row">
<div class="col-sm-9">
<label for="inputFirstname">Activty Name</label>
<input type="text" class="form-control" id="inputFirstname" placeholder="Activity name">
</div>
</div>
<div class="form-group row">
<div class="col-md-10">
<label for="inputLastname" class="form-control">Description</label>
#Html.Kendo().TimePickerFor(model => model.ActivityLines.First(), new { #class = "whatever-class", #cols = 115, #rows = 10 })
</div>
</div>
<div class="form-group row">
<div class="col-md-6">
<label for="inputLastname" class="form-control">Start Time</label>
</div>
<div class="col-md-6">
<label for="inputLastname" class="form-control">End Time </label>
</div>
</div>
</div>
<input type="submit" class="btn btn-file px-4" value="Save Work Item" />
<button type="button" class="btn btn-primary px-4">Cancel</button>
</form>
</script>
This is the main block from above I am having the issue with.
<div class="form-group row">
<div class="col-md-10">
<label for="inputLastname" class="form-control">Description</label>
#Html.Kendo().TimePickerFor(model => model.ActivityLines.First(), new { #class = "whatever-class", #cols = 115, #rows = 10 })
</div>
</div>
Do I need to use include here on the ActivityHeader get statement like I have done for the staff?.
public List<ActivityEditViewModal> GetAllActivites()
{
var staffRepo = new StaffRepositry(_db);
List<ActivityHeader> activity = new List<ActivityHeader>();
activity = _db.ActivityHeader.AsNoTracking()
.Include(x => x.StaffMembers)
.ToList();
if (activity != null)
{
List<ActivityEditViewModal> activityDisplay = new List<ActivityEditViewModal>();
foreach (var x in activity)
{
var customerDisplay = new ActivityEditViewModal()
{
ActivityHeaderId = x.ActivityHeaderId,
ActivityDate = x.ActivityDate,
Name = x.Name,
ActivityEndDate = x.ActivityEndDate,
Description = x.Description
};
activityDisplay.Add(customerDisplay);
}
return activityDisplay;
}
return null;
}
Ok So this is what I had to do I had to use the function called include which is explained in this article on Microsoft web site. Its called defered loading and by the reason of this is EF6 used to take forever to load one to many relationships actually a problem i had with an old app as EF6 uses lazy loading which always loaded all one to many relationships now you can load them when you require them.
https://learn.microsoft.com/en-us/ef/core/querying/related-data
public List<ActivityEditViewModal> GetAllActivites()
{
var staffRepo = new StaffRepositry(_db);
List<ActivityHeader> activity = new List<ActivityHeader>();
activity = _db.ActivityHeader.AsNoTracking()
.Include(x => x.StaffMembers)
.Include(x=>x.ActivityLines)
.ToList();
if (activity != null)
{
List<ActivityEditViewModal> activityDisplay = new
List<ActivityEditViewModal>();
foreach (var x in activity)
{
var customerDisplay = new ActivityEditViewModal()
{
ActivityHeaderId = x.ActivityHeaderId,
ActivityDate = x.ActivityDate,
Name = x.Name,
ActivityEndDate = x.ActivityEndDate,
Description = x.Description,
ActivityLines = x.ActivityLines
};
activityDisplay.Add(customerDisplay);
}
return activityDisplay;
}