Populating complex objects from view, on post, using interfaces - asp.net-mvc

I'm trying to post data from a bunch of text fields using the following:
Controller:
[HttpGet]
public ActionResult Order()
{
OrderViewModel vm = new OrderViewModel();
vm.Id = "some id";
List<IOrderItem> itemList= new List<IOrderItem>();
for (int i = 0; i <= 10; i++)
{
OrderItem x = new OrderItem();
x.ItemId = i + "";
itemList.Add(x);
}
vm.OrderItemList = itemList;
return View(vm);
}
[HttpPost]
public IActionResult Order(OrderViewModel model)
{
return View("blabla");
}
These are the models:
public class OrderViewModel : B.IOrderItemViewModel
{
private List<IOrderItem> orderItems;
public List<IOrderItem> OrderItemList
{
get { return orderItems; }
set { orderItems = value; }
}
private string orderId;
public string Id
{
get { return orderId; }
set { orderId = value; }
}
}
public class OrderItem : IOrderItem
{
private string orderItemId;
public string ItemId
{
get { return orderItemId; }
set { orderItemId = value; }
}
private string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}
}
this is the view:
#model OrderViewModel
#using (Html.BeginForm("Order", "Home", FormMethod.Post))
{
#for (int i = 0; i < Model.OrderItemList.Count; i++)
{
#Html.HiddenFor(x => x.OrderItemList[i].ItemId)
#Html.TextBoxFor(x => x.OrderItemList[i].Description)
<br />
}
<input type="submit" value="submit" />
}
Here is the problem - The interfaces are in another project, let's call it B. I reference B in the project.json file for the main project, A. In B, I just defined the two interfaces the are inherited above.
If I do not use any interfaces, and I just use the objects
e.g. instead of :
List<IOrderItem> OrderItemList
I use :
List<OrderItem> OrderItemList
When I run the project, and hit the view, I see the textboxes. I fill in some data and hit submit. It goes to the controller as expected. If I put a breakpoint in the HttpPost actionresult method, and look at the model, I can see all the data I entered. Perfect.
If I use the code above, where I am inheriting from some interfaces, it does not work. The view loads, I enter in some data, I post, it hits the breakpoint, but the model is empty and it's all null.
Any ideas / help would be greatly appreciated!

You cant bind to interfaces. The process of model binding involves first initializing your model (internally the DefaultModelBinder uses Activator.CreateInstance()) , but you can't initialize an interface (how would it know which type to initialize), which is why
public List<OrderItem> OrderItemList { get; set; }
works, but
public List<IOrderItem> OrderItemList { get; set; }
wont.
This article discusses it more detail and includes a section on creating a custom Abstract Model Binder that may solve your problem.

Related

Radiobuttonfor default all unchecked

I have a model as below
public class TestModel
{
public string SE { get; set; }
}
Then i have an interface and implementing class, Businesslogic class as below.
public interface ITest
{
List<TestModel> SE();
}
public class Test : ITest
{
public List<TestModel> SE()
{
SEBLL B=new SEBLL();
List<TestModel> lstSeries = new List<TestModel>();
lstSeries = B.SE();
return lstSeries;
}
}
my Business logic class where i bind data from database or sometimes manually
public class SEBLL
{
public List<TestModel> SE()
{
List<TestModel> e = new List<TestModel>();
e.Add(new TestModel{ SE = "A" });
e.Add(new TestModel{ SE = "B" });
return e;
}
}
my controller
public ActionResult Index()
{
ITest s = new Test();
List<TestModel> lstSeries = s.SE();
return View(lstSeries);
}
Finally here is the stronly typed view.
#model IEnumerable<TestModel>
foreach (var item in Model)
{
<div>
<label>
#Html.RadioButtonFor(m => item.SE, item.SE, new { #id = "SE", #class = "radio" })
SE #item.series
</label>
</div>
}
I need both of the Radiobuttons to be unchecked intially.
I am bit new to MVC, radiobutton list is populated with 2 values, but always anyone value is being selected when view initially loads. i tried setting checked html property but that did not help.
please let me know where am i going wrong in the whole process.
Correct me with the process too if i am following wrong flow.

ASP.NET MVC Model Binding returns null when model type is inherited from CollectionBase

