MVC Questionnaire App - asp.net

I am currently building a survey/questionnaire app where the admin user can create questionnaires with multiple sections and questions under those sections.
Response:
public ActionResult AnswerQuestions(int ID, Response model)
{
model.Answers = model.Answers.Where(a => !String.IsNullOrEmpty(a.TextAnswer) || a.RatingAnswer.HasValue).ToList();
model.UserID = User.Identity.GetUserId();
model.QuestionnaireID = ID;
db.Responses.Add(model);
db.SaveChanges();
return RedirectToAction("Details", "Responses", new { id = ID });
}
At the moment i am using the above code to gather the answers from each question and store it but it is creating a new response each time. Can someone please help me so that I could use an existing "response" and still push the answers to the database.
View Code :
<form action="#Url.Action("AnswerQuestions", "Responses", new { id = Model.QuestionnaireID })" method="post">
<div class="well well-small">
<h4>Questions - #ViewBag.ResponseID</h4>
</div>
<ul class="unstyled bordered skip-first">
#foreach (var question in Model.SectionVM.Questions)
{
<li class="row">
<div class="span9">
<input type="hidden" name="Answers.Index" value="#question.QuestionID" />
<input type="hidden" name="Answers[#question.QuestionID].QuestionID" value="#question.QuestionID" />
#Html.Raw(question.Question1)
</div>
<div class="span3">
#if (question.QuestionType == "Text")
{
<input class="span3" type="text" name="Answers[#question.QuestionID].TextAnswer" />
}
#if (question.QuestionType == "Rating")
{
<input type="radio" name="Answers[#question.QuestionID].RatingAnswer" value="1"/><p>Don’t Know</p>
<input type="radio" name="Answers[#question.QuestionID].RatingAnswer" value="2" /><p>Not us</p>
<input type="radio" name="Answers[#question.QuestionID].RatingAnswer" value="3" /><p>Somewhat true of us</p>
<input type="radio" name="Answers[#question.QuestionID].RatingAnswer" value="4" /><p>Mostly true of us</p>
<input type="radio" name="Answers[#question.QuestionID].RatingAnswer" value="5" /><p>That’s us</p>
}
</div>
</li>
}
</ul>

Related

Model does not get any properties fillen after post

Problem: Hi fellow programmers, I am new to ASP.NET and tried to figure out why this happens for hours but could not find why. The model in the controller does not get any property after the post. They are all nulls, why is that so?
The view
#model AddProductViewModel
#{
ViewBag.Title = "Add a new Product";
}
<h2 class="text-center">#ViewBag.Title</h2>
<div class="row">
<div class="col-sm-12 offset-lg-2 col-lg-8 offset-xl-3 col-xl-6">
<form asp-action="Add" method="post">
<div class="mb-3">
<label asp-for="#Model.Model" class="form-label">Model Name</label>
<input asp-for="#Model.Model" class="form-control" aria-required="true" />
<span asp-validation-for="Model" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.Description" class="form-label">Description</label>
<textarea asp-for="#Model.Description" class="form-control" aria-required="true" rows="5"></textarea>
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.Colour" class="form-label">Colour</label>
<input asp-for="#Model.Colour" class="form-control" value="" />
<span asp-validation-for="Colour" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.Size" class="form-label">Size</label>
<input asp-for="#Model.Size" class="form-control" value="" />
<span asp-validation-for="Size" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.Price" class="form-label">Price</label>
<input asp-for="#Model.Price" class="form-control" value="" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.ImageData" class="form-label">Image URL</label>
<input asp-for="#Model.ImageData" class="form-control" aria-required="true" />
<span asp-validation-for="ImageData" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.CategoryId" class="form-label">Category</label>
<select asp-for="#Model.CategoryId" class="form-control">
#foreach (var category in Model.Categories)
{
<option value="#category.Id">#category.Name</option>
}
</select>
<span asp-validation-for="CategoryId" class="text-danger"></span>
</div>
<div class="mb-3">
<label asp-for="#Model.BrandId" class="form-label">Brand</label>
<select asp-for="#Model.BrandId" class="form-control">
#foreach (var brand in Model.Brands)
{
<option value="#brand.Id">#brand.Name</option>
}
</select>
<span asp-validation-for="BrandId" class="text-danger"></span>
</div>
<div class="mb-3">
<input class="btn btn-primary" type="submit" value="Add" />
</div>
</form>
</div>
</div>
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
The controller
...
[HttpPost]
public async Task<IActionResult> Add(AddProductViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
try
{
await productService.AddProductAsync(model);
return RedirectToAction("Index", "Home");
}
catch (Exception)
{
ModelState.AddModelError("", "Something went wrong");
return View(model);
}
}
...
That's the first time something like that happens to me. I have done another project which was very similar if not the same. Any help will be appreciated!
Your model contains property named Model which will conflict with your parameter AddProductViewModel model.
So just change your parameter name like below:
[HttpPost]
public async Task<IActionResult> Add(AddProductViewModel addProductViewModel)
{
if (!ModelState.IsValid)
{
return View(addProductViewModel);
}
try
{
await productService.AddProductAsync(addProductViewModel);
return RedirectToAction("Index", "Home");
}
catch (Exception)
{
ModelState.AddModelError("", "Something went wrong");
return View(addProductViewModel);
}
}

