Visual studio doesn't understand method InitializeViewBag("Simple list") from example below.
How does it make? Does this method exist?
Why in this example, it is used : Example
Model
public class SimpleListModel
{
public string ItemToAdd { get; set; }
public List<string> Items { get; set; }
public void AddItem()
{
Items.Add(ItemToAdd);
ItemToAdd = "";
}
}
Razor
#using PerpetuumSoft.Knockout
#model KnockoutMvcDemo.Models.SimpleListModel
#{
var ko = Html.CreateKnockoutContext();
}
#using (ko.Html.Form("AddItem", "SimpleList", null, new { id = "myform" }))
{
<span>New item:</span>
#ko.Html.TextBox(m => m.ItemToAdd).ValueUpdate(KnockoutValueUpdateKind.AfterKeyDow n)
<button type="submit" #ko.Bind.Enable(m => m.ItemToAdd.Length >
}
Controller
public class SimpleListController : BaseController
{
public ActionResult Index()
{
InitializeViewBag("Simple list");
var model = new SimpleListModel { Items = new List<string> { "Alpha", "Beta", "Gamma" } };
return View(model);
}
public ActionResult AddItem(SimpleListModel model)
{
model.AddItem();
return Json(model);
}
}
Looking at the source code for BaseController in github, I can see that InitializeViewBag method is defined there. That's how SimpleListController has access to it. If you have that same exact implementation of BaseController and the method is still not recognised, I would think it's something annoying, like an extra bracket here or there, or a duplicate BaseController somewhere else.
Related
ViewModel:
public class GroepModel
{
public int id { get; set; }
public String Naam { get; set; }
public String Beschrijving { get; set; }
}
Controller:
public class GroepController : Controller
{
AlinaDatabaseDataContext db = new AlinaDatabaseDataContext();
// GET: Groep
public ActionResult Groepen()
{
List<GroepModel> groepen = Mapper.Map<List<GroepenWerkvorm>, List<GroepModel>>(db.GroepenWerkvorms.ToList());
return View(groepen);
}
}
View
#model alina1617.Models.GroepModel
#{
ViewBag.Title = "Groepen";
}
<h2>Groepen</h2>
<div>
#Html.DropDownListFor(model => model. //This is not working )
</div>
I've looked around and a lot of the things I'm finding solve this using ViewBags, but isn't it suboptimal to use them? So what would be the best approach to get a dropdownlist using a model class with data from a database?
first you need to add an SelectList to your viewModel :
public class MyViewModel {
public SelectList list {get;set;}
public int selectedItem {get;set;}
}
then you need to add your list to the SelectList :
public class GroepController : Controller
{
AlinaDatabaseDataContext db = new AlinaDatabaseDataContext();
// GET: Groep
public ActionResult Groepen()
{
List<GroepModel> groepen = Mapper.Map<List<GroepenWerkvorm>, List<GroepModel>>(db.GroepenWerkvorms.ToList());
var model = new MyViewModel();
model.list = new SelectList(groepen, "id", "Naam");
return View(model);
}
}
and in the view :
#model alina1617.Models.MyViewModel
#{
ViewBag.Title = "Groepen";
}
<h2>Groepen</h2>
<div>
#Html.DropDownListFor(model => model.selectedItem, Model.list )
</div>
I'm working with MVC 5 for a few months read a lot of articles, forums and documentation but always wondering what is better in the view;
1) binding data using static method of model like here
2) binding the same data using ViewData[index] which is set in Controller that with previous example will look like this
#Html.DropDownListFor(n => n.MyColorId, ViewData[index])
You want to use option 1, mainly because you want to use Strongly Type as much as possible, and fix the error at compile time.
In contrast, ViewData and ViewBag are dynamic, and compile could not catch error until run-time.
Here is the sample code I used in many applications -
Model
public class SampleModel
{
public string SelectedColorId { get; set; }
public IList<SelectListItem> AvailableColors { get; set; }
public SampleModel()
{
AvailableColors = new List<SelectListItem>();
}
}
View
#model DemoMvc.Models.SampleModel
#using (Html.BeginForm("Index", "Home"))
{
#Html.DropDownListFor(m => m.SelectedColorId, Model.AvailableColors)
<input type="submit" value="Submit"/>
}
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new SampleModel
{
AvailableColors = GetColorListItems()
};
return View(model);
}
[HttpPost]
public ActionResult Index(SampleModel model)
{
if (ModelState.IsValid)
{
var colorId = model.SelectedColorId;
return View("Success");
}
// If we got this far, something failed, redisplay form
// ** IMPORTANT : Fill AvailableColors again; otherwise, DropDownList will be blank. **
model.AvailableColors = GetColorListItems();
return View(model);
}
private IList<SelectListItem> GetColorListItems()
{
// This could be from database.
return new List<SelectListItem>
{
new SelectListItem {Text = "Orange", Value = "1"},
new SelectListItem {Text = "Red", Value = "2"}
};
}
}
I would say, completely separate dropdown items from ViewData. Have your model contain a property for dropdown. Fill that in your controller and just bind it in the view like
ViewModel
class MyModel
{
public IEnumerable<SelectListItem> dropdowndata {get; set;}
}
Controller
public Actionresult MyAction(string id)
{
IEnumerable<data> mydata = callDALmethodtogetit();
Mymodel model = new MyModel
{
dropdowndata = mydata.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
});
}
}
View
#Html.DropDownListFor(model => model.dropdowndata, Model.dropdowndata)
First post here, so i'm sorry if this post is not very good!
I am currently building a web application using asp.net, and ran in to this problem:
The model item passed into the dictionary is of type 'Daily_Planner.Models.DayPlanPartialview', but this dictionary requires a model item of type 'Daily_Planner.Models.Group'
It seems like I am having a problem with passing on the right model. I am using several partial views and I am afraid that I may be using my models wrong, however I can not find out what it is. I have tried giving some adequate information, however if you need more I will be more than willing to post more of the code.
Partial view _GroupManage:
#model IEnumerable<Daily_Planner.Models.Group>
<select>
#foreach (var item in Model)
{
<option value="#item.Id">#item.GroupName</option>
}
</select>
Partial view _layout:
#if (Request.IsAuthenticated)
{
#Html.ActionLink("Daily Planner", "Index", "Home", new { area = "" }, new { #class = "navbar-brand" })
}
else
{
#Html.ActionLink("Daily Planner", "Login", "Account", new { area = "" }, new { #class = "navbar-brand" })
}
#if (Request.IsAuthenticated)
{
{ Html.RenderPartial("_GroupManage"); }
#Html.Partial("_LoginPartial")
}
Model:
namespace Daily_Planner.Models
{
public class DayPlanPartialview
{
public DayPlan displayComments { get; set; }
public Group groupM { get; set; }
public IEnumerable<Daily_Planner.Models.DayPlan> ListofComments { get; set;}
public IEnumerable<Daily_Planner.Models.Group> groupmanage{ get; set; }
}
HomeController
namespace Daily_Planner.Controllers
{
public class HomeController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
private DayPlanPartialview pv = new DayPlanPartialview();
[Authorize]
public ActionResult Index()
{
// Add list of comments with newest comment first // .Take(10).ToList(); will only show the last 10 posts
pv.ListofComments = db.DayPlans.OrderByDescending(x => x.DateCreated).ToList();
return View(pv);
}
[HttpPost]
[Authorize]
public ActionResult Index(DayPlanPartialview dayP)
{
dayP.displayComments.ApplicationUserId = User.Identity.GetUserId();
dayP.displayComments.DateCreated = DateTime.Now;
db.DayPlans.Add(dayP.displayComments);
db.SaveChanges();
pv.ListofComments = db.DayPlans.ToList();
return RedirectToAction("");
}
#model IEnumerable<Daily_Planner.Models.Group>
<select>
#foreach (var item in Model)
{
<option value="#item.Id">#item.GroupName</option>
}
</select>
Lets say I have two classes one base class:
public class BaseModel {
}
And a couple of children:
public class FooModel : BaseModel {
}
public class BarModel : BaseModel {
}
Now my view I would like to expect a model like this:
#model IEnumerable<BaseModel>
And in my action I would pass in child classes along the lines of:
return View(new List<BaseModel>(){ new FooModel(), new BarModel() })
I would then edit these on one page, using EditFor (This works fine)
The problem is when I post back I would like to be able to cast these types to there implementation types, but this doesn't work. If I try and cast it, it is null or an exception is thrown.
[HttpPost]
public ActionResult BaseModelUpdate(IList<BaseModel > model)
{
// I would like to access items in the list as FooModel and BarModel
}
How can I achieve this? (getting the items in the list back to their child class types?)
I was thinking I could try and use TryUpdateModel?
Thanks for your help.
You need to specify the index of the item in the collection.
Here is the controller code:
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return
View(new List<BaseModel>() { new BarModel() { BaseProp = "Bar" }, new FooModel() { BaseProp = "Foo" } });
}
[HttpPost]
public ActionResult Index(IList<BaseModel> model)
{
return this.View(model);
}
}
As You can see, there is nothing special about it. The magic is in the view:
#using MvcApplication1.Models
#model IList<MvcApplication1.Models.BaseModel>
#{
ViewBag.Title = "title";
//Layout = "_Layout";
}
<h2>title</h2>
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(p => p[i])
}
<input type="submit" value="Save" />
}
As you can see, the expression passed to EditorFor contains the index of the current item in the collection. Why this is required is explained here. In brief, EditorFor returns an input element for each property whose name attribute contains the index of the item within the collection, e.g.
<input class="text-box single-line" name="[0].BaseProp" type="text" value="Bar" />
UPDATE
If you are trying to preserve the type of the objects you will need to have a special property in the model which will store the specific model type and a custom IModelBinder implementation which will create the specific model instance based on that property.
Bellow are the model classes. The Type property will render as a hidden input:
namespace MvcApplication1.Models
{
using System.Web.Mvc;
public class BaseModel
{
public string BaseProp { get; set; }
[HiddenInput(DisplayValue = false)]
public virtual string Type
{
get
{
return _type ?? this.GetType().FullName;
}
set
{
_type = value;
}
}
private string _type;
}
public class FooModel : BaseModel
{
public string FooProp { get; set; }
}
public class BarModel :BaseModel
{
public string BarProp { get; set; }
}
}
This is an example implementation of the custom model binder:
public class BaseModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// call to get the BaseModel data so we can access the Type property
var obj = base.BindModel(controllerContext, bindingContext);
var bm = obj as BaseModel;
if(bm != null)
{
//call base.BindModel again but this time with a new
// binding context based on the spefiic model type
obj = base.BindModel(
controllerContext,
new ModelBindingContext(bindingContext)
{
ModelMetadata =
ModelMetadataProviders.Current.GetMetadataForType(null, Type.GetType(bm.Type)),
ModelName = bindingContext.ModelName
});
}
return obj;
}
}
You need to register the custom binder on application_start:
ModelBinders.Binders.Add(typeof(BaseModel), new BaseModelBinder());
Is it possible to have a single view model with a list that is used for a dropdownlist and also get the selected value of the dropdownlist from the view model when I post a form?
If so, how can I do this?
Sure, as always start by defining your view model:
public class MyViewModel
{
public int? SelectedItemValue { get; set; }
public IEnumerable<Item> Items { get; set; }
}
public class Item
{
public int? Value { get; set; }
public string Text { get; set; }
}
then the controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
// TODO: Fill the view model with data from
// a repository
Items = Enumerable
.Range(1, 5)
.Select(i => new Item
{
Value = i,
Text = "item " + i
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// TODO: based on the value of model.SelectedItemValue
// you could perform some action here
return RedirectToAction("Index");
}
}
and finally the strongly typed view:
<% using (Html.BeginForm()) { %>
<%= Html.DropDownListFor(
x => x.SelectedItemValue,
new SelectList(Model.Items, "Value", "Text")
) %>
<input type="submit" value="OK" />
<% } %>