Unable to process binding "event: function(){return {change:flagSalesOrderItemAsEdited} }" - asp.net-mvc

I am currently doing a Pluralsight course on Knockout and MVC (called Parent-Child Data with EF, MVC, Knockout, Ajax, and Validation) which I have been very impressed with, but suddenly I get this bug which has so far been a show stopper for me.
Not only is it a problem in my code, it is also a bug in the very code downloaded from Pluralsight that I saw working on their video!
So in the Edit Partial View I have:
<h2>#ViewBag.Title</h2>
<p data-bind="text: MessageToClient"></p>
<div>
<div class="form-group">
<label class="control-label" for="CustomerName">Customer Name:</label>
<input class="form-control" name="CustomerName" id="CustomerName"
data-bind="value: CustomerName, event: {change: flagSalesOrderAsEdited}"/>
</div>
<div class="form-group">
<label class="control-label" for="PONumber">P.O. Number:</label>
<input class="form-control" name="PONumber" id="PONumber"
data-bind="value: PONumber, event: {change: flagSalesOrderAsEdited}"/>
</div>
</div>
<table class="table table-stripe">
<tr>
<th>Product Code</th>
<th>Quantity</th>
<th>Unit Price</th>
<th><button data-bind="click: addSalesOrderItem" class="btn btn-info btn-xs">Add</button></th>
</tr>
<tbody data-bind="foreach: SalesOrderItems">
<tr>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: Quantity, event: {change: flagSalesOrderItemAsEdited}" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: UnitPrice, event: {change: flagSalesOrderItemAsEdited}" />
</td>
<td class="form-group">Delete</td>
</tr>
</tbody>
</table>
<p><button data-bind="click: save" class="btn btn-primary">Save</button></p>
<p>
« Back to List
</p>
and I apply bindings;
<script type="text/javascript">
var salesOrderViewModel = new SalesOrderViewModel(#Html.Raw(data));
ko.applyBindings(salesOrderViewModel);
</script>
In my javascript file I have
var ObjectState = {
Unchanged: 0,
Added: 1,
Modified: 2,
Deleted: 3
};
var salesOrderItemMapping = {
'SalesOrderItems': {
key: function(salesOrderItem) {
return ko.utils.unwrapObservable(salesOrderItem.salesOrderItemId);
},
create: function(options) {
return new SalesOrderViewModel(options.data);
}
}
};
SalesOrderItemViewModel = function(data) {
var self = this;
ko.mapping.fromJS(data, salesOrderItemMapping, self);
self.flagSalesOrderItemAsEdited = function() {
if (self.ObjectState() !== ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
return true;
};
}
SalesOrderViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, salesOrderItemMapping, self);
self.save = function() {
$.ajax({
url: "/Sales/Save",
type: "POST",
data: ko.toJSON(self),
contentType: "application/json",
success: function (data) {
if (data.salesOrderViewModel !== null) {
ko.mapping.fromJS(data.salesOrderViewModel, {}, self);
}
if (data.newLocation !== undefined && data.newLocation !== null) {
window.location.href = data.newLocation;
}
}
});
}
self.flagSalesOrderAsEdited = function () {
if (self.ObjectState() !== ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
return true;
}
The mappings are derived from the server side viewModels:
namespace SolutionName.Web.ViewModels
{
public class SalesOrderViewModel : IObjectWithState
{
public SalesOrderViewModel()
{
this.SalesOrderItems = new ListStack<SalesOrderItemViewModel>();
}
public int SalesOrderId { get; set; }
public string CustomerName { get; set; }
public string PONumber { get; set; }
public string MessageToClient { get; set; }
public ObjectState ObjectState { get; set; }
public List<SalesOrderItemViewModel> SalesOrderItems { get; set; }
}
}
and
namespace SolutionName.Web.ViewModels
{
public class SalesOrderItemViewModel : IObjectWithState
{
public int SalesOrderItemId { get; set; }
public string ProductCode { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public int SalesOrderId { get; set; }
public ObjectState ObjectState { get; set; }
}
}
The error occurs in the table where I have data-binded the flag field:
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
I get 'flagSalesOrderItemAsEdited' is undefined
And it falls over the in the knockout script.
Unable to process binding "foreach: function(){return SalesOrderItems }"
Message: Unable to process binding "event: function(){return {change:flagSalesOrderItemAsEdited} }"
Message: 'flagSalesOrderItemAsEdited' is undefined
ex.message = "Unable to process binding \"" + bindingKey + ": " + bindings[bindingKey] + "\"\nMessage: " + ex.message;
Line 3326 Exception
So how do I fix this?
EDIT
One suggested solution is to use $parent as a prefix in the HTML.
So I tried:
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: $parent.flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: Quantity, event: {change: $parent.flagSalesOrderItemAsEdited}" />
</td>
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: UnitPrice, event: {change: $parent.flagSalesOrderItemAsEdited}" />
</td>
This stopped the exception being thrown. However the method:
self.flagSalesOrderAsEdited = function () {
if (self.ObjectState() !== ObjectState.Added) {
self.ObjectState(ObjectState.Modified);
}
was NOT invoked. It was as though the class it is in was not instantiated.

try the following.use $root while calling a function inside a loop
<td class="form-group">
<input class="form-control input-sm"
data-bind="value: ProductCode, event: {change: $root.flagSalesOrderItemAsEdited}, hasfocus: true" />
</td>
We can also use $parent which is the immeditely outside the current context.
More info on binding context

Related

Razor pages identity profile image

I am learning razor pages and followed several decent examples on Microsoft to create a very rudimentary application. I was wondering if anyone had any resources on how I could add a profile image to razor pages. I am scaffolding identity in Visual studio but nowhere can I find any tutorial on how to integrate an image.
I do have a simple model and controller that will save a file path to the DB but fail to integrate this to the razor templates.
If anyone has any resources for what I am looking to learn I would greatly appreciate it. Apologies if this is very basic, I'm probably not using the correct terminology to search in Google.
identity user in the Db.
I do have a simple model and controller that will save a file path to
the DB but fail to integrate this to the razor templates.
Model:
Let's assume you have Identity base Model as below:
public class IdentityUser
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now you would like to add profile picture functionality there:
So you could extent your class as below:
public class IdentityExtendedModel : IdentityUser
{
public string ImageName { get; set; }
public string ProfilePictureLocation { get; set; }
}
Identity View Model:
We don't want to manipulate our identity Model directly while submitting our form inputso I will use viewModel
public class IdentityViewModel
{
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Username")]
public string Username { get; set; }
[Phone]
[Display(Name = "Phone number")]
public string PhoneNumber { get; set; }
[Display(Name = "Profile Picture")]
public byte[] ProfilePicture { get; set; }
}
View Upload Profile Picture:
#model DotNet6MVCWebApp.Models.IdentityViewModel
<form asp-action="CreateIdentityProfilePic" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-md-6">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="FirstName"></label>
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LastName"></label>
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Username"></label>
<input asp-for="Username" class="form-control" />
<span asp-validation-for="Username" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PhoneNumber"></label>
<input asp-for="PhoneNumber" class="form-control" />
<span asp-validation-for="PhoneNumber" class="text-danger"></span>
</div>
<button id="update-profile-button" type="submit" class="btn btn-primary">Save</button>
</div>
<div class="col-md-6">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProfilePicture" style="width: 100%;"></label>
#if (Model.ProfilePicture != null)
{
<img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src="data:image/*;base64,#(Convert.ToBase64String(Model.ProfilePicture))">
}
else
{
<img id="profilePicture" style="width:350px;height:350px; object-fit:cover" src="">
}
<input type="file"
accept=".png,.jpg,.jpeg,.gif,.tif"
asp-for="ProfilePicture"
class="form-control"
style="border:0px!important;padding: 0px;padding-top: 10px;padding-bottom: 30px;"
onchange="document.getElementById('profilePicture').src = window.URL.createObjectURL(this.files[0])" />
<span asp-validation-for="ProfilePicture" class="text-danger"></span>
</div>
</div>
</div>
</form>
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> CreateIdentityProfilePic(InputModel model, IFormFile profilePicture)
{
if (profilePicture == null || profilePicture.Length == 0)
{
return Content("File not selected");
}
var path = Path.Combine(_environment.WebRootPath, "ImageName/Cover", profilePicture.FileName);
using (FileStream stream = new FileStream(path, FileMode.Create))
{
await profilePicture.CopyToAsync(stream);
stream.Close();
}
if (model == null)
{
return Content("Invalid Submission!");
}
var identityModel = new IdentityModel
{
FirstName = model.FirstName,
LastName = model.LastName,
Username = model.Username,
PhoneNumber = model.PhoneNumber,
ImageName = profilePicture.FileName,
ProfilePictureLocation = path,
};
_context.Add(identityModel);
await _context.SaveChangesAsync();
return RedirectToAction("IdentityProfilePicList");
}
Note: When you would submit Upload profile Picture view this controller should receive the request.
Controller Profile Picture List:
public ActionResult IdentityProfilePicList()
{
var memberList = _context.identityModels.ToList();
return View(memberList);
}
View Profile Picure List:
#model IEnumerable<DotNet6MVCWebApp.Models.IdentityExtendedModel>
#{
ViewData["Title"] = "Index";
}
<div class="form-row">
<table>
<tr>
<td>
<a asp-action="Index" class="btn btn-success">Create New</a>
</td>
<form method="get" action="#">
<td style="padding-right:760px">
</td>
<td>
<input class="form-control" type="text" placeholder="Search for..." name="SearchString" value="#ViewData["CurrentFilter"]" aria-label="Search" aria-describedby="btnNavbarSearch" />
</td>
<td>
<input type="submit" value="Search" class="btn btn-primary" />
</td>
</form>
</tr>
</table>
</div>
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
#Html.DisplayNameFor(model => model.LastName)
</th>
<th>
#Html.DisplayNameFor(model => model.Username)
</th>
<th>
#Html.DisplayNameFor(model => model.ProfilePictureLocation)
</th>
<th>
Member Details
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
#Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Username)
</td>
<td>
<img src="~/ImageName/Cover/#item.ImageName"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
</td>
<td>
<a asp-action="EditMember" class="btn btn-primary" asp-route-memberId="#item.Id">Details</a> | <a asp-action="EditMember" class="btn btn-warning" asp-route-memberId="#item.Id">Edit</a>
</td>
</tr>
}
</tbody>
</table>
Output:
Database:
Note: If you need any further assistance on this please check this GitHub repository.
Thanks for all the help it helped me massively to resolve the issue. At the end of registering the image in the scaffolded razor pages Identity I had to use the code behind file register.cshtl.cs and add it to the Input module there and be consumed by the register.cshtml which now works perfectly.
I hope this helps someone else.
First I extended the ApplicationUser and added the migration.
public class ApplicationUser : IdentityUser
{
[Required]
public string Name { get; set; }
public string? StreetAddress { get; set; }
public string? City { get; set; }
public string? PostalCode { get; set; }
[ValidateNever]
public string? ImageUrl { get; set; }
public int? CompanyId { get; set; }
[ForeignKey("CompanyId")]
[ValidateNever]
public Company Company { get; set; }
}
Then added to the Input model in the Register.cshtml.cs
public class InputModel
{
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
//Added custom fields
[Required]
public string Name { get; set; }
public string? StreetAddress { get; set; }
public string? City { get; set; }
public string? PostalCode { get; set; }
public string? PhoneNumber { get; set; }
[ValidateNever]
public string? ImageUrl { get; set; }
public string? Role { get; set; }
public int? CompanyId { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> RoleList { get; set; }
[ValidateNever]
public IEnumerable<SelectListItem> CompanyList { get; set; }
}`
Then in the on Post method add the file path to the database.
public async Task<IActionResult> OnPostAsync(IFormFile file, string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = CreateUser();
string wwwRootPath = _hostEnvironment.WebRootPath;
if (file != null)
{
string fileName = Guid.NewGuid().ToString();
var uploads = Path.Combine(wwwRootPath, #"images\companies");
var extension = Path.GetExtension(file.FileName);
if (Input.ImageUrl != null)
{
var oldImagePath = Path.Combine(wwwRootPath, Input.ImageUrl.TrimStart('\\'));
}
using (var fileStreams = new FileStream(Path.Combine(uploads, fileName + extension), FileMode.Create))
{
file.CopyTo(fileStreams);
}
Input.ImageUrl = #"\images\companies\" + fileName + extension;
}
else
{
Input.ImageUrl = #"\images\companies\QPQ-logo.jpg";
}
await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
user.Name = Input.Name;
user.StreetAddress = Input.StreetAddress;
user.City = Input.City;
user.PostalCode = Input.PostalCode;
user.PhoneNumber = Input.PhoneNumber;
user.ImageUrl = Input.ImageUrl;
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
if (Input.Role == null)
{
await _userManager.AddToRoleAsync(user, SD.Role_User_Indi);
}
else
{
await _userManager.AddToRoleAsync(user, Input.Role);
}
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}`
One of the first mistakes I made was not declaring the front-end form as a multipart form in the Register.cshtml.
<h1>#ViewData["Title"]</h1><div class="row pt-4">
<div class="col-md-8">
<form id="registerForm" class="row" asp-route-returnUrl="#Model.ReturnUrl" method="post" enctype="multipart/form-data">
<h2>Create a new account.</h2>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-floating py-2 col-12">
<input asp-for="Input.Email" class="form-control" aria-required="true" />
<label asp-for="Input.Email"></label>
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.Name" class="form-control" aria-required="true" />
<label asp-for="Input.Name"></label>
<span asp-validation-for="Input.Name" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.StreetAddress" class="form-control" />
<label asp-for="Input.StreetAddress"></label>
<span asp-validation-for="Input.StreetAddress" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.City" class="form-control" />
<label asp-for="Input.City"></label>
<span asp-validation-for="Input.City" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.PostalCode" class="form-control" />
<label asp-for="Input.PostalCode"></label>
<span asp-validation-for="Input.PostalCode" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.Password" class="form-control" aria-required="true" />
<label asp-for="Input.Password"></label>
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<input asp-for="Input.ConfirmPassword" class="form-control" aria-required="true" />
<label asp-for="Input.ConfirmPassword"></label>
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
<div class="form-floating py-2 col-6">
<select asp-for="Input.Role" asp-items="#Model.Input.RoleList" class=form-select>
<option disabled selected>-Select Role-</option>
</select>
</div>
<div class="form-floating py-2 col-6">
<select asp-for="Input.CompanyId" asp-items="#Model.Input.CompanyList" class=form-select>
<option disabled selected>-Select Company-</option>
</select>
</div>
<div class="form-floating py-2 col-12">
<label asp-for="Input.ImageUrl"></label>
<input asp-for="Input.ImageUrl" type="file" id="uploadBox" name="file" class="form-control" />
</div>
<button id="registerSubmit" type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
</form>
</div>
<div class="col-md-4">
<section>
<h3>Use another service to register.</h3>
<hr />
#{
if ((Model.ExternalLogins?.Count ?? 0) == 0)
{
<div>
<p>
There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
about setting up this ASP.NET application to support logging in via external services</a>.
</p>
</div>
}
else
{
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="#Model.ReturnUrl" method="post" class="form-horizontal">
<div>
<p>
#foreach (var provider in Model.ExternalLogins)
{
<button type="submit" class="btn btn-primary" name="provider" value="#provider.Name" title="Log in using your #provider.DisplayName account">#provider.DisplayName</button>
}
</p>
</div>
</form>
}
}
</section>
</div>

ASP .NET MVC: Get the url and view the image as it changes

I have a function that allows me to edit an image but I want as soon as I click the Edit button I will get the URL of the current image
image:
Do not see "no file chosen"
I want the URL of the image to appear on the right
Step Two Once I swap a picture I also want the picture to change to the picture I swap
My Service:
public class FileService : IFileService
{
private readonly IWebHostEnvironment _environment;
public FileService(IWebHostEnvironment environment)
{
_environment = environment;
}
public async Task<string> File([FromForm] CreateAnimalViewModel model)
{
string wwwPath = _environment.WebRootPath;
var path = Path.Combine(wwwPath, "Images", model.Photo!.FileName);
if (model.Photo.Length > 0)
{
using var stream = new FileStream(path, FileMode.Create);
await model.Photo.CopyToAsync(stream);
}
return model.Animal!.PhotoUrl = model.Photo.FileName;
}
public interface IFileService
{
Task<string> File([FromForm] CreateAnimalViewModel model);
}
My ViewModel:
public class CreateAnimalViewModel
{
public Animal? Animal { get; set; }
public IFormFile Photo { get; set; }
}
My Controller:
public async Task<IActionResult> EditAnimal(int id)
{
var animal = await _repo.FindAnimalById(id);
ViewBag.Category = new SelectList(_repository.GetCategoriesTable(), "CategoryId", "Name");
return View(new CreateAnimalViewModel() { Animal = animal});
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditAnimal([FromForm] CreateAnimalViewModel model)
{
ModelState.Clear();
TryValidateModel(model);
await _file.File(model);
if (!ModelState.IsValid)
{
await _repo.EditAnimal(model.Animal!);
return RedirectToAction(nameof(Manager));
}
return View();
}
My Repository:
public async Task<int> AddAnimal(Animal animal)
{
_context.Add(animal!);
return await _context.SaveChangesAsync();
}
public async Task<int> EditAnimal(Animal animal)
{
_context.Update(animal);
return await _context.SaveChangesAsync();
}
public DbSet<Category> GetCategories()
{
var category = _context.Categories;
return category;
}
My View:
#model PetShop.Client.Models.CreateAnimalViewModel
<form asp-action="EditAnimal" method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly"></div><input type="hidden" asp-for="Animal!.AnimalId" id="Space"/>
<dl class="row" >
<dt class = "col-sm-2"><label asp-for="Animal!.Name" id="Space"></label></dt>
<dd class = "col-sm-10"><input asp-for="Animal!.Name"/><span asp-validation-for="Animal!.Name" id="Validation"></span></dd>
<dt class = "col-sm-2"><label asp-for="Animal!.BirthDate" id="Space"></label></dt>
<dd class = "col-sm-10"><input asp-for="Animal!.BirthDate"/><span asp-validation-for="Animal!.BirthDate" id="Validation"></span></dd>
<dt class = "col-sm-2"><label asp-for="Animal!.Description" id="Space"></label></dt>
<dd class = "col-sm-10"><input asp-for="Animal!.Description"/><span asp-validation-for="Animal!.Description"></span>
</dd> <dt class = "col-sm-2"><label asp-for="Animal!.CategoryId" id="Space"></label></dt>
<dd class = "col-sm-10"><select asp-for="Animal!.CategoryId"asp-items="ViewBag.Category"></select>
<span asp-validation-for="Animal!.CategoryId"></span></dd>
<dt class = "col-sm-2"><label asp-for="Photo"></label></dt>
<dd class = "col-sm-10"><input type="file" asp-for="Photo" accept="image/*"/>
<img src="~/images/#Model.Animal!.PhotoUrl"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
<span asp-validation-for="Photo" id="ImageValidation"></span></dd>
<br /> <br /><br/><input type="submit" value="Save" id="ButtonDesign"/>
</dl>
</form>
<a asp-action="Commands"><input type="submit" value="Back to Admin Page" id="BackPageButton"/></a>
image:
You cannot implement that:
I have gone through your code what you are trying to implement is to set initial value on input type="file" which is not possible due to
security reasons which you can check here. In addition you can also have a look here thousands of questions been askedbefore. Note that it's against RFC standard.
What you can do is:
While loading the Edit page hide the <input type="file"
Set a checkbox beside photo and it URL like below:
If the checkbox clicked then show the "file upload" option.
Note: Elementary JavaScript required for that implementation.
HTML:
<div>
<form asp-action="EditAnimal" method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly"></div><input type="hidden" asp-for="Animal!.AnimalId" id="Space" />
<div>
<h4><strong>Animal Details</strong> </h4>
<table class="table table-sm table-bordered table-striped">
<tr>
<th> <label asp-for="Animal!.Name"></label></th>
<td> <input asp-for="Animal!.Name" class="form-control" placeholder="Enter animal name" /><span asp-validation-for="Animal!.Name"></span></td>
</tr>
<tr>
<th> <label asp-for="Animal!.Description"></label></th>
<td> <input asp-for="Animal!.Description" class="form-control" placeholder="Enter animal description" /><span asp-validation-for="Animal!.Description"></span></td>
</tr>
<tr>
<th> <label asp-for="Animal!.Category"></label></th>
<td> <input asp-for="Animal!.Category" class="form-control" placeholder="Enter animal category" /><span asp-validation-for="Animal!.Category"></span></td>
</tr>
<tr>
<th> <label asp-for="Photo"></label></th>
<td>
<img src="~/images/#Model.Animal!.PhotoUrl"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
<span>#Model.ImageURL</span>
<input type="checkbox" id="CheckBoxId" class="form-check-input" style="margin-top:16px;border:1px solid" /> <span><strong>Upload New File</strong></span>
<input type="file" name="photo" id="chooseFile" accept="image/*" />
</td>
</tr>
<tr>
<th> <label>Updated On Local Time</label></th>
<td> <input asp-for="Animal!.LocalTime" class="form-control" disabled /><span asp-validation-for="Animal!.Category"></span></td>
</tr>
<tr>
<th> <button type="submit" class="btn btn-primary" style="width:107px">Update</button></th>
<td> </td>
</tr>
<tr>
<th>#Html.ActionLink("Back To List", "Index", new { /* id=item.PrimaryKey */ }, new { #class = "btn btn-success" })</th>
<td> </td>
</tr>
</table>
</div>
</form>
</div>
JavaScript:
#section scripts {
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<script>
$(document).ready(function () {
//On Edit Page Load Hiding the upload option
$("#chooseFile").hide();
//When upload check box clicked showing the upload option
$('#CheckBoxId').mousedown(function() {
if (!$(this).is(':checked')) {
this.checked = true;
$("#chooseFile").show();
}
else{
$("#chooseFile").hide();
}
});
});
</script>
}
Output:

POSTing KnockoutJS model to MVC controller, List<T> in List<T> is empty

I have little experience with KnockoutJS so please bear with me.
I have a basic example that I want to get working so I can expand it to my project.
For this example you click the button and the AddSku method is called to return QuoteLine data with List.
However, as the diagram shows, BomDetails is empty:
Models:
public class QuoteViewModel
{
public int Id { get; set; }
public string QuoteName { get; set; }
public IList<QuoteLine> QuoteLines { get; set; }
}
public class QuoteLine
{
public string Description { get; set; }
public string Sku { get; set; }
public IList<BomDetail> BomDetails = new List<BomDetail>();
}
public class BomDetail
{
public string Name { get; set; }
}
Controller methods:
[HttpGet]
public ActionResult CreateQuote()
{
QuoteViewModel quote = new QuoteViewModel();
quote.Id = 10;
quote.QuoteName = "Test Create Quote";
quote.QuoteLines = new List<QuoteLine>();
return View(quote);
}
[HttpPost]
public ActionResult CreateQuote(QuoteViewModel viewModel)
{
if (ModelState.IsValid)
{
}
return RedirectToAction("CreateQuote");
}
[HttpGet]
public JsonResult AddSku()
{
QuoteLine line = new QuoteLine();
line.BomDetails = new List<BomDetail>();
line.Sku = "TestSku";
line.Description = "TestDescription";
line.BomDetails.Add(new BomDetail
{
Name = "BOM Detail 1"
});
line.BomDetails.Add(new BomDetail
{
Name = "BOM Detail 2",
});
return Json(line, JsonRequestBehavior.AllowGet);
}
The view:
#model EngineeringAssistantMVC.ViewModels.QuoteViewModel
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<div class="container-fluid">
<h2>Create Quote</h2>
#using (Html.BeginForm("CreateQuote", "Test", FormMethod.Post, new { #id = "createQuoteForm", #class = "form-horizontal", role = Model, enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.QuoteName)
<h3>Quote Lines</h3>
<table class="table master-detail-table" id="receiving-table">
<thead>
<tr>
<th>SKU</th>
<th>Description</th>
</tr>
</thead>
<tbody data-bind="foreach: QuoteLines">
<tr>
<td>
<input class='form-control' data-bind='value: $data.Sku, attr: { name: "QuoteLines[" + $index() + "].Sku" } ' type='text' readonly='readonly' />
</td>
<td>
<input class='form-control' data-bind='value: $data.Description, attr: { name: "QuoteLines[" + $index() + "].Description" } ' type='text' readonly='readonly' />
</td>
</tr>
<tr class="detail-row">
<td colspan="7">
<table class="table">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody data-bind="foreach: BomDetails">
<tr>
<td>
<input class='form-control' data-bind='value: $data.Name, attr: { name: "BomDetails[" + $index() + "].Name" } ' type='text' readonly='readonly' />
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h3>Add Sku from Db</h3>
<div class="row">
<div class="col-sm-2">
<input type="button" value="Add Sku" id="btnAddSku" class="btn btn-satcom-primary btn-wider" />
</div>
</div>
<h3>Submit</h3>
<div class="row">
<div class="col-sm-1">
<input type="submit" value="Submit" class="btn btn-satcom-primary btn-wider" id="btnSubmit" />
</div>
</div>
}
</div>
<script type="text/javascript">
$(function () {
quoteViewModel = new QuoteViewModel();
ko.applyBindings(quoteViewModel);
$('#btnAddSku').off().on('click', function () {
AddFromDb();
});
});
function QuoteViewModel() {
var self = this;
self.Id = ko.observable('#Model.Id');
self.QuoteName = ko.observable('#Model.QuoteName');
self.QuoteLines = ko.observableArray([]);
self.AddQuoteLine = function (sku, description, bomDetails) {
self.QuoteLines.push(new QuoteLineViewModel(sku, description, bomDetails));
}
}
function QuoteLineViewModel(sku, description, bomDetails) {
var self = this;
self.Sku = sku;
self.Description = description;
self.BomDetails = ko.observableArray([]);
$.each(bomDetails, function (index, item) {
self.BomDetails.push(new BomDetailViewModel(item.Name));
});
}
function BomDetailViewModel(name) {
var self = this;
self.Name = name;
}
function AddFromDb() {
$.ajax({
type: "GET",
url: '#Url.Action("AddSku", "Test")',
success: function (line) {
window.quoteViewModel.AddQuoteLine(line.Sku, line.Description, line.BomDetails);
}
});
}
I have tried so many things to get it populated but can't figure out where the problem lies, but I hope it is just something silly that I'm doing or not doing.
I have also tried using ko.mapping but I can't get that working either.
I managed to get this working so hopefully it will help somebody else in the future.
I removed the #Using (Html.BeginForm)
I changed the submit button to a normal button and added data-bind to a fucntion
<input type="button" value="Submit" class="btn btn-satcom-primary btn-wider" id="btnSubmit" data-bind="click:SaveToDatabase" />
The SaveToDatabase function:
self.SaveToDatabase = function () {
var dataToSend = ko.mapping.toJSON(self);
$.ajax({
type: "POST",
url: '#Url.Action("CreateQuote", "Test")',
contentType: 'application/json',
data: dataToSend,
success: function (data) {
},
error: function (err) {
console.log(err.responseText);
}
});
}
This correctly sends all the data to the controller.

Input box as “date” on web when model is “int” (again)

I decided to create a new post here instead of the other post which was a bit confusing. (Input box as "date" on web when model is "int")
In this post I have added the code I use.
The problem is that in the SQL server the field FrDr is of type int and I need the user on the webpage to enter in a input field of type "date"
If I add [DataType(DataType.Date)] to the model i get the input date type autmoatically. but not if I add it to the Page Model
How can I change my code so that the user get an input field of type date that save the date value as an int to SQL.
MODEL AGR.CS
namespace DateTest.VismaModels {
public partial class Agr {
[Key]
[Display(Name = "Actor")]
public int AgrActNo { get; set; }
public int AgrNo { get; set; }
[DataType(DataType.Date)]
[Display(Name = "From Date")]
public int FrDt { get; set; }
}
}
PAGE MODEL
namespace DateTest.Pages {
public class IndexModel : PageModel {
private readonly DateTest.VismaModels.F0001Context _context;
public IndexModel(DateTest.VismaModels.F0001Context context) {
_context = context;
}
public bool ShowMessage => !string.IsNullOrEmpty(Message);
[TempData]
public string Message { get; set; }
[BindProperty]
public Agr AgrRow { get; set; }
public async Task<IActionResult> OnGetAsync(int? id) {
if (id == null) {
Console.WriteLine("New");
AgrRow = new Agr();
AgrRow.AgrNo = _context.Agr.Select(q => q.AgrNo).DefaultIfEmpty(0).Max() + 1;
//AgrRow.FrDt = int.Parse(DateTime.Now.ToString("yyyyMMdd"));
}
if (!ModelState.IsValid) {
//var errors = ModelState.Select(x => x.Value.Errors).Where(y => y.Count > 0).ToList();
var errors = string.Join(" | ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage));
Message += "Model not valid : " + errors;
return Redirect("~/Activities/index");
}
return Page();
}
public async Task<IActionResult> OnPostSaveNewRowAsync() {
if (!ModelState.IsValid) {
//var errors = ModelState.Select(x => x.Value.Errors).Where(y => y.Count > 0).ToList();
var errors = string.Join(" | ", ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage));
Message += "Model not valid : " + errors;
return Redirect("~/index");
}
_context.Add(AgrRow);
try {
await _context.SaveChangesAsync();
} catch (DbUpdateConcurrencyException) {
throw;
}
Message += "Saved";
return RedirectToPage("/index");
}
}
}
RAZOR PAGE
#page
#model DateTest.Pages.IndexModel
#{
ViewData["Title"] = "Activities";
}
<h2>Activities</h2>
#if (Model.ShowMessage) {<div class="alert alert-info alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"></button>
#Model.Message
</div>
}
<p>
<a asp-page="Create">Create New</a>
</p>
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="AgrRow.AgrNo" />
<table class="table">
<tbody>
<tr>
<td><input asp-for="#Model.AgrRow.AgrActNo" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.FrDt" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.ToDt" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.FrTm" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.ToTm" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.CustNo" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.ProdNo" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Descr" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.NoInvoAb" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Price" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Am" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Fin" class="form-control" /></td>
<td><input asp-for="#Model.AgrRow.Invo" class="form-control" /></td>
<td>
<div class="form-group">
<button type="submit" value="Save New" asp-page-handler="saveNewRow" asp-route-id="#Model.AgrRow.AgrNo" class="btn btn-default">
<i class="fa fa-save"></i>Create
</button>
</div>
</td>
</tr>
</tbody>
</table>
</form>
UPDATE
If I add the following to the Page Model
public DateTime FrDt { get; set; }
And print out the result in the post method like this
Console.WriteLine("FRDT = " + FrDt);
Console.WriteLine("AGRROW FRDT = " + AgrRow.FrDt);
I get this
FRDT = 0001-01-01 00:00:00
AGRROW FRDT = 0
I figured it out.
In my Agr Model I add a temporary field FrDt2 which is of DateTime and use the [NotMapped] annotation so it does not send it to sql
[NotMapped]
public DateTime FrDt2 { get; set; }
And in my Page model I convert FrDt2 to int before adding it to FrDt.
AgrRow.FrDt = int.Parse(AgrRow.FrDt2.ToString("yyyyMMdd"));
And, to show an existing item from SQL in the input date box I convert it back to int like this
AgrRow.FrDt2 = DateTime.ParseExact(AgrRow.FrDt.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);

