Property get always null value in mvc4 - c#

I am working with c# and ASP.NET MVC4. I have a model class
public class Category {
private Category _parent;
public int Id { get; private set; }
public string Name { get; set; }
public int ParentId { get; private set; }
public Category Parent {
get {
if (this._parent == null) {
_parent = new Category(this.ParentId);
}
return _parent;
}
set {
_parent = value;
}
}
}
In the View I have a text input and a dropdown list for selecting the name and parent category:
<input type="text" placeholder="product name" class="form-control" name="name">
#Html.DropDownList("category", new SelectList(ViewBag.Category, "Id", "Name"), new { #class = "form-control" })
When I submit the form to the following Controller Action:
[HttpPost]
public ActionResult AddCategory(string category_NA, Category category_Parent)
{
................
}
I always get the category_Parent as null.
Can anyone help with this please? Thanks.

Related

Pass Model into Layout Page MVC

I am doing Web project with MVC 5 . I need pass to some data to layout page (data as Category_id or Category_Name).
I read some answers that say I need to make View Model , but my project must be in MVC and not in MVVM,
Do you any ideas?
Thanks!
you have to create a base view model that you will have to use for ALL your views
using Microsoft.AspNetCore.Mvc.Rendering;
public interface IBaseViewModel
{
public int CategoryId { get; set; }
public List<SelectListItem> CategoryList { get; set; }
}
public class BaseViewModel : IBaseViewModel
{
public int CategoryId { get; set; }
public List<SelectListItem> CategoryList { get; set; }
}
action
public IActionResult Index()
{
var baseViewModel=new BaseViewModel();
InitBaseViewModel(baseViewModel);
return View(baseViewModel);
}
private void InitBaseViewModel(IBaseViewModel baseViewModel)
{
//this is for test
// in the real code you can use context.Categories.Select ....
var items = new List<SelectListItem> {
new SelectListItem {Text = "Category1", Value = "1"},
new SelectListItem {Text = "Category2", Value = "2"},
new SelectListItem {Text = "Category3", Value = "3"}
};
baseViewModel.CategoryList= items;
}
layout
#model IBaseViewModel // you can omit it but I like to have it explicitly
#if(Model!=null && Model.CategoryList!=null && Model.CategoryList.Count > 0)
{
<select class="form-control" style="width:450px" asp-for="CategoryId" asp-items="CategoryList">
}
for another view you can create this action code
public IActionResult MyAction()
var myActionViewModel= new MyActionViewModel {
..... your init code
}
InitBaseViewModel(myActionViewModel);
return View(myActionViewModel)
}
public class MyActionViewModel : BaseViewModel
//or
public class MyActionViewModel : IBaseViewModel
{
public .... {get; set;}
}
You can pass directly a obj to View if you want in this way:
public virtual async Task<IActionResult> Index()
{
var model = await MethodThatRedurnModel();
return View(model);
}

Dynamically binding input-text to class/object propertys using Blazor

