Why is my controller getting back an empty viewmodel from view? - asp.net-mvc

I have a viewmodel:
public class ProductInShopsCheckboxViewModel
{
public ProductInShop? ProductInShop { get; set; }
public IEnumerable<ProductInShop>? Checkboxes { get; set; }
}
view(that works):
#model WebApp.ViewModels.ProductInShopsCheckboxViewModel
#if (Model?.Checkboxes != null) {
<h4>#Model.Checkboxes!.ElementAt(0).Shop!.ShopName</h4>
<p></p>
<form asp-action="Create">
#for (var i = 0; i < Model.Checkboxes!.Count(); i++) {
<input type="checkbox" asp-for="Checkboxes.ElementAt(i).IsSelected">#Model.Checkboxes.ElementAt(i).Product?.ProductName
<p></p> }
<div class="form-group">
<input type="submit" value="Send email" class="btn btn-primary"/>
</div>
</form> }
<p></p>
<a asp-action="Index">Back to Shops</a>
and controller:
[HttpPost]
public IActionResult Create(ProductInShopsCheckboxViewModel vm)
{
//TODO: check which vm.Checkboxes items are selected
//Send those items to mail(code ready)
}
However I get back viewmodel with empty list and I cannot figure out what I'm doing wrong. I appreciate any advice.

Related

how can i show the data that the user entered in the file input when he want to edit it