fiil a list with values of a table

I'm new in learning asp.net MVC. I am writing because I am stubborn to a problem. Indeed, I have an application that should allow me to create an XML file that will be added to a database. At this point, I created my Model, and my view that allows me to create my XML tags.
I saw on this site that could add lines in my table via Javascript. What I have done just as you can see in the code.
I can not recover what is the value of each line that I can insert. Passing my view a list I created myself. I can recover both inputs I inserted in my controller.
My question is, there's another way to create a dynamic lines via javascript, then all the entries that the user has entered the recover and fill in my list? Then I know myself how I can play with my list. But I just want to recover all the different lines that my user has inserted. I am new in ASP.NET MVC. Any help , please
This is my code.
Model
public class XMLFile
{
public string TypeDoc { get; set; }
public string Type { get; set; }
public string Contenu { get; set; }
public string DocName { get; set; }
}
This is my controller :
public class XMLFileController : Controller
{
List<XMLFile> file = new List<XMLFile>();
[HttpGet]
public ActionResult Save()
{
file.AddRange( new XMLFile[] {
new XMLFile (){Type = "Titre", Contenu = "Chef de Service"},
new XMLFile (){Type = "Item", Contenu="Docteur Joel"}
});
return View(file);
}
[HttpPost]
public ActionResult Save(List<XMLFile> formCollection)
{
try
{
if (formCollection == null)
{
return Content("la liste est nulle");
}
else
{
return RedirectToAction("Create", "Layout");
}
}
catch
{
return View();
}
}
}
My view with a script for adding a new Row :
#using (Html.BeginForm("Save", "XMLFile", FormMethod.Post,new { #class = "form-horizontal", #role = "form", #id = "FormCreateXML" }))
{
<table class="table table-bordered" id="XMLFileTable">
<thead>
<tr>
<th>Type</th>
<th>Contenu</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i<Model.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(model=>model[i].Type, new {#class="form-control help-inline", #placeholder="type" })</td>
<td> #Html.TextBoxFor(model=>model[i].Contenu, new {#class="form-control help-inline", #placeholder="contenu" })</td>
<td> <input type="button" class="BtnPlus" value="+" /> </td>
<td> <input type="button" class="BtnMinus" value="-" /> </td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td> <button type="submit" class="btn btn-success" >Save</button> </td>
</tr>
</tfoot>
</table>
}
</body>
<script type="text/javascript">
$(document).ready(function () {
function addRow() {
var html = '<tr>' +
'<td><input type="text" class="form-control" placeholder="type"></td>' +
'<td> <input type="text" class="form-control" placeholder="contenu"></td>' +
'<td> <input type="button" class="BtnPlus" value="+" /> </td>' +
'<td> <input type="button" class="BtnMinus" value="-" /></td>' +
'</tr>'
$(html).appendTo($("#XMLFileTable"))
};
function deleteRow() {
var par = $(this).parent().parent();
par.remove();
};
$("#XMLFileTable").on("click", ".BtnPlus", addRow);
$("#XMLFileTable").on("click", ".BtnMinus", deleteRow);
});
</script>

Resources