This is my model
public class MessageSetTypeCollection<T> : CollectionBase where T : MessageSetType, new()
{
public string Name { get; set; }
public string[] Tags { get; set; }
public MessageSetType this[int index]
{
get
{
return (MessageSetType)List[index];
}
}
public void Add(MessageSetType value)
{
List.Add(value);
}
}
This is my controller actions
public ActionResult TestAction()
{
MessageSetTypeCollection<MessageSetType> Model = new MessageSetTypeCollection<MessageSetType>();
Model.Add(new MessageSetType()
{
Alert = "test" // Alert is a public property of the MessageSetType class
});
Model.Add(new MessageSetType()
{
Alert = "test2"
});
return View(Model);
}
[HttpPost]
public void TestAction(MessageSetTypeCollection<MessageSetType> Model)
{
return;
}
In the view I've this code
#using (Html.BeginForm())
{
#Html.EditorFor(a => a[0].Alert)
#Html.EditorFor(a => a[1].Alert)
<input type="submit" value="OK" />
}
When I submit this form to the TestAction action, the inner list into the Model parameter has a Count of 0 elements. Why?
I've also tested this code with List<MessageSetType> model type instead of MessageSetTypeCollection<MessageSetType> and all works correctly. Where is the error?
Please see here source code for List:
http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs
The implementation is pretty different. You don't have a collection of type MessageSetType on which your indexer should work.
I think you can use source code of to adjust your model: MessageSetTypeCollection.
I've resolved inheriting the MessageSetTypeCollection<T> from List<T> instead of CollectionBase
public class MessageSetTypeCollection<T> : List<T> where T : MessageSetType, new()
{
//Omissis
}

Return value from one model to another in same view?

