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?
Related
i want to passing some values from table grid(View) to controller using jquery. But i cant bind it to Model.
Model:
{
public class TestModel
{
public string id { get; set; }
public string isChecked { get; set; }
}
}
View .post
$('#genArchive').on('click', function (e) {
var list = [];
$("#fakTable tr").each(function () {
var model = {}
model.id = $(this).find("td").eq(10).html();
model.isChecked = $(this).find("td").eq(11).html();
list.push(model);
})
$.post('/Home/DownloadAsArchive', { vars: list } );
});
Controller side with empty model after POST.
[Authorize]
[HttpPost]
public FileResult DownloadAsArchive(IList<TestModel> vars)
{
return null;
}
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);
}
My view model as
public class StudViewModel
{
public StudLineViewModel()
{
StudLine = new List<CreateReqLineViewModel>();
}
public StudheaderViewModel StudHeader { get; set; }
public List<StudLineViewModel> StudLine { get; set; }
}
public class StudheaderViewModel
{
public int Roll_no { get; set; }
public DateTime? birthday_date { get; set; }
}
public class StudLineViewModel
{
public int Sub_Id { get; set; }
public double marks { get; set; }
}
var MainVMObj = new StudViewModel();
var StudHeaderObj = new StudheaderViewModel ();
StudHeaderObj.Roll_no =1;
StudHeaderObj.birthday_date =null;
MainVMObj.StudHeader = StudHeaderObj ;
MainVMObj.StudLine =null;
return Partialview("StudInfo",MainVMObj );
Code in View
#Html.TextBoxFor(model => model.StudHeader.birthday_date ,new { #class = "InputText", onclick = "javascript:NewCssCal (this.Id,'ddMMMyyyy','arrow','','','','future')" })
expected Result
1. birth day assign null but it show as default date like 01-Jan-0001.
I was debug i found issue at
MainVMObj.StudHeader = StudHeaderObj -->assign object value it make null value to default value. so birth date from null to default(01-01-001)
Please provide the solution
Editors Give the Ans not edit the Post
I'm having a trouble with my project (ASP.NET MVC 5/AJAX/BOOTSTRAP).
When click on Save button on Page, .Net calls in POST the proper action, but the Hidden Fields for PSATOKEN does not contain value (see #Html.HiddenFor(m => m.PSAToken) in the View), despite PSAToken contains a GUID value (saw in Debug Mode) in the Controller method.
Let's see some code below.
Many thanks to answerers!
Model
public interface IPSAPageViewModel
{
String PSAToken { get; set; }
int IdPSAAzienda { get; set; }
}
public abstract class BasePSAPageViewModel : IPSAPageViewModel
{
public String PSAToken { get; set; }
public int IdPSAAzienda { get; set; }
}
public class DatiGeneraliViewModel : BasePSAPageViewModel
{
public DatiGeneraliViewModel()
{
this.Item = new InformazioniGenerali();
}
public Crea.PSA.ServiceLayer.BO.InformazioniGenerali Item { get; set; }
public List<SelectListItem> FormeGiuridicheList { set; get; }
public List<SelectListItem> FormeConduzioneList { set; get; }
}
Controller
private ViewResult ViewPSAPage(IPSAPageViewModel vm)
{
base.createViewBagPaginePrecSucc();
return View(vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
[HttpParamAction]
public ActionResult SalvaDatiGeneraliProsegui(DatiGeneraliViewModel vm)
{
return salvataggioDatiGenerali(vm, true);
}
[HttpPost]
[ValidateAntiForgeryToken]
[HttpParamAction]
public ActionResult SalvaDatiGenerali(DatiGeneraliViewModel vm)
{
//Here vm.PSAToken doesn't contain the value setted
return salvataggioDatiGenerali(vm);
}
private ActionResult salvataggioDatiGenerali(DatiGeneraliViewModel vm, bool proseguiCompilazione = false)
{
if (ModelState.IsValid)
{
var resp = aziendeManager.Save(vm.PSAToken, vm.Item, SessionManager.UserIdConnected, CONTROLLERNAME);
if (resp.Success)
{
var psaAzienda = resp.DataObject;
setVarsInSession(psaAzienda.idToken.ToString(), psaAzienda.idPsaAzienda.ToString(), psaAzienda.Aziende.ragioneSociale);
//Here there is some Value (POST)
vm.PSAToken = psaAzienda.idToken.ToString();
//vm.IdPSAAzienda = psaAzienda.idPsaAzienda.ToString();
if (proseguiCompilazione)
return RedirectToAction("DatiAziendaliRiepilogativi", new { id = psaAzienda.idToken });
}
else
ModelState.AddModelError("", resp.Message);
}
setSuccessMessage();
vm.FormeGiuridicheList = aziendeManager.GetAllFormeGiuridiche().ToSelectItems();
vm.FormeConduzioneList = aziendeManager.GetAllFormeConduzione().ToSelectItems();
return ViewPSAPage(vm);
}
View
to see the view click here
Here you can see the value at debug in VS
But in the generated HTML the Hidden Field of PSATOKEN is empty
I found the solution here:
patrickdesjardins.com/blog/… .
I am creating a select list which is populated with enum values:
<%= Html.DropDownListFor(model => model.OrderStatus, new SelectList(Enum.GetValues(typeof(OrderStatus))))%>
I am now performing client-side validation to ensure that OrderStatus is set properly:
[DisplayName("Order Status"), EnsureOrderStatus("ID")]
public OrderStatus OrderStatus { get; set; }
public class EnsureOrderStatus : ValidationAttribute, IClientValidatable
{
private readonly string OrderIDPropertyName;
public EnsureOrderStatus(string orderIDPropertyName)
{
OrderIDPropertyName = orderIDPropertyName;
}
protected override ValidationResult IsValid (object value, ValidationContext validationContext)
{
ValidationResult validationResult = ValidationResult.Success;
var propertyTestedInfo = validationContext.ObjectType.GetProperty(OrderIDPropertyName);
if (propertyTestedInfo == null)
{
validationResult = new ValidationResult(string.Format("Unknown property {0}", OrderIDPropertyName));
}
else
{
int orderID = (int)propertyTestedInfo.GetValue(validationContext.ObjectInstance, null);
OrderStatus orderStatus = (OrderStatus)value;
if (orderID == 0 && orderStatus != OrderStatus.Future)
{
validationResult = new ValidationResult(string.Format("Order must have have an Order Status of Future when being created."));
}
}
return validationResult;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessage,
ValidationType = "ensureorderstatus"
};
rule.ValidationParameters["orderid"] = OrderIDPropertyName;
yield return rule;
}
}
The value of 'value' inside of IsValid is always the first enum value of OrderStatus. I was wondering if there was other steps which need to be taken to ensure that the selected value of a DropDownList element updates properly.
UPDATED CODE:
//OrderDetailsModel:
[DisplayName("Order Status"), EnsureOrderStatus("ID")]
public OrderStatus OrderStatus { get; set; }
public List<OrderStatusModel> OrderStatusModels = new List<OrderStatusModel>();
public OrderDetailsModel()
{
OrderStatusModels.Add(new OrderStatusModel { EnumStatus = OrderStatus.Active, StringStatus = "Active" });
OrderStatusModels.Add(new OrderStatusModel { EnumStatus = OrderStatus.Completed, StringStatus = "Completed" });
OrderStatusModels.Add(new OrderStatusModel { EnumStatus = OrderStatus.Future, StringStatus = "Future" });
}
<%= Html.DropDownListFor(model => model.OrderStatus, new SelectList(Model.OrderStatusModels, "StringStatus", "EnumStatus"))%>
I would rethink this.. personally. Trying to hack an enum into a SelectList will create more work than is required.
Simply, wrap this all in a ViewModel.. consider this:
public enum OrderStatus {
NOT_SHIPPED,
SHIPPED
}
public class ViewModel {
public OrderStatus SelectedStatus { get; set; }
public List<StatusViewModel> Models = new List<StatusViewModel>();
}
public class StatusViewModel {
public string StringStatus { get; set; }
public OrderStatus EnumStatus { get; set; }
}
Setup (perhaps in the controller.. or somewhere else) involves this:
ViewModel model = new ViewModel();
model.Models.Add(new StatusViewModel() { EnumStatus = OrderStatus.NOT_SHIPPED, StringStatus = "Not shipped" });
model.Models.Add(new StatusViewModel() { EnumStatus = OrderStatus.SHIPPED, StringStatus = "Shipped" });
return View(model);
..and your view is simply this:
#model Models.ViewModel
#Html.DropDownListFor(x => x.SelectedStatus, new SelectList(Model.Models, "StringStatus", "EnumStatus"))
Then, when your model comes in after being posted, the SelectedStatus is strongly typed OrderStatus enum:
public ActionResult Index(ViewModel model) {
// model.SelectedStatus is an OrderStatus
}