Im trying to build a dynamic list of input field for properties inside a class using Blazor but cant figur out how to bind/link the content of a input box to a property of a class. (the class have can have a large number of public props, not only Name and Description as in the below example, they are not always of the type "string")
lets say that I have this class/model:
public class customer{
public string Name { get; set; }
public int Age { get; set; }
public string Description { get; set; }
}
I got this blazor component (updateC.razor):
#inherits CLogic
#if (nfo != null)
{
#foreach (var obj in nfo)
{
<input type="text" class="form-control"
bind=#SCustomer.GetType().GetProperty(obj.ToString())/>
}
}
and finally Clogic:
public class Clogic: ComponentBase{
[Parameter]
public Customer SCustomer { get; set; } = new Customer();
[Parameter]
public PropertyInfo[] nfo { get; set; }
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
nfo = SCustomer.GetType().GetProperties();
StateHasChanged();
}
}
}
This is suppose to bind changes made in each input field to the correct property in the current instance of SCustomer (when input is made it is suppose to update the correct property of the class/object). This is not working, values inside of SCustomer are not changed after input are done. I'm guessing that i'm going about this completely wrong but can't seem to figure out how to make this work and can't find any examples doing this.
#foreach (var propertyInfo in nfo)
{
<input type="text" class="form-control"
value="#propertyInfo.GetValue(SCustomer)"
#onchange="#((ChangeEventArgs __e) =>
propertyInfo.SetValue(SCustomer, __e.Value.ToString()))" />
}
#foreach(propertyInfo in nfo)
{
<label>#propertyInfo.Name</label>
<input type="text" value="#propertyInfo.GetValue(SCustomer)" #onchange="#((ChangeEventArgs __e) =>
propertyInfo.SetValue(SCustomer,Convert.ChangeType(__e.Value,
propertyInfo.PropertyType,null))"/>
}
I finally found a way of doing this, here is my solution:
I created a helper class:
public class PropHolder
{
[Parameter]
public PropertyInfo info { get; set; }
[Parameter]
public string type { get; set; }
public PropHolder(PropertyInfo nfo, string propType)
{
info = nfo;
type = propType;
}
}
then i created a dictionary of this class and some cheking functions (this is inside of Clogic)
[Parameter]
public Dictionary<int, PropHolder> Props{ get; set; }
public void GetAllProps()
{
Props = new Dictionary<int, PropHolder>();
//nfo = SCustomer.GetType().GetProperties();
int Cid = 0;
foreach (PropertyInfo pif in SCustomer.GetType().GetProperties())
{
Props[Cid] = new PropHolder(pif, pif.PropertyType.Name);
Cid++;
}
}
public string CheckName(PropHolder propertyInfo)
{
if (propertyInfo.GetType() == typeof(PropHolder))
{
return propertyInfo.type;
}
else
{
return propertyInfo.GetType().Name.ToString();
}
}
public PropertyInfo getInfo(PropHolder propertyInfo)
{
if (propertyInfo.GetType() == typeof(PropHolder))
{
return propertyInfo.info;
}
else
{
return null;
}
}
and finally im able to loop over the keys of my dictionary and bind all values correctly (got lots of help figuring this out from the answere given by: "agua from mars")
here is the updateC.razor content:
#if (Props != null)
{
#foreach (int key in Props.Keys)
{
var pinfo = Props[key];
#if (CheckName(pinfo) == "String")
{
<input type="text" class="form-control" value=#(getInfo(pinfo).GetValue(SCustomer)) #onchange="#((ChangeEventArgs __e) => getInfo(pinfo).SetValue(SCustomer, __e.Value.ToString()))" />
}
}
}
this gives me a input box for every prop that is of the type String, if additional types are to be handled it can now easily be added. This is probably not the best sulution but it do work. I will update the answere if any better working solutions are posted.
I solved this using generics. This lets each input type pass on a generic type which defines what type it wants to use.
<input #bind="Name.Value" />
<input #bind="Age.Value" />
<div><code>Name</code>: #Name.Value</div>
<div><code>Age</code>: #Age.Value</div>
#code {
private FormField<string> Name { get; set; } = new FormField<string>();
private FormField<int> Age { get; set; } = new FormField<int>();
public class Form
{
public ICollection<IFormField> Fields { get; set; }
}
public interface IFormField
{
public int ControlType { get; set; }
}
public class FormField<T> : IFormField
{
public int ControlType { get; set; }
public T Value { get; set; }
}
}
Here it is on BlazorFiddle: https://blazorfiddle.com/s/wen1g26q

ViewModel is passing null values to controller on submit