Using input tag helper "name" and getting input value empty. ASP.NET MVC (.NET 5)

I have simple form for creating items
<form asp-action="CreateItem" enctype="multipart/form-data">
<div class="form-group">
<label asp-for="#Model.ItemPhoto" class="control-label"></label>
<input type="file" asp-for="#Model.ItemPhoto" name="Photo" class="form-control-file" />
<span asp-validation-for="#Model.ItemPhoto" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.Name" class="control-label"></label>
<input asp-for="#Model.Name" class="form-control" />
<span asp-validation-for="#Model.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.ItemType" class="control-label"></label>
<select asp-for="#Model.ItemType" class="form-control">
#foreach (var itemType in Enum.GetValues(typeof(RandApp.Enums.ItemType)))
{
<option value="#itemType.ToString()">#itemType</option>
}
</select>
<span asp-validation-for="#Model.ItemType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.MaterialType" class="control-label"></label>
<select asp-for="#Model.MaterialType" class="form-control">
#foreach (var materialType in Enum.GetValues(typeof(RandApp.Enums.MaterialType)))
{
<option value="#materialType.ToString()">#materialType</option>
}
</select>
<span asp-validation-for="#Model.MaterialType" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.Color" class="control-label"></label>
<select asp-for="#Model.Color" class="form-control">
#foreach (var color in Enum.GetValues(typeof(RandApp.Enums.ItemColor)))
{
<option value="#color.ToString()">#color</option>
}
</select>
<span asp-validation-for="#Model.Color" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.Size" class="control-label"></label>
<select asp-for="#Model.Size" class="form-control">
#foreach (var size in Enum.GetValues(typeof(RandApp.Enums.ItemSize)))
{
<option value="#size.ToString()">#size</option>
}
</select>
<span asp-validation-for="#Model.Size" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.DesignedFor" class="control-label"></label>
<select asp-for="#Model.DesignedFor" class="form-control">
#foreach (var desigendFor in Enum.GetValues(typeof(RandApp.Enums.DesignedFor)))
{
<option value="#desigendFor.ToString()">#desigendFor</option>
}
</select>
<span asp-validation-for="#Model.DesignedFor" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.Price" class="control-label"></label>
<input asp-for="#Model.Price" class="form-control" />
<span asp-validation-for="#Model.Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.Description" class="control-label"></label>
<textarea asp-for="#Model.Description" class="form-control"></textarea>
<span asp-validation-for="#Model.Description" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
there is its controller
public async Task<IActionResult> CreateItem(Item item, IFormFile Photo)
{
if (ModelState.IsValid)
{
var path = Path.Combine(_webHostEnvironment.WebRootPath, "assets", Photo.FileName);
var stream = new FileStream(path, FileMode.Create);
await Photo.CopyToAsync(stream);
item.ItemPhoto = Photo.FileName;
await _itemRepo.CreateAsync(item);
ViewBag.Item = item;
return RedirectToAction("ReadItems");
}
return View();
}
my goal is to get the path of chosen photo and save it in folder called "assets"(located in "wwwroot" folder).
The problem is that when i fill the fields and submitting the values, i get item.ItemPhoto value null and i can't enter in if statement. (see the photo down below).
[1]: https://i.stack.imgur.com/H2aLt.png
one solution i have found is to remove "enctype="multipart/form-data" from form and "name="Photo" tag helper from input
<form asp-action="CreateItem" enctype="multipart/form-data">
<div class="form-group">
<label asp-for="#Model.ItemPhoto" class="control-label"></label>
<input type="file" asp-for="#Model.ItemPhoto" name="Photo" class="form-control-file" />
<span asp-validation-for="#Model.ItemPhoto" class="text-danger"></span>
</div>
but in this case i can't get the path properly.
what can i do to solve this problem, why am i getting empty value from input?
File and string type cannot be passed together with the same name automatically and cannot achieve this requirement by using just one input. You can set a hidden input for ItemPhoto and use js to set the value when the file changed:
#model Item
<form asp-action="CreateItem" enctype="multipart/form-data">
<div class="form-group">
<label asp-for="#Model.ItemPhoto" class="control-label"></label>
//change here...
<input type="file" name="ItemPhoto" class="form-control-file" onchange="SetValue(this)" />
//add hidden input......
<input asp-for="#Model.ItemPhoto" hidden/>
<span asp-validation-for="#Model.ItemPhoto" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="#Model.Name" class="control-label"></label>
<input asp-for="#Model.Name" class="form-control" />
<span asp-validation-for="#Model.Name" class="text-danger"></span>
</div>
<input type="submit" value="Create"/>
</form>
#section Scripts
{
<script>
function SetValue(input) {
var fileName = input.files[0].name;
//asp-for will generate the id and name
//so you can get the selector by using $("#ItemPhoto")
$("#ItemPhoto").val(fileName);
}
</script>
}
Backend (The name attribute should always match with the parameter/property name):
public async Task<IActionResult> CreateItem(Item item, IFormFile ItemPhoto)
{
//....
return View();
}

