When passing the model from my view to my controller, the data is all null. I was able to successfully pass it using an ActionLink but I don't think that is the best way; for security reasons (I do not want sensitive data in the querystring).
My models
public class DashboardModel
{
// Dasboard quick numbers
public int TotalUsers { get; set; }
public int TotalUnauthUsers { get; set; }
public int GamesPlayed { get; set; }
public int AssociatedGroups { get; set; }
public int TotalGroups { get; set; }
// Dashboard table
public IEnumerable<ManageUserData> UnauthUsers { get; set; }
}
public class ManageUserData
{
public string UserName { get; set; }
public int AlternateId { get; set; }
public string Email { get; set; }
public string Role { get; set; }
public IEnumerable<string> InvestigatorGroups { get; set; }
public string Institution { get; set; }
// User status
public bool AccountLocked { get; set; }
public bool EmailConfirmed { get; set; }
}
Snippet of my view
#model TestGame.ViewModels.DashboardModel
#foreach (var user in Model.UnauthUsers)
{
<tr>
<td>#user.UserName</td>
<td>#user.AlternateId</td>
<td>#user.Email</td>
<td>#user.Role</td>
<td>
#if (!user.EmailConfirmed)
{
<div class="text-warning">Unconfirmed Email</div>
}
#if (user.AccountLocked)
{
<div class="text-danger">Account Locked</div>
}
</td>
<td>
#if (user.AccountLocked || !user.EmailConfirmed)
{
using (Html.BeginForm("Manage", "Admin", FormMethod.Post))
{
#Html.HiddenFor(x => user.UserName)
#Html.HiddenFor(x => user.Email)
<input type="submit" value="Manage" />
You have to make sure the path starts with the object being posted back; what you have will work great if the action being posted to (HttpPost Admin/Manage action) takes an object of type User; if it takes an object of the model type, change your form to the following:
for (var i = 0; i < Model.UnAuthUsers.Count; i++)
using (Html.BeginForm("Manage", "Admin", FormMethod.Post))
{
#Html.HiddenFor(x => x.UnAuthUsers[i].UserName)
#Html.HiddenFor(x => x.UnAuthUsers[i].Email)
<input type="submit" value="Manage" />
Creating a reference from the model (being x) will do the trick.
EDIT: Based on comments, add two properties to your model:
public class DashboardModel
{
public string SelectedUserName { get; set; }
public string SelectedEmail { get; set; }
}
In your form, render a hidden for for that name; I've had trouble using HiddenFor, so I've in the past used the hidden directly:
using (Html.BeginForm("Manage", "Admin", FormMethod.Post))
{
<input type="hidden" name="#Html.NameFor(i => i.SelectedUserName)" value="#Model.UnauthUsers[i].UserName" />
<input type="hidden" name="#Html.NameFor(i => i.SelectedEmail)" .. />
And upon submitting the form, the user from that form will be posted back via the model, via these new properties.
Related
I have a CategoryModel that contains a list item for the ItemModel as well as some other strings etc.
I'm trying to pass the list from the view to the controller. The model on the view is the ABCModel which contains a list of 'Categories' of type CategoryModel. On the view I'm only looking to post the 'active' Category back to be saved.
When I do this the active category is passed with details added from the form but the list of items is null.
.cs Models
namespace Company.Models
{
public class ABCModel
{
public IList<CategoryModel> Categories;
public SummaryModel Summary;
}
}
namespace Company.Models
{
public class CategoryModel
{
public string Label { get; set; }
public bool Active { get; set; }
public decimal Amount { get; set; }
public string Frequency { get; set; }
public string Type { get; set; }
public List<ItemModel> Items;
}
)
namespace Company.Models
{
public class ItemModel
{
public string Label { get; set; }
public string Explanation { get; set; }
public string Amount { get; set; }
public string Frequency { get; set; }
public bool Flag { get; set; }
public string Note { get; set; }
}
}
Controller
[HttpPost]
public ActionResult SaveItems([FromForm] CategoryModel activeCategoryModel, [FromForm] List<ItemModel> items, [FromQuery] string activecategory, [FromQuery] string nextcategory)
{
...
}
View
#model ABCModel
#{ CategoryModel nextCategory = null; }
#{ CategoryModel activeCategoryModel = null; }
#if (Model.Categories.Any())
{
#for (var i = 0; i < Model.Categories.Count; i++)
{
Company.Models.CategoryModel category = Model.Categories[i];
#if (category.Active)
{
activeCategoryModel = category;
if ((i + 1 < Model.Categories.Count) && (Model.Categories[i + 1] != null))
{
nextCategory = Model.Categories[i + 1];
}
}
}
}
<form id="ABC-form" class="needs-validation" novalidate="" asp-action="SaveItems" asp-controller="Home" method="post">
#if (activeCategoryModel != null)
{
<input asp-for="#activeCategoryModel.Label" type="hidden" />
<input asp-for="#activeCategoryModel.Active" type="hidden" />
<input asp-for="#activeCategoryModel.Amount" type="hidden" />
<input asp-for="#activeCategoryModel.Frequency" type="hidden" />
<input asp-for="#activeCategoryModel.Type" type="hidden" />
#if (activeCategoryModel.Items.Any())
{
#for (var i = 0; i < activeCategoryModel.Items.Count; i++)
{
#Html.HiddenFor(x => activeCategoryModel.Items[i].Label)
#Html.HiddenFor(x => activeCategoryModel.Items[i].Explanation)
#Html.TextBoxFor(x => items[i].Amount, new { #class = "form-control" })
}
}
}
<button id="continue" type="submit" asp-action="SaveItems" asp-route-activecategory="#activeCategoryModel.Label" asp-route-nextcategory="#(nextCategory != null ? nextCategory?.Label : null)">Continue</button>
</form>
I had been including IFormCollection as a parameter within the SaveItems on the controller and this showed that the items are being passed in the format I was thinking would work but the model only shows the values entered and the list of items is null.
How can I populate the list of items on the active category model from the view?
Items in CategoryModel should be a property not a filed,so your CategoryModel should be like below:
public class CategoryModel
{
public string Label { get; set; }
public bool Active { get; set; }
public decimal Amount { get; set; }
public string Frequency { get; set; }
public string Type { get; set; }
public List<ItemModel> Items { get; set; }
}
HTML.BeginCollectionItem does not return values to the controller. It always return NULL in the controller. I am not sure if has got anything to do if there is a partial view within another partial view. Below is the snippet of the code/view.
ProductEditModel
public class ProductEditModel
{
// Product details displayed on edit form
public Product ProductModel { get; set; }
public IList<ProductAssetAudioEditModel> ProductAssetAudios { get; set;}
}
ProductAssetAudioEditModel
public class ProductAssetAudioEditModel
{
public int ProductId { get; set; }
public int? ProductAssetId { get; set; }
public virtual IList<ProductAssetResourceEditModel> ProductAssetResources { get; set; }
}
ProductAssetResourceEditModel
public class ProductAssetResourceEditModel
{
public int? ProductAssetResourceId { get; set; }
public int ProductAssetId { get; set; }
public int ResourceNumber { get; set; }
public int? ElectronicFileId { get; set; }
public ElectronicFile ElectronicFile { get; set; }
}
ProductEditView.cshtml
<div id="audio">
#foreach (ProductAssetAudioEditModel audio in Model.ProductAssetAudios)
{
Html.RenderPartial("_ProductAssetAudioRow", audio);
}
</div>
_ProductAssetAudioRow.cshtml
#using (Html.BeginCollectionItem("ProductAssetAudios"))
{
....
<tbody>
#foreach (var resource in Model.ProductAssetResources)
{
Html.RenderPartial("_ProductAssetAudioResource", resource);
}
</tbody>
.....
}
_ProductAssetAudioResource
#using (Html.BeginCollectionItem("ProductAssetResources"))
{
#Html.HiddenFor(m => Model.ProductAssetResourceId)
#Html.HiddenFor(m => Model.ProductAssetId)
<td>
#if (Model.ElectronicFileId.HasValue)
{
#Html.HiddenFor(model => model.ElectronicFileId)
#Html.ActionLink(Model.ElectronicFile.FileName, "Details", "File", new { id = Model.ElectronicFileId, area = "Edi" }, null);
}
</td>
<td>
#Html.EditorFor(c => Model.TrackTitle)
</td>
}
In the controller , ProductAssetResources is NULL even though edit page binds the properties correctly for editing.
I am not sure what I am missing here.
-Alan-
There is 1 form on which i will ask for Academic Details for :
Graduation
Post Graduation(Masters)
Professional Qualification.
So far any user say UserId="1" 3 entries will be created in my AcademicMaster each for bachelor,Master(Post Graduation) and Professional Qualification.
My Database AcademicMaster table fields and datamodel:
Id,Qualification(GraduationCourses),Acheievement,UserId
View Model:
public class AcademicViewModel
{
public int Id { get; set; }
public virtual Graduation Graduation{ get; set; }
public virtual PostGraduation PostGraduation{ get; set; }
public virtual ProfessionalQualification ProfessionalQualification{ get; set; }
}
public class Graduation
{
public string BachelorQualification { get; set; }
public string BachelorAchievement { get; set; }
}
public class PostGraduation
{
public string MasterQualification { get; set; }
public string MasterAchievement { get; set; }
}
public class ProfessionalQualification
{
public string ProfessionalQualifications { get; set; }
}
So my View is like this:
#model AcademicViewModel
#{
IEnumerable<SelectListItem> graduationList = ViewBag.GraduationList;
IEnumerable<SelectListItem> postGraduationList = ViewBag.PostGraduationList;
}
#using (Html.BeginForm())
{
<div class="row">
Bachelors
#Html.DropDownListFor(model => model.Graduation.Qualification, graduationList)
</div>
#Html.TextAreaFor(model => model.Graduation.Achievement)
<div class="row">
MASTERS
#Html.DropDownListFor(model => model.PostGraduation.Qualification, postGraduationList)
</div>
#Html.TextAreaFor(model => model.PostGraduation.Achievement)
<div class="row">
PROFESSIONAL QUALIFITCATION
#Html.TextAreaFor(model => model.ProfessionalQualification.ProfessionalQualifications)
</div>
<input type="submit" value="Save">
}
This is my Controller:
[HttpPost]
public ActionResult MyController(AcademicViewModel model)
{
//Actions
}
So is my View Model structure appropriate and how to create 3 entries in AcademicMaster Table??
I will start by saying that having one table may not be the best choice (what happens if later you start adding additional properties which may be applicable to Graduation that are not applicable to Professional - for example YearOfGraduation - you could end up with a huge number of fields, many of which may have null values.
However, if you want one table, then at least add another field so that you can identify if the data is related to Graduation, PostGraduation or Professional. The associated data model for the AcademicMasters table would be
public class AcademicMaster
{
public int ID { get; set; }
public string Type { get; set; } // may be an enum?
public string Qualification { get; set; }
public string Achievement { get; set; }
public int UserID { get; set; }
}
Side note: It might be better to use an enum for the Type property
public enum AcademicType
{
Graduation,
PostGraduation,
Professional
}
There does not seem to be any need to your current Graduation, PostGraduation and ProfessionalQualification models and your view model should be
public class AcademicViewModel
{
public string GraduationQualification { get; set; }
public string GraduationAchievement { get; set; }
public string PostGraduationQualification { get; set; }
public string PostGraduationAchievement { get; set; }
public string ProfessionalAchievement { get; set; }
public IEnumerable<SelectListItem> GraduationList { get; set; }
public IEnumerable<SelectListItem> PostGraduationList { get; set; }
}
Side notes: Its not clear what your current ProfessionalQualifications property is - does that get assigned to the Qualification field or the Acheievement field in the database? Since your using a view model, then it should include the SelectList's rather that using ViewBag.
Then your view will be
#model AcademicViewModel
#using (Html.BeginForm())
{
<h2>Graduation</h2>
#Html.DropDownListFor(m => m.GraduationQualification, Model.GraduationList)
#Html.TextAreaFor(m => m.GraduationAchievement)
... // repeat for PostGraduation and Professional
<input type="submit" value="Save">
}
And the POST method would be
[HttpPost]
public ActionResult MyController(AcademicViewModel model) // should be named Create?
{
var userID = ....
AcademicMaster graduation = new AcademicMaster
{
Type = AcademicType.Graduation,
Qualification = model.GraduationAchievement,
Achievement = model.GraduationAchievement,
UserId = userID;
};
db.AcademicMasters.Add(graduation);
// Repeat for PostGraduation and Professional
db.SaveChanges();
// redirect?
}
I have view to display Restaurant. In this view at the bottom of the page, I want to display Comments form to add comments about that Restaurant.
Can someone please help me to do this using MVC 4 & C#.
My Models has the followign two tables:
/Classifieds TABLE
public class Classifieds
{
[Key]
public string C_Unique_Id { get; set; }
public string AdType { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
//ClassifiedsComments TABLE
public class ClassifiedsComments
{
[Key]
public string CCommentsUniqueId { get; set; }
public string CommentAuthor { get; set; }
public string Comment { get; set; }
[ForeignKey("Classifieds")]
public string C_Unique_Id { get; set; } //this is the foreign key of Classified record
public virtual Classifieds Classifieds { get; set; }
}
Classifieds Details view:
#model SomeIndianShit.Models.Classifieds
#{
ViewBag.Title = "Details";
}
<table class="recordDetailsDisplayTableStype">
<tr>
<td colspan="2" align="left">
#Html.DisplayFor(model => model.Description)
<br /><br />
</td>
</tr>
<tr>
<td>
Ad Type
</td>
<td align="left"> :
#Html.DisplayFor(model => model.AdType)
</td>
</tr>
SOME OTHER FIELDS DISPLAY HERE
</table>
//Here I want to display "ClassifiedsComments" form to add comments to above Classified.
//HOW can I display the ClassifiedsComments create.cshtml code here??
Try this:
With View:
#using (Html.BeginForm("Classifieds", "ClassifiedsDetails", FormMethod.Post))
and
#using (Html.BeginForm("ClassifiedsComments", "ClassifiedsDetails", FormMethod.Post))
And use 1 Model for this:
public class ClassifiedsDetails
{
public Classifieds Model1{ get; set; }
public ClassifiedsComments Model2{ get; set; }
}
Update:
public class ClassifiedsDetails
{
public ClassifiedsDetails()
{
Model1 = new Classifieds();
Model2 = new ClassifiedsComments();
}
public Classifieds Model1{ get; set; }
public ClassifiedsComments Model2{ get; set; }
}
public class Classifieds
{
public Classifieds()
{
C_Unique_Id = String.Emty;
AdType = String.Emty;
//---- Add default setting here------
}
[Key]
public string C_Unique_Id { get; set; }
public string AdType { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
Or display list of comments in View with Model:
public class ClassifiedsDetails
{
public ClassifiedsDetails()
{
Model1 = new Classifieds();
Model2 = new List<ClassifiedsComments>();
}
public Classifieds Model1{ get; set; }
public List<ClassifiedsComments> Model2{ get; set; }
}
View:
#model ClassifiedsDetails
#Html.LabelFor(model => model.Model1.Title)
#foreach (var items in Model.Model2)
{
#item. //fields
}
To display data in view create view model, but to post comment, dont use model:
public class ClassifiedsViewModel
{
public ClassifiedsViewModel()
{
Comments = new List<ClassifiedsComments>();
}
public Classifieds Classifieds { get; set; }
public List<ClassifiedsComments> Comments { get; set; }
}
Fill this view model, use in view to display details and comments like above you write.
if(Model != null && Model.Classifieds != null)
{
<table> ...display details.. </table>
}
if(Model != null && Model.Comments != null)
{
<table> ...display comments with foreach loop.. </table>
}
And at bottom, create comment post form
#using (Html.BeginForm("SaveComment", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
//set to which Classifieds will this comment posted
#Html.Hidden("C_Unique_Id", Model.Classifieds.C_Unique_Id)
<fieldset>
#Html.TextBox("Comment")
//other fields ....
<input type="submit" value="Save" />
</fieldset>
}
Edit2: View model creating:
public ActionResult Details(int id)
{
//add breakpoint here and follow any step.
ClassifiedsViewModel viewModel = new ClassifiedsViewModel();
viewModel.Classifieds = db.Classifieds.Find(id);
viewModel.Comments = db.LoadCommentsByClassifiedsId(id); //create db method
// or instead of this line use:
viewModel.Comments = db.ClassifiedsComments.Where(e => e.C_Unique_Id == id).ToList();
return(viewModel);
}
You can then add a ViewModel like this
public class ClassifiedsDetailViewModel
{
public ClassifiedsDetailViewModel()
{
ClassifiedsComments = new ClassifiedsComments();
}
public Classifieds Classifieds { get; set; }
public ClassifiedsComments ClassifiedsComments { get; set; }
}
Pass this view model to your view and then add #Html.Partial("_CreateClassifiedsCommentsFormPartial", Model.ClassifiedComments) below the table. And in your partial view, you can use Html.BeginForm or the Ajax.BeginForm.
See sample here
I have a dropdownlist which I would like to be optional, however, the ModelState.IsValid check is trying to validate the property. Is there a way to tell it the property is optional?
Here is my code:
<h2>New Version</h2>
#using (Html.BeginForm()) {
<div>Version Number: #Html.TextBoxFor(m => m.Number)</div>
<div>Enabled: #Html.CheckBoxFor(m => m.IsActive)</div>
<div></div>
<div>
Template (optional):
#Html.DropDownListFor(m => m.TemplateVersionId, new SelectList(Model.CurrentVersions, "VersionId", "Number"),
"-- Select Template --")
</div>
<input type="submit" value="Save" />
#Html.ActionLink("Cancel", "Index");
}
and my ViewModel:
public class AddVersionViewModel
{
public double Number { get; set; }
public bool IsActive { get; set; }
public int TemplateVersionId { get; set; }
public ICollection<Version> CurrentVersions { get; set; }
}
I want the TemplateVersionId to be optional.
Change it to an int? to make it nullable.
When you create Nullable items it becomes optional in forms
public class AddVersionViewModel
{
public double Number { get; set; }
public bool IsActive { get; set; }
public int? TemplateVersionId { get; set; }
public ICollection<Version> CurrentVersions { get; set; }
}