My view :
#using (Html.BeginForm("", "", FormMethod.Post, new { id = "responseEntryForm" }))
{
<div id="SiteDeployment" class="tabcontent">
#Html.HiddenFor(m=>m.Sections,Model.Sections)
#for (int index = 0; index < Model.Sections.Count; index++)
{
<fieldset class="leftFieldset">
<div class="inputFieldDiv">
<label class="GroupHeadings"> #Model.Sections[index].Name</label><br />
#for (int subIndex = 0; subIndex < Model.Sections[index].SubSections.Count; subIndex++)
{
<div style="width:100%">
<div class="SubGroups">
#Model.Sections[index].SubSections[subIndex].Name
</div>
<div class="subEntries">
#for (int subsubIndex = 0; subsubIndex < Model.Sections[index].SubSections[subIndex].QuestionsList.Count; subsubIndex++)
{
<div class="subSections">
<label class="StrucQuestions"> #Model.Sections[index].SubSections[subIndex].QuestionsList[subsubIndex].Question</label>
#Html.DropDownListFor(m => m.Sections[index].SubSections[subIndex].QuestionsList[subsubIndex].Response, new SelectList(Model.ResponseList, "Value", "Text"), new { #class = "strucType", #id = "ddl_" + subIndex + Model.Sections[index].SubSections[subIndex].QuestionsList[subsubIndex].QuestionID })
</div>
}
</div>
</div>
}
</div>
</fieldset>
}
calling my controller and passing form data as below using ajax call:
function submitResponses() {
$.ajax({
url: '#Url.Action("SaveResponsesData", "Dashboard")',
datatype: 'json',
type: "POST",
data: $('#responseEntryForm').serialize(),
success: function (result) {
if (result == "T ") {
alert("Save is successful");
}
}
});
}
and my controller is like below:
[HttpPost]
public ActionResult SaveResponsesData(ResponseEntryViewModel objResponseEntryViewModel)
{
// ViewBag.SelectedType = TypeValue.ToUpper();
return View();
}
My ViewModel looks like below:
public class ResponseEntryViewModel
{
public int TypeID { get; set; }
public string TypeName { get; set; }
public string CompletedBy { get; set; }
public string CompletedOn { get; set; }
public int User_ID { get; set; }
public List<SectionDataModel> Sections = new List<SectionDataModel>();
public IEnumerable<SelectListItem> ResponseList { get; set; }
public class SectionDataModel
{
public int SectionID { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public int TypeId { get; set; }
public List<SubSectionModel> SubSections = new List<SubSectionModel>();
}
public class SubSectionModel
{
public int SubSectionID { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public int SectionId { get; set; }
public List<QuestionModel> QuestionsList = new List<QuestionModel>();
}
public class QuestionModel
{
public int SubSectionID { get; set; }
public int QuestionID { get; set; }
public string Question { get; set; }
public bool Response { get; set; }
}
}
In my view model ResponseEntryViewModel , i have a list of sections which has a list of sub sections which further has a list of questions and user is entering responses to those questions from my view.
When I click on submit. My viewModel is not having any values and Sections count is 0.
Any suggestions?
I'm not sure if this is the issue but it could be.
datatype
should be
dataType
Easiest would be just to remove dataType attribute completely. You are sending form-encoded data, not JSON there.
If you want to send JSON to server, you would have to use [FromBody] attribute:
[HttpPost]
public ActionResult SaveResponsesData([FromBody] ResponseEntryViewModel objResponseEntryViewModel)
{
// ...
return View();
}
However, you don't have data in JSON format. For JSON, normally you use JavaScript object and call JSON.stringify(data) function on it. JQuery's serialize() uses different format, for form-posting - Encode a set of form elements as a string for submission.
Example of this formatting would be: single=Single&multiple=Multiple&multiple=Multiple3&check=check2&radio=radio1
You might also have to set contentType: "application/x-www-form-urlencoded"
I think it would be a lot easier if you make a separete model for saving the response, something like, I hope you understand what I trying to show:
public class SaveResponseModel
{
public int SubSectionID { get; set; }
public int QuestionID { get; set; }
public bool Response { get; set; }
}
[HttpPost]
public ActionResult SaveResponsesData(List<SaveResponseModel> responses)
{
// ViewBag.SelectedType = TypeValue.ToUpper();
return View();
}
.cshtml:
for each Sections, SubSection, Question...{
<input name="responses.Index" type="hidden" value="#item.QuestionID" />
<input name="responses[#item.QuestionID].QuestionID" type="hidden" value="#item.QuestionID" />
<input name="responses[#item.QuestionID].SubSectionID" type="hidden" value="#item.SubSectionID" />
#Html.DropDownList((string)string.Format("responses[{0}].Response", #item.QuestionID), new SelectList(Model.ResponseList, "Value", "Text"), new { #class = "strucType", #id = "ddl_" + subIndex + Model.Sections[index].SubSections[subIndex].QuestionsList[subsubIndex].QuestionID })
}

Dynamic CMS ViewModel

I'm building a CMS where I can dynamically say what types of editors will be shown. For instance on 1 type of page I might want a textbox and a textarea, and another a textbox and datepicker
I've defined the fields
public class CMSField
{
public string Label { get; set; }
}
public class CMSTextField : CMSField
{
public string Value { get; set; }
}
public class CMSDatePickerField : CMSField
{
public DatePickerControl Value { get; set; }
}
My view model
public class CmsVM
{
public List<CMSField> fields { get; set; }
public CmsVM()
{
fields= new List<CMSField>();
}
}
Then in the controller I can set this up like so (manually defined for now but will be defined elsewhere):
public ActionResult Add()
{
CmsVM model = new CmsVM();
model.fields.Add(new CMSTextField() { Label = "Title", Value = "" });
model.fields.Add(new CMSDatePickerField()
{
Label = "Date",
Value = new DatePickerControl
{
SelectedDate = DateTime.Now
}
});
return View("Add", "_FormLayout", model);
}
Then in my view I show the relevant editor
e.g.
#model CmsVM
#for (var i = 0; i < Model.fields.Count(); i++)
{
var cmsField = Model.fields[i];
if (cmsField is CMSTextField)
{
var textBox = (CMSTextField)cmsField;
#Html.TextBoxFor(model => textBox.Value)
}
else if (cmsField is CMSDatePickerField)
{
var dp = (CMSDatePickerField)cmsField;
#Html.EditorFor(model => dp.Value)
#Html.ValidationMessageFor(model => dp.Value)
}
}
All good so far, I get whatever controls I define in the controller shown. But I now need to deal with the postback. The model is always null
[HttpPost]
public ActionResult AddNew(CmsVM model)
{
...
}
Is there a better way to set this up, so that I can post the viewmodel back?

Simple form post always sends null to controller

I have no idea what's the reason, it's so straight and simple, but, curiously, doesn't work. I always recieve NULL as model in controller.
Here is the code:
Model
public class EnrolleePlaces
{
[HiddenInput(DisplayValue=false)]
public int id { get; set; }
public string SpecialtyCode { get; set; }
public string Specialty { get; set; }
public int Places { get; set; }
}
Controller
public ViewResult EditPlaces(int id)
{
return View(repo.EnrolleePlaces.FirstOrDefault(p => p.id == id));
}
[HttpPost]
public ActionResult NewPlaces(EnrolleePlaces places) // 'places' is ALWAYS null
{
if (ModelState.IsValid)
{
repo.SaveEnrolleePlaces(places);
return RedirectToAction("Settings");
}
return View("EditPlaces", places);
}
public ViewResult CreatePlaces()
{
return View("EditPlaces", new EnrolleePlaces());
}
And a view
#model Domain.Entities.EnrolleePlaces
#{
Layout = null;
}
Edit: #Model.Specialty
#using (Ajax.BeginForm("NewPlaces", "Enrollee", new { area = "Admin" },
new AjaxOptions() { UpdateTargetId = "AdminContent" } ,
new { enctype = "multipart/form-data" }))
{
#Html.EditorForModel()
// here is input type="submit"
}
I have over 15 controllers in my project, made by the same pattern, but only this one is strange
have you tried changing the name of the parameter that your action method receives ?
for example:
[HttpPost]
public ActionResult NewPlaces(EnrolleePlaces dd) // any name other than "places"
{
if (ModelState.IsValid)
{
repo.SaveEnrolleePlaces(places);
return RedirectToAction("Settings");
}
return View("EditPlaces", places);
}

Categories