is there a way to send SelectList in view data with int & string (in Edit function)?

I'm using Asp.net core coding a web application, I've tried to edit an item before by the default editing function in the controller with no problem.
recently I've changed an odd field in the list in index view to a select list instead of a regular view.
after that, when I tried to edit a raw it opened the editing page then when I clicked on the save button, an error message ("InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.String'.") appeared.
Save Button at Edit View:
'''
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
GET Edit Function:
'''
public async Task<IActionResult> Edit(int? id)
{
List<Status> StatusList = _context.Status.ToList();
ViewData["StatusList"] = new SelectList(StatusList, "STID", "Name");
if (id == null)
{
return NotFound();
}
var bugsSummary = await _context.BugsSummary.SingleOrDefaultAsync(m => m.Id == id);
if (bugsSummary == null)
{
return NotFound();
}
ViewData["UserId"] = new SelectList(_context.Users, "Id", "Id", bugsSummary.UserId);
ViewData["ProjectsPId"] = new SelectList(_context.Projects, "PId", "PName", bugsSummary.ProjectsPId);
return View(bugsSummary);
}
'''
I've set a breakpoint in the post edit function it doesn't enter the post edit.
I've tried to send the status ViewData in StatusList as (int, string), but the SelectList class doesn't allow.
how can I solve the InvalidCastException error?
additional attachments:
HTTP POST EDIT FUNC.
'''
public async Task<IActionResult> Edit(int id,
[Bind("Id,ProjectsPId,Bug,BugSummary,TesterName,ImageUrl,StatusSTID")]
BugsSummary bugsSummary, IFormFile myfile)
{
if (id != bugsSummary.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
//bugsSummary.ImageUrl = "";
bugsSummary.ImageUrl = await UserFile.UploadeNewImageAsync(bugsSummary.ImageUrl,
myfile, _environment.WebRootPath, Properties.Resources.ImgFolder, 100, 100);
bugsSummary.PublicationDate = DateTime.Today.Date;
bugsSummary.UserId = _userManager.GetUserId(User);
_context.Update(bugsSummary);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!BugsSummaryExists(bugsSummary.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["UserId"] = new SelectList(_context.Users, "Id", "Id", bugsSummary.UserId);
ViewData["ProjectsPId"] = new SelectList(_context.Projects, "PId", "PName", bugsSummary.ProjectsPId);
return View(bugsSummary);
}
'''
Form:
'''
<form asp-action="Edit" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="ProjectsPId" class="control-label"></label>
<input asp-for="ProjectsPId" class="form-control" />
<span asp-validation-for="ProjectsPId" class="text-danger"></span>
</div>
<input type="hidden" asp-for="UserId" />
<div class="form-group">
<label asp-for="Bug" class="control-label"></label>
<input asp-for="Bug" class="form-control" />
<span asp-validation-for="Bug" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="BugSummary" class="control-label"></label>
<input asp-for="BugSummary" class="form-control" />
<span asp-validation-for="BugSummary" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="TesterName" class="control-label"></label>
<input asp-for="TesterName" class="form-control" />
<span asp-validation-for="TesterName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ImageUrl" class="control-label"></label>
<img src="#Url.Content("~/" + AkelTestingTool.Properties.Resources.ImgFolder + "/" + Model.ImageUrl)" width="150" height="90" />
<input type="file" name="myfile" id="file" accept=".png,.jpg,.jpeg,.gif,.tif" class="btn btn-default" />
<input asp-for="ImageUrl" class="form-control" />
<span asp-validation-for="ImageUrl" class="text-danger"></span>
</div>
<label asp-for="Status.Name" class="control-label"></label>
#foreach (Microsoft.AspNetCore.Mvc.Rendering.SelectListItem status in ViewBag.StatusList as Microsoft.AspNetCore.Mvc.Rendering.SelectList)
{
if (int.Parse(status.Value) == Model.StatusSTID)
{
status.Selected = true;
}
else
{
status.Selected = false;
}
}
<div class="from-group">
<select asp-for="Status.Name" asp-items="#(ViewBag.StatusList)"></select>
#* <select asp-for="StatusSTID" asp-items="ViewBag.StatusList","Name" ></select> *#
<span asp-validation-for="Status.Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
'''

Class receiving the value reset

I'm doing insert via post, but my class is getting the zero values of the inputs.
The values of the inputs are passed via variable and corrections are displayed, but at the time of the post are coming down.
The most interesting thing is if you type inside the input, then the values come correctly.
<form method="post">
<div class="col-sm-3">
<label>SALDO</label>
<div style="border:1px solid #bbb9b9; border-radius:3px;"></div>
<br />
<div class="form-group">
<label asp-for="Caixas.ValorFinalDinheiro" class="control-label"></label>
<input asp-for="Caixas.ValorFinalDinheiro" name="Caixas.ValorFinalDinheiro" id="Caixas.ValorFinalDinheiro" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalCheque" class="control-label"></label>
<input asp-for="Caixas.ValorFinalCheque" class="form-control finalFundo" disabled="disabled"/>
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalBoleto" class="control-label"></label>
<input asp-for="Caixas.ValorFinalBoleto" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalCartao" class="control-label"></label>
<input asp-for="Caixas.ValorFinalCartao" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalDeposito" class="control-label"></label>
<input asp-for="Caixas.ValorFinalDeposito" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinal" class="control-label"></label>
<input asp-for="Caixas.ValorFinal" class="form-control" style="background-color:#9FF781" />
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<input type="submit" value="Confirmar o Fechamento do Caixa" class="btn btn-primary btn-sm" />
<a asp-page="Index" class="btn btn-success btn-sm">Retorna ao Caixa</a>
</div>
</div>
</form>
[BindProperty]
public Caixas Caixas { get; set; }
public async Task<IActionResult> OnPostAsync()
{
var C = _context.Caixas.Find(Caixas.Id);
C.fechado = true;
C.DataFinal = DateTime.Now;
C.HoraFinal = DateTime.Now;
C.FuncionarioFechamentoId = _userManager.GetUserId(HttpContext.User);
C.ValorFinalDinheiro = Caixas.ValorFinalDinheiro;
C.ValorFinalCheque = Caixas.ValorFinalCheque;
C.ValorFinalBoleto = Caixas.ValorFinalBoleto;
C.ValorFinalCartao = Caixas.ValorFinalCartao;
C.ValorFinalDeposito = Caixas.ValorFinalDeposito;
C.ValorFinal = Caixas.ValorFinal;
C.ValorSaida = Caixas.ValorSaida;
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Remove Caixas property and add it as parameter in your OnPostAsync method:
// [BindProperty]
// public Caixas Caixas { get; set; }
[HttpPost]
public async Task<IActionResult> OnPostAsync([FromBody]Caixas c)
{
// ...
}

Javascript click handlers

The task is :
I have done the form and the design bit for the web page but never came across this push: function() in javascript
I am confused on how to approach or do this task any help will be appreciated! Thank you :)
var _itq = {
push: function() {
console.log.apply(console, arguments);
}
};
_itq.push("_itq", "initialised", "ok");
<form id="commentForm" method="get" action="">
<div class="form-group">
<input class="form-control" type="email" placeholder="Enter Your Email" required>
</div>
<div class="form-group">
<div>
<input type="text" class="form-control" placeholder=" Enter Your Name" pattern=".{2,}" required title="2 characters minimum">
</div>
<div>
<input type="password" class="form-control" placeholder="Your Password" pattern=".{5,10}" required title="5 to 10 characters">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-block btn-info">Sign Up</button>
</div>
</form>
Here is the second question, this should set you on the right path for question 1.
document.getElementById('commentForm').onsubmit = function() { // attach to form submit
var email = document.getElementById("email").value; // get the email value (add id="email" to the email input in the html)
var name = document.getElementById("name").value; // get the name (add id="name" to the name input in the html)
var initials = name.split(' ').map(function(v, i) { // get the initials by splitting
return v[0] // on space and taking the 1st char
}).join('');
_itq.push(email, initials); // pass into your push
return false; // return false to stop the submit (optional depending on requirements)
};
working snippet below
var _itq = {
push: function() {
console.log.apply(console, arguments);
}
};
_itq.push("_itq", "initialised", "ok");
document.getElementById('commentForm').onsubmit = function() {
var email = document.getElementById("email").value;
var name = document.getElementById("name").value;
var initials = name.split(' ').map(function(v, i) {
return v[0]
}).join('');
_itq.push(email, initials);
return false;
};
<form id="commentForm" method="get" action="">
<div class="form-group">
<input id="email" class="form-control" type="email" placeholder="Enter Your Email" required>
</div>
<div class="form-group">
<div>
<input id="name" type="text" class="form-control" placeholder=" Enter Your Name" pattern=".{2,}" required title="2 characters minimum">
</div>
<div>
<input type="password" class="form-control" placeholder="Your Password" pattern=".{5,10}" required title="5 to 10 characters">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-block btn-info">Sign Up</button>
</div>
</form>
Try Something like this
Your HTML:
<button type="submit" class="btn btn-block btn-info" onclick="fillarr()">Sign Up</button>
JS:
function fillarr(){
_itq.push(document.getElementById("emailId").value, document.getElementById("nameId").value);
}
You still need to add an ID for your Email and Name field though

Resources