for your information the file is saved as a binary data
here is is the model:
public class Movie
{
[Key]
public int MovieId { get; set; }
public string MovieName { get; set; }
public string MovieDescription { get; set; }
public byte[] MovieImage { get; set; }
public string MovieTrailer { get; set; }
//FK
public int AdminId { get; set; }
//Navigation
public Admin Admin { get; set; }
public ICollection<Event> Events { get; set; }
public AddingCategory AddingCategory { get; set; }
public ViewingMovie ViewingMovie { get; set; }
}
}
And here is the controller :
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.tblMovies.FindAsync(id);
if (movie == null)
{
return NotFound();
}
ViewData["AdminId"] = new SelectList(_context.Set<Admin>(), "AdminId", "Email", selectedValue: movie.AdminId);
return View(movie);
}
// POST: Movies/Edit/5
// To protect from overposting attacks, 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> Edit(int id, [Bind("MovieId,MovieName,MovieDescription,MovieImage,MovieTrailer,AdminId")] Movie movie, IFormFile MovieImage)
{
if (id != movie.MovieId)
{
return NotFound();
}
if (MovieImage != null)
{
//This code is used to copy image to DataBase
using (var myStream = new MemoryStream())
{
await MovieImage.CopyToAsync(myStream);
movie.MovieImage = myStream.ToArray();
}
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.MovieId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["AdminId"] = new SelectList(_context.Set<Admin>(), "AdminId", "Email", movie.AdminId);
return View(movie);
}
and the last here is the edit view:
<div class="row">
<div class="col-md-4">
<form asp-action="Edit" method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="MovieId" />
<div class="form-group">
<label asp-for="MovieName" class="control-label"></label>
<input asp-for="MovieName" class="form-control" />
<span asp-validation-for="MovieName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieDescription" class="control-label"></label>
<input asp-for="MovieDescription" class="form-control" />
<span asp-validation-for="MovieDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieImage" class="control-label"></label>
<input asp-for="MovieImage" class="form-control" type="file" />
<span asp-validation-for="MovieImage" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MovieTrailer" class="control-label"></label>
<input asp-for="MovieTrailer" class="form-control" />
<span asp-validation-for="MovieTrailer" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AdminId" class="control-label"></label>
<select asp-for="AdminId" class="form-control" asp-items="ViewBag.AdminId"></select>
<span asp-validation-for="AdminId" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
all i want is to show what the user entered in the file input and if he want to edit it or keep as it is.
i have been struggling with this problem in days PLEASE somebody help me out

Asp.net core form always returns null

I'm trying to build one view that includes all (Create, Edit, Delete, and Index) in one View which is Index.
The problem is with Editing. Always returns null to the controller as shown in the gif.
I have Model and ViewModel as follows.
The Model BootstrapCategory
public class BootstrapCategory
{
[Key]
public Guid Id { get; set; }
[MaxLength(20)]
[Required]
public string Category { get; set; }
}
The ViewModel VMBPCategoris
public class VMBPCategoris
{
public List<BootstrapCategory> bootstrapCategories { get; set; }
public BootstrapCategory bootstrapCategory { get; set; }
}
The View
Note: Edit not by the usual button in the table it instead by another
button as shown in the gif
#model VMBPCategoris
#foreach (var item in Model.bootstrapCategories)
{
<tr>
<td>
<form asp-action="Edit" method="post">
<input type="hidden" asp-for="#item.Id" />
<div class="#item.Id d-none">
<div class="input-group">
<input id="btnGroupEdit" type="submit" value="Save" class="input-group-text btn btn-primary" />
<input asp-for="#item.Category" class="form-control" aria-label="Input group example" aria-describedby="btnGroupEdit">
</div>
<span asp-validation-for="#item.Category" class="text-danger"></span>
</div>
</form>
<div class="#item.Id">
#Html.DisplayFor(modelItem => item.Category)
</div>
</td>
<td>
Edit |
<a asp-action="Details" asp-route-id="#item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
The Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit([Bind("Id,Category")] BootstrapCategory bootstrapCategory)
{
_context.Update(bootstrapCategory);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
//return View(vMBPCategoris);
}
The view model class VMBPCategoris needs to have its members properties assigned to instances in a constructor:
public class VMBPCategoris
{
public List<BootstrapCategory> bootstrapCategories { get; set; }
public BootstrapCategory bootstrapCategory { get; set; }
public VMBPCategoris()
{
bootstrapCategories = new List<BootstrapCategory>();
bootstrapCategory = new BootstrapCategory();
}
}
You can give a name to your input tag.Change your form like below.
<form asp-action="Edit" method="post">
<input type="hidden" asp-for="#item.Id" name="Id"/>
<div class="#item.Id d-none">
<div class="input-group">
<input id="btnGroupEdit" type="submit" value="Save" class="input-group-text btn btn-primary" />
<input asp-for="#item.Category" name="Category" class="form-control" aria-label="Input group example" aria-describedby="btnGroupEdit">
</div>
<span asp-validation-for="#item.Category" class="text-danger"></span>
</div>
</form>
I made an easy solution by changing a little bit with the ViewModel and using the value attribute to get value from a model and using asp-for to set a value to another model.
It's all clarified in that Stackoverflow post

How to pass a complex model back to the controller in asp.net mvc

New to web development.
I have a view that allows user to select an excel file.
When submit "preview" button is pressed file is read and data is sent back to the user to preview the data.
Then I want to be able send the model back to the control for db upload.
(this is the part I'm struggling with).
ViewModel:
public class UploadItemsViewModel
{
public List<Item> Items { get; set; }
public int CompanyID { get; set; }
public Company Company { get; set; }
public HttpPostedFileBase upload { get; set; }
public UploadJournalsViewModel()
{
Items = new List<Item>();
}
}
Controller:
public ActionResult Upload(FormCollection formCollection, int CompanyID)
{
if (Request != null)
{
HttpPostedFileBase file = Request.Files["UploadedFile"];
if ((file != null) && (file.ContentLength > 0) && !string.IsNullOrEmpty(file.FileName))
{
string fileName = file.FileName;
string fileContentType = file.ContentType;
byte[] fileBytes = new byte[file.ContentLength];
var data = file.InputStream.Read(fileBytes, 0, Convert.ToInt32(file.ContentLength));
}
}
UploadItemsViewModel itmViewModel = new UploadItemsViewModel { Company = db.Companies.Find(CompanyID), CompanyID = CompanyID };
return View(itmViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload(UploadItemsViewModel itmViewModel, string Preview, string Upload)
{
if (ModelState.IsValid)
{
if (itmViewModel.upload != null && itmViewModel.upload.ContentLength >0)
{
try
{
itmlViewModel.Items = App.Services.ItemsMassUploadFileRead.ReadExcelFile(itmViewModel.upload, db.Companies.Find(itmViewModel.CompanyID));
if (string.IsNullOrEmpty(Preview))
{
foreach (var itm in itmViewModel.Items)
{
itm.StartDate = DateTime.Today;
itm.CompanyID = itmViewModel.CompanyID;
itm.User = null;
itm.Items.Add(itm);
db.SaveChanges();
}
return View();
}
else
{
return View(itmViewModel);
}
} }
catch (Exception ex)
{
ModelState.AddModelError("File", ex.Message.ToString());
return View(itmViewModel);
}
}
else
{
ModelState.AddModelError("File", "Please Upload Your file");
}
}
return View(itmViewModel);
}
View:
#using (Html.BeginForm("Upload", "ItemsUpload", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.CompanyID)
<div class="form-group">
<div class="input-group">
<label class="input-group-btn">
<span class="btn btn-default">
Browse… <input type="file" style="display: none;" accept=".xlsx" name="upload">
</span>
</label>
<input type="text" class="form-control " readonly>
</div>
<span class="help-block">
Please use a provided Excel template
</span>
</div>
<div class="form-group">
<input type="submit" value="Preview" name ="Preview" class="btn btn-default" disabled style="display: none" id="submit"/>
</div>
<div class="form-group">
<input type="submit" value="Upload" name="Upload" class="btn btn-default" id="Upload" />
</div>
<div class="help-block" id="previewHelp" style="display: none">
Preview results and scroll down to upload data to the database.
</div>
if (Model.Journals.Count != 0)
{
table here to preview the upload
}
After clicking the Upload button model comes back without the "items" collection.
The Items list will be always null in the controller, because you don't rendered any input on the View with the name Items

MVC 4 Model Binding to Radio button list on Submit

My project is MVC 4 with jquery mobile. I'm trying to figure out how to fill my model with data on submit with the following:
From a hidden value that will be populated on the get request
Checked Radio button value from a list on the view
Here is my Model:
public class ResultsModel
{
[Display(Name = "PatientFirstName")]
public string PatientFirstName { get; set; }
[Display(Name = "PatientLastName")]
public string PatientLastName { get; set; }
[Display(Name = "PatientMI")]
public string PatientMI { get; set; }
public List<QuestionModel> QuestionList = new List<QuestionModel>();
}
public class QuestionModel
{
public string Question { get; set; }
public int QuestionID { get; set; }
public int AnswerID { get; set; }
}
It has a collection of questions that is filled with data on the get request. Here is the controller code:
public class ResultsController : Controller
{
//
// GET: /Results/
public ActionResult Index()
{
if (Request.IsAuthenticated)
{
ResultsModel resultsModel = new ResultsModel();
//Get data from webservice
myWebService.TestForm inrform;
var service = new myWebService.myService();
testform = service.TestForm(id);
if (testform != null)
{
//Render the data into results data model
int count = 1;
string text = string.Empty;
foreach (myWebService.Question questiontext in testform.QuestionList)
{
QuestionModel newquestion = new QuestionModel();
text = "Question " + count + ": " + questiontext.text;
if (questiontext.text != null)
{
newquestion.Question = text;
newquestion.QuestionID = questiontext.id;
}
resultsModel.QuestionList.Add(newquestion);
count += 1;
}
}
else
{
//Error
}
return View(resultsModel);
}
// Error
return View();
}
[AllowAnonymous]
[HttpPost]
public ActionResult Index(ResultsModel model,FormCollection fc)
{
if (fc["Cancel"] != null)
{
return RedirectToAction("Index", "Home");
}
if (ModelState.IsValid)
{
in the post action, the model.QuestionList is always empty. Here is my View:
#model Models.ResultsModel
#{
ViewBag.Title = Resources.Account.Results.Title;
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<li data-role="fieldcontain">
#Html.LabelFor(m => m.INRTestDate)
#Html.EditorFor(m => m.INRTestDate)
#Html.ValidationMessageFor(model => model.INRTestDate)
</li>
<li data-role="fieldcontain">
#Html.LabelFor(m => m.INRValue)
#Html.TextBoxFor(m => m.INRValue)
</li>
<li>
#for (int i = 0; i < Model.QuestionList.Count; i++)
{
<div data-role="label" name="question" >
#Model.QuestionList[i].Question</div>
#Html.HiddenFor(m => m.QuestionList[i].QuestionID)
<div data-role="fieldcontain">
<fieldset data-role="controlgroup" data-type="horizontal" data-role="fieldcontain" >
<input id="capture_type_yes" name="answer_type" type="radio" data-theme="c" value="1" />
<label for="capture_type_yes" >
Yes</label>
<input id="capture_type_no" name="answer_type" type="radio" data-theme="c" value="0" />
<label for="capture_type_no">
No</label>
<input id="capture_type_na" name="answer_type" type="radio" data-theme="c" value="2" />
<label for="capture_type_na">
Not Applicable</label>
</fieldset>
</div> }
<label for="textarea-a">
Comments</label>
<textarea name="textarea" id="textarea-a"> </textarea>
<fieldset class="ui-grid-a">
<div class="ui-block-a">
<button type="submit" name="Submit" data-theme="c">Submit</button></div>
<div class="ui-block-b">
<button type="submit" name="Cancel" data-theme="b" class="cancel">Cancel</button></div>
</fieldset>
</li>
In the for each code above, my model.Questionlist collection is not being updated. I also need to figure out how I can tie the radio button click (Yes,No or Not Applicable) to the AnswerID property of my model.Questionlist collection
It actually ended up being this line:
public List<QuestionModel> QuestionList = new List<QuestionModel>();
I needed the get/set to initialize
public List<QuestionModel> QuestionList { get; set; }
Please Can You Add Code after
[AllowAnonymous]
[HttpPost]
public ActionResult Index(ResultsModel model,FormCollection fc)
{
if (fc["Cancel"] != null)
{
return RedirectToAction("Index", "Home");
}
if (ModelState.IsValid)
{
This would be a good post if you finish your code.
MVC5 Bind a LIST for Radio Button with Razor
Create a class RadioList anywhere in the program
public class RadioList
{
public int Value { get; set; }
public string Text { get; set; }
public RadioList(int Value, string Text)
{
this.Value = Value;
this.Text = Text;
}
}
In your Controller
List<RadioList> Sexes = new List<RadioList>();
Sexes.Add(new RadioList(1, "Male"));
Sexes.Add(new RadioList(2, "Female"));
Sexes.Add(new RadioList(3, "Other"));
ViewBag.SexeListe = Sexes;
In the Razor view (replace model.Sexe by the value in your database/model field
#foreach (RadioList radioitem in ViewBag.SexeListe) {
#Html.RadioButtonFor(model => model.Sexe, #radioitem.Value) #Html.Label(#radioitem.Text)
}

Stack trace error

I am having the error message:I can`t figure out what is wrong. Can someone help me please. Thanks.
Stack Trace:
[NotSupportedException: Collection is
read-only.]
System.SZArrayHelper.Clear() +56
System.Web.Mvc.CollectionHelpers.ReplaceCollectionImpl(ICollection`1
collection, IEnumerable newContents)
+125
ExecuteStep(IExecutionStep step,
Boolean& completedSynchronously) +184
My code is:
Model:
namespace TestArrays.Models
{
public class Person
{
public CategoriesRow[] Categories { get; set; }
public Person()
{
Categories = new CategoriesRow[1];
Categories[0] = new CategoriesRow();
Categories[0].Name = "Bug";
Categories[0].ID = "0";
}
}
public class CategoriesRow
{
public String Name { get; set; }
public String ID { get; set; }
}
}
Controller
public ActionResult Create()
{
return View(new Person());
}
//
// POST: /Person/Create
[HttpPost]
public ActionResult Create(Person person)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}
Views
<form id="Form" action="<%=Url.Action("Create",new{Action="Create"}) %>" method="post" enctype="multipart/form-data">
<div id="categoriessection" >
<h3>Categories</h3>
<ul class= "list" id="categoryList">
<%if (Model.Categories!=null) {%>
<%int count = 0; %>
<%foreach (var category in Model.Categories)
{%>
<%if (!String.IsNullOrEmpty(category.Name))
{%>
<li><input type="hidden" name="Categories.Index" value="<%=count%>" />
<input type="text" value="<%=category.Name%>" name="Categories[<%=count%>].Name" style="width:280px"/>
<input type="hidden" value="<%=category.ID%>" name="Categories[<%=count++%>].ID" style="width:280px"/>
<input type="button" value = "Delete"/>
</li>
<%}
%>
<%} %>
<%} %>
<li> <input type="hidden" name="Categories.Index" value="value0" />
<input type="text" value="" name="Categories[value0].Name" style="width:280px"/>
<input type="hidden" value="" name="Categories[value0].ID" style="width:280px"/>
<input type="button" value= "Add" />
</li>
</ul>
</div>
<div>
<input type ="submit" value="Save" id="save" />
</div>
</form>
I usually use lists instead of vectors in models:
public List<CategoriesRow> Categories { get; set; }

Resources