Using a few answers here, I ended up using a Tuple<> with two Models for a single View. For simplicity, tuple.Item1 has a number known in the view, and I'm submitting info for tuple.Item2 to its controller. I want to send back the value of tuple.Item1.num in the submitted tuple.Item2, for the specific member tuple.Item2.num.
Currently, I have this for the message submission:
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "createMessage", #action = "/api/Message" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.Hidden("ID", #Model.Item2.ID)
<div class="editor-field">
#Html.TextArea("Text", #Model.Item2.Text)
#Html.ValidationMessageFor(tuple => tuple.Item2.Text)<br />
</div>
<input type="submit" value="Post Discussion" />
}
So, I'd like to have something sending the value of tuple.Item1.num within the Item2 (Message) Model posted to the Controller. How would I do this?
Mind you, I'm verrry new to the MVC and ASP.net frameworks, so I likely have some things mixed up. I understand that this HtmlHelper knows it's working with MessageController due to its #action attribute, but I'm still confused on how it's posting values to it. Any help would be great, thanks!
As per requested, my models:
DrugEntry.cs
namespace Project.Models
{
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using FileHelpers;
[DelimitedRecord(",")]
[Table("DRUGS")]
public class DrugEntry
{
private string ndc;
private string dosage;
private string brand;
private string generic;
private string currentStatus;
public DrugEntry()
{
this.ndc = string.Empty;
this.dosage = string.Empty;
this.brand = string.Empty;
this.generic = string.Empty;
this.currentStatus = "good"; // By default, a drug has no shortages associated with it, and thus is considered 'good'
}
public DrugEntry(string ndc, string dosage, string brand, string generic, string currentStatus)
{
this.ndc = ndc;
this.dosage = dosage;
this.brand = brand;
this.generic = generic;
this.currentStatus = currentStatus;
}
[Key]
[Column("NDC")]
public string NDC
{
get
{
return this.ndc;
}
set
{
this.ndc = value;
}
}
[Column("DOSAGE")]
public string Dosage
{
get
{
return this.dosage;
}
set
{
this.dosage = value;
}
}
[Column("BRAND_NAME")]
public string Brand
{
get
{
return this.brand;
}
set
{
this.brand = value;
}
}
[Column("GENERIC_NAME")]
public string Generic
{
get
{
return this.generic;
}
set
{
this.generic = value;
}
}
[Column("CURRENT_STATUS")]
public string CurrentStatus
{
get
{
return this.currentStatus;
}
set
{
this.currentStatus = value;
}
}
}
}
Message.cs
namespace Project.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
using FileHelpers;
[Table("MESSAGE")]
public class Message
{
private int id;
private int shortageID;
private string ndc;
private string user;
private DateTime date;
private string text;
[Key]
[Column("ID")]
public int ID
{
get
{
return this.id;
}
set
{
this.id = value;
}
}
[Column("SHORTAGE_ID")]
public int ShortageID
{
get
{
return this.shortageID;
}
set
{
this.shortageID = value;
}
}
[Column("NDC")]
public string NDC
{
get
{
return this.ndc;
}
set
{
this.ndc = value;
}
}
[Column("USER")]
public string User
{
get
{
return this.user;
}
set
{
this.user = value;
}
}
[Column("DATE")]
public DateTime Date
{
get
{
return this.date;
}
set
{
this.date = value;
}
}
[Column("TEXT")]
public string Text
{
get
{
return this.text;
}
set
{
this.text = value;
}
}
}
}
Use ViewModels
Instead of using a tuple, I would recommend to use a ViewModel. A ViewModel is just a simple class that you specifically create to meet the requirements of your view.
A ViewModel is an Asp Mvc standard, you don't modify a model just for the view, instead you create ViewModels.
What is ViewModel in MVC?
You would setup your view model like so.
// You typically name your ViewModel to the View
// that it represents.
public class MessageSubmission
{
public Message Message { get; set; }
public DrugEntry DrugEntry { get; set; }
}
ViewModels should be stored in their own folder. Create a folder called ViewModels and store them there. The following is a folder structure of an applicaton created by Microsoft, notice the ViewModels folder?
View
Since you are using weakly-typed html extensions, I would suggest the following.
#model MyMvcApplication1.ViewModels.MessageSubmission
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "createMessage", #action = "/api/Message" }))
{
#Html.ValidationSummary(true)
#Html.Hidden("ID", #Model.Message.ID)
<!-- // You can assign the DrugEntry.NDC, to a Message.NDC like this -->
#Html.Hidden("NDC", #Model.DrugEntry.NDC)
<div class="editor-field">
#Html.TextArea("Text", #Model.Message.Text)
#Html.ValidationMessageFor(model => model.Message.Text)<br />
</div>
<input type="submit" value="Post Discussion" />
}
Controller
Simply setup your controller like you normally would.
[HttpPost]
public ActionResult Message(Message message)
{
// Add your stuff here.
}
The MVC default model binder automatically assigns the values from the view page(ID,NDC,Text) to the waiting model in the controller (Message.ID, Message.NDC, Message.Text)
The binder aligns the fields by comparing the ID of the html controls to the properties of the model.
View | Model Properties
------------------------------
Hidden.ID | Message.ID
Hidden.NDC | Message.NDC
TextArea.Text | Message.Text
You can try this in ajax
#using (Ajax.BeginForm("ActionOfItem2", "ControllerOfItem2", new AjaxOptions { UpdateTargetId = "UpdateDiv"}))
{
#Html.Hidden("ID", #Model.Item2.ID)
<input type="submit" value="Post Discussion" />
}
Controller
public ReturnType ActionOfItem2(string ID)
{
// Use the ID here
}

Model property is empty

I am trying to move from webForms to Asp.net-MVC and have some problems. I am trying to figure why this is not working, I am getting this error: "Object reference not set to an instance of an object"
I have the class 'Pages':
namespace _2send.Model
{
public class Pages
{
public string PageContent { get; set; }
public string PageName { get; set; }
public int LanguageId { get; set; }
}
}
I am inserting the value to 'Pages.PageContent' property with this class:
namespace _2send.Model.Services
{
public class PagesService : IPagesService
{
public void GetFooterlinksPage()
{
DB_utilities db_util = new DB_utilities();
SqlDataReader dr;
Pages pages = new Pages();
using (dr = db_util.procSelect("[Pages_GetPageData]"))
{
if (dr.HasRows)
{
dr.Read();
pages.PageContent = (string)dr["PageContent"];
dr.Close();
}
}
}
The Controller method looks like this:
private IPagesService _pagesService;
public FooterLinksPageController(IPagesService pagesService)
{
_pagesService = pagesService;
}
public ActionResult GetFooterLinksPage()
{
_pagesService.GetFooterlinksPage();
return View();
}
I am trying to write the property in the view like this:
#model _2send.Model.Pages
<div>
#Model.PageContent;
</div>
When debugging, the method is fired and the dataReader is inserting the value to the 'PageContent' property, but I am still getting this error from the view.
Thanks!
return View();
You didn't pass a model.
You need to pass the model as a parameter to the View() method.
You need to rewrite service method to return Pages:
public Pages GetFooterlinksPage()
{
DB_utilities db_util = new DB_utilities();
Pages pages = new Pages();
using (var dr = db_util.procSelect("[Pages_GetPageData]"))
{
if (dr.HasRows)
{
dr.Read();
pages.PageContent = (string)dr["PageContent"];
return pages;
// Because you use using, you don't need to close datareader
}
}
}
And then rewrite your action method:
public ActionResult GetFooterLinksPage()
{
var viewmodel = _pagesService.GetFooterlinksPage();
return View(viewmodel);
}
You can return a model:
var viewmodel = new _2send.Model.Pages().
//here you configure your properties
return View(viewmodel);

How can use MVC DropDownlist

I have a problem with DropDownlist in MVC
I use ModelView in my application and this is my code
namespace MedicallexiconProject.ViewModel
{
public class WordViewModel
{
private readonly ICategoryService _categoryService;
public WordViewModel(ICategoryService categoryService)
{
_categoryService = categoryService;
var selectList = _categoryService.GetAllCategorysSelectList().
Select(x => new SelectListItem
{
Text = x.Name,
Value = x.ID.ToString()
}).ToList();
Categories = selectList;
}
public WordViewModel()
{
}
public string Name { get; set; }
private IList<SelectListItem> _categories;
public IList<SelectListItem> Categories
{
get
{
if (_categories == null)
{
_categories = new List<SelectListItem>();
}
return (_categories);
}
set { _categories = value; }
}
}
}
and this is my controller
[HttpGet]
public ActionResult Create()
{
var wordViewModel = new WordViewModel(_categoryService);
ViewBag.CategoryID = wordViewModel.Categories;
return View();
}
[HttpPost]
public ActionResult Create(WordViewModel wordViewModel)
{
Mapper.CreateMap<WordViewModel, Word>();
var word = new Word();
Mapper.Map(wordViewModel, word);
if (ModelState.IsValid)
{
_wordService.AddNewWord(word);
_uow.SaveChanges();
return RedirectToAction("Index");
}
return View(wordViewModel);
}
Now how can I insert dropdownlist in my View?
As AlfalfaStrange mentioned, you should not add logic in your ViewModel. That makes it ugly ! Keep your ViewModel simple POCO.
Add one more property in your ViewModel called "SelectedCategoryID" like this
public class WordViewModel
{
public int SelectedCategoryID { set;get;}
public IList<SelectListItem> Categories { set;get;}
public string Name { set;get;}
}
Initialize your Items (Categories) of your ViewModel in your GET method. Here i am calling a method called GetCategories which returns a list of categories.I can simply call the method wherever i want.
public ActionResult Create()
{
var model=new WordViewModel();
model.Categories=YourService.GetCategories();
return View(model);
}
In your strongly typed Create view , use this
#model WordViewModel
using(#Html.BeginForm())
{
#Html.DropDownFor(x=>x.SelectedCategoryID,
new SelectList(Model.Categories,"Value","Text"),"Select Category")
<input type="submit" value="Save" />
}
In your HttpPost action method , you can check for wordViewModel.SelectedCategoryID for the selected value.
[HttpPost]
public ActionResult Create(WordViewModel wordViewModel)
{
if(ModelState.IsValid)
{
//Checck for wordViewModel.SelectedCategoryID here now
}
//some validation failed. Let's reload the category data again.
wordViewModel.Categories=YourService.GetCategories();
return View(wordViewModel);
}
It's absolutely fine to include code that loads a dropdown list in your view model. A select list and a drop down are both "view" items.... they are not related to business logic and your controller and model need not know anything about SelectLists or SelectListItems or DropDownList, etc.

Resources