Foreach through IEnumerable in view - asp.net-mvc-3

can someone help please, dont know what I am doing wrong:
my viewmodel:
public class ScormModuleViewModel
{
public IEnumerable<ScormModuleInfo> ScormModuleInfo { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
public double Total { get; set; }
public int RecordsPerPage { get; set; }
}
then my controller:
public ActionResult Index(int? page)
{
List<ScormModuleInfo> modules = new List<ScormModuleInfo>();
foreach (var directory in Directory.EnumerateDirectories(scormRootDir))
{
ScormModuleInfo module = new ScormModuleInfo();
module.ScormModule = ZincService.ScormService.GetScormModule(scormTitle, scormDirectory);
if (module.ScormModule != null)
module.Installed = true;
else
{
module.Installed = false;
module.ScormModule = new ScormModule();
module.ScormModule.Directory = scormDirectory;
module.ScormModule.Title = scormTitle;
}
module.ScormModule.EntryPointRef = scormEntryPointRef;
module.ScormModule.IdentifierRef = scormIdentifierRef;
module.ScormModule.LaunchHeight = scormLaunchHeight;
module.ScormModule.LaunchWidth = scormLaunchWidth;
module.ScormModule.LaunchResize = scormLaunchResize;
module.ScormModule.RelativeHtmlPath = scormRelativeHtmlPath;
module.ScormModule.SchemaVersion = scormSchemaVersion;
if (module.Installed)
{
// TODO: Save changes to module
//ZincService.ScormService.UpdateScormModuleSettings(
}
modules.Add(module);
noOfScormPackages++ ;
}
}
ScormModuleViewModel model = new ScormModuleViewModel();
model.ScormModuleInfo = modules;
model.CurrentPage = page.GetValueOrDefault(1);
model.PageSize = PageSizeSettings.ScormPackages;
model.Total = noOfScormPackages;
// Now iterate over modules and read imsmanifest.xml file for details of entry point and resources required.
return View(model);
}
and my view:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<Zinc.Web.Areas.Admin.ViewModels.Scorm.ScormModuleViewModel>>" %>
foreach (var module in Model)
{ %>
<li>
<div class="col1">
<a href='javascript:LoadTrainingModuleWithDedicatedAPIObj("<%= module.ScormModuleInfo.r .sc.ScormModule.RelativeHtmlPath %>/<%= module.ScormModule.EntryPointRef %>", "<%: module.ScormModule.Title %>", "<%: module.ScormModule.LaunchWidth %>", "<%: module.ScormModule.LaunchHeight %>");'>
<% if (module.ScormModule.Title.Length < 70)
{ %>
<%: module.ScormModule.Title%>
<% }
else
{ %>
<%: module.ScormModule.Title.Substring(0, 70)%>....
<% } %>
</a>
</div>
i get redlines under all the module. properties?
i dont understand, what am i doing wrong?
thanks

Model is your ViewModel. Model.ScormModuleInfo is the IEnumerable of ScormModuleInfo type.
foreach (var module in Model.ScormModuleInfo)
In your controller you are passing ScormModuleViewModel to view not List<ScormModuleViewModel>
So Replace
System.Web.Mvc.ViewPage<List<Zinc.Web.Areas.Admin.ViewModels.Scorm.ScormModuleViewModel>>
To:
System.Web.Mvc.ViewPage<Zinc.Web.Areas.Admin.ViewModels.Scorm.ScormModuleViewModel>

Related

views for select list contained within a viewmodel and validation

My Scenario :
My Model is "Item" which contain 0 or many number of taxes (Model "Tax")
"Item" --> has 0 or n --> "Tax"
I have a View Model for MVC named "VMItem" which has an object of "Item", all taxes ("TaxDic") and selected taxes ("Taxes"). MVC page is bind to this view model ("ViewItem").
Requirement :
I want the MVC to display a list box of all taxes and enable user to select relevant tax for each item when he create the item.
Below is my code,
Reference to the following codes, ListBox which is for Taxes is not mandatory field. But when I submitted the form with some selection on ListBox, it shows red box around the ListBox and if I select nothing on ListBox and submit, there is no error it seems.
please have a look at the following scenario.. if there is any better way to achieve this, please someone guide me.
model
public class Item
{
[Display(Name="Item ID")]
public virtual Guid ItemID { get; set; }
[Required]
[Display(Name = "Name")]
public virtual string Name { get; set; }
[Required]
[Display(Name="Price")]
public virtual decimal Price { get; set; }
public virtual IEnumerable<Tax> Taxes { get; set; }
}
View Model
public class VMItem
{
public Item Item { get; set; }
public IEnumerable<Tax> Taxes { get; set; }
public IEnumerable<SelectListItem> TaxDic { get; set; }
}
Function to generate Select list item in the controller class
private VMItem GenerateViewModel(Item Item)
{
IEnumerable<Tax> Taxes = TaxServices.FindAll();
IList<SelectListItem> taxDic = new List<SelectListItem>();
// Generating Taxes and taxDic here..
VMItem VmItem = new VMItem
{
Item = Item,
Taxes = Taxes,
TaxDic = taxDic
};
return VmItem;
}
Contorller Action
// GET: /Product/Add
public ActionResult Add()
{
return View(GenerateViewModel(new Item()));
}
[HttpPost]
public ActionResult Add(VMItem collection)
{
Item item = new Item();
try
{
if(ModelState.IsValid)
{
item = collection.Item;
var taxes = collection.Taxes;
return View(GenerateViewModel(collection.Item));
}
// else..
}
// catch...
}
Views
<% using (Html.BeginForm()) { %>
<!-- more codes for other fields -->
<%: Html.ListBoxFor(model => model.Taxes, Model.TaxDic) %>
<!-- submit button goes below -->
<% } %>
This looks like a many-to-many relationship so first check if your tax class looks like this:
public class Tax
{
public Guid TaxId { get; set;}
public virtual ICollection<Item> Items { get; set;}
}
Then inside your OnModelCreating function in the context class add this:
modelBuilder.Entity<Item>()
.HasMany(i => i.Taxes).WithMany(t => t.Items)
.Map(t => t.MapLeftKey("ItemId")
.MapRightKey("TaxId")
.ToTable("ItemsTaxes"));
ViewModel
public class ItemTaxViewModel
{
public Item item { get; set; }
public virtual ICollection<AssignedTaxes> Taxes { get; set; }
}
public class AssignedTaxes
{
public int TaxId { get; set; }
public bool Assigned { get; set; }
}
Controller Action
public ActionResult Create()
{
var newItemVM = new ItemTaxViewModel
{
Item = new Item(),
Taxes = PopulateTaxes()
};
return View(newItemVM);
}
[HttpPost]
public ActionResult Create(ItemTaxViewModel itemTaxViewModel)
{
if(ModelState.IsValid)
{
var item = new Item();
item = itemTaxViewModel.Item;
AddOrUpdateTaxes(item, itemTaxViewModel.Taxes);
context.Items.Add(item);
context.SaveChanges();
return RedirectToAction("Index");
}
return View(itemTaxViewModel);
}
Helper Methods
private List<AssignedTaxes> PopulateTaxes()
{
var taxes = context.Taxes;
var assignedTaxes = new List<AssignedTaxes>();
foreach(var tax in taxes)
{
assignedTaxes.Add(new AssignedTaxes
{
TaxId = tax.TaxId,
Assigned = false
});
}
return assignedTaxes;
}
private void AddOrUpdateTaxes(Item item, ICollection<AssignedTaxes> assignedTaxes)
{
foreach(var assignedTax in assignedTaxes)
{
if(assignedTax.Assigned)
{
item.Taxes.Add(context.Taxes.Single(t => t.TaxId == assignedTax.TaxId));
}
}
}
EditorTemplate under the /Views/Shared/EditorTemplates
#model AssignedTaxes
#using projectName.ViewModels
<fieldset>
#Html.HiddenFor(model => model.TaxId)
#Html.CheckBoxFor(model => model.Assigned)
</fieldset>
View
<div class="editor-field">
#Html.EditorFor(model => model.Taxes)
<div class="editor-field">
Seems likes a lot of work but it's one of the cleanest ways to implement many-to-many checkboxes that I've come up with after going through many tutorials.
#Luis, your way looks well but it is too complicated and at the end you ended it with checkbox. May be it is better way. I came up with a solution and it was simple enough to me. I post it below. Please give me a feedback on it whether I should keep going with it or need improvement.
Controller
IEnumerable<Tax> Taxes = _TaxServices.FindTax();
IList<SelectListItem> taxDic = new List<SelectListItem>();
foreach (Tax tax in Taxes)
{
SelectListItem item = new SelectListItem();
item.Value = tax.TaxID.ToString();
item.Text = tax.Name;
taxDic.Add(item);
}
VMItem VmItem = new VMItem
{
Item = Item,
TaxDic = taxDic,
};
// Adding selected taxes to the list
IList<int> SelectedTaxes = new List<int>();
if (Item.Taxes != null && Item.Taxes.Count > 0)
{
foreach (Tax tax in Item.Taxes)
{
SelectedTaxes.Add(tax.TaxID);
}
}
VmItem.SelectedTax = SelectedTaxes;
return view(VmItem);
View
<%: Html.ListBoxFor(model => model.SelectedTax, Model.TaxDic) %>
Controller [HttpPost]
item.Taxes = new Iesi.Collections.Generic.HashedSet<Tax>();
if (vmItem.SelectedTax.Count() > 0)
{
IEnumerable<int> SelectedTaxesIDs = vmItem.SelectedTax.ToList();
foreach (int n in SelectedTaxesIDs)
{
item.Taxes.Add(_TaxServices.FindTax(n));
}
}
Please give feedback on this way.
Thanks.

Object reference error trying to use RenderPartial

I have a main view using a ViewModel. Inside the ViewModel I do this (Edited to show complete ViewModel):
namespace MyNameSpace.ViewModels
{
public class MyViewModel
{
public ModelOne ModelOne { get; set; }
public ModelTwo ModelTwo { get; set; }
}
}
On my main view I do this (EDIT: Added #Html.Hidden code):
#using MyNameSpace.ViewModels
#using MyNameSpace.Models
#model MyViewModel
...
#using (Html.BeginFormAntiForgeryPost())
{
#Html.Hidden("myData", new MvcSerializer()
.Serialize(Model, SerializationMode.Signed))
....
<div>
#{Html.RenderPartial("_MyCheckBox",
Model.ModelTwo, new ViewDataDictionary());}
</div>
}
....
My partial view is:
#using MyNameSpace.Models
#model ModelTwo
<div>
<fieldset>
<div class="editor-label">
#Html.LabelFor(x => x.MyCheckBox)</div>
<div class="editor-field">
<select multiple="multiple" id="#Html.FieldIdFor(x =>
x.MyCheckBox)" name="#Html.FieldNameFor(x =>
x.MyCheckBox)">
#foreach (MyEnum item in Enum.GetValues(typeof(MyEnum)))
{
var selected = Model.MyCheckBox.Contains(item); //ERROR HERE
<option value="#((int)item)" #if (selected)
{<text>selected="selected"</text>}>
#T(item.ToString())
</option>
}
</select>
</div>
</fieldset>
</div>
I am getting the Object reference not set to an instance ... error and am not sure how to correct it.
Originally, I had that <fieldset> inside my main view and was getting that error. I thought it was because of the two models and that's why I placed it in a partial view. But only to discover I am running into the same problem.
I am not setting the MyCheckBox in my partial view on the line var selected = Model.MyCheckBox.Contains(item); properly.
Any thoughts?
EDIT (Adding MyCheckBox code)
Here is MyCheckBox inside ModelOne.cs:
[Mandatory(ErrorMessage = "Please select at least one option")]
[Display(Name = "Please select one ore more options")]
[MySelector]
public virtual int MyCheckBox { get; set; }
And here it is inside ModelTwo.cs:
private IList<MyEnum> _myEnum;
public IList<MyEnum> MyCheckBox
{
get
{
if (_myEnum== null)
{
_myEnum= new List<MyEnum>();
foreach (MyEnumitem in Enum.GetValues(typeof(MyEnum)))
{
if (((MyEnum)Record.MyCheckBox& item) == item)
_myEnum.Add(item);
}
}
return _myEnum;
}
set
{
_myEnum= value;
Record.MyCheckBox= 0;
foreach (var item in value)
{
Record.MyCheckBox|= (int)item;
}
}
}
Please note, I am using Orchard (hence the Record) which, in turn, uses NHibernate. I don't believe that is relevant, but I could be wrong. The MyCheckBox code is using [Flags] attribute of enum and storing the value as an int in the DB (hence the proxy). Here is what the enum would look like:
MyEnum.cs:
[Flags]
public enum MyEnum
{
[Display(Name="Name 1")]
Enum1 = 1,
[Display(Name="Name 2")]
Enum2 = 2,
[Display(Name="Name 3")]
Enum3 = 4,
[Display(Name="Name 4")]
Enum4 = 8,
[Display(Name="Name 5")]
Enum5 = 16
}
MyController.cs
private MyViewModel myData;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var serialized = Request.Form["myData"];
if (serialized != null) //Form was posted containing serialized data
{
myData= (MyViewModel)new MvcSerializer().Deserialize
(serialized, SerializationMode.Signed);
TryUpdateModel(myData);
}
else
myData= (MyViewModel)TempData["myData"] ?? new MyViewModel();
TempData.Keep();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.Result is RedirectToRouteResult)
TempData["myData"] = myData;
}
Then in the particular Action within *MyController.cs:
public ActionResult Step5(string backButton, string nextButton)
{
if (backButton != null)
return RedirectToAction("Step4");
else if ((nextButton != null) && ModelState.IsValid)
return RedirectToAction("Confirm");
else
return View(myData);
}

Display Template For Generics - View is not found

I have the following classes:
public class Widget
{
public string Name { get; set; }
}
GenericModel
public class GenericModel<T>
{
public List<T> Data { get; set; }
}
My Controller action is:
public ActionResult Simple()
{
var model = new GenericModel<Widget>()
{
Data = new List<Widget>
{
new Widget {Name = "a"}
}
};
return View(model);
}
And my view is:
#model MyApp.GenericModel<MyApp.Widget>
#{
ViewBag.Title = "Simple";
}
<h2>Simple</h2>
#Html.DisplayFor(m=>m)
I have a file called GenericModel.cshtml in Views/Shared/DisplayTemplate folder:
#model MyApp.GenericModel<MyApp.Widget>
<ul>
#for (int i = 0; i < Model.Data.Count; i++ )
{
<li>
#Html.EditorFor(m=> Model.Data[i].Name)
</li>
}
</ul>
This view can not be found. I see when I print out the name of the type of my model I get "GenericModel1". Seeing that, I renamed my template "GenericModel1.cshtml". This seems like a bit of a hack, is there an easier way to find this display template without resorting to this?
You have to set it in your viewstart:
#Code
Layout = "~/Views/Shared/DisplayTemplate.cshtml"
End Code
Note: The above is VB.
You can also pass it via your controller like this:
public ActionResult Simple()
{
var model = new GenericModel<Widget>()
{
Data = new List<Widget>
{
new Widget {Name = "a"}
}
};
return View("", "DisplayTemplate", model);
}

MVC3 / Partial Views / Multiple Paginated Lists

How to I render out two paginated lists on my Home Page?
I am willing to do what it takes - If it means tailoring my code to fit a solution or tailoring an existing solution to fit my code.
I have successfully rendered one paginated list to the home page using a partial view.
Take a look at my code:
Views
..Home > Index.cshtml
#foreach (var m in Model.First)
{
Html.RenderPartial("FirstSummary", m);
}
<div class="pager">
#Html.PageLinks(Model.PagingInfo, x => Url.Action("Index", new { page = x }))
</div>
Views
..Shared > FirstSummary.cshtml
#model MovinMyStuff.Domain.Entities.First
#{
if (#Model.IsActive)
{
<div class="first-list-item">
<ul>
<li>
<span class="first-name">
#Model.Property.ToString()
#Model.Property.ToString()
#Model.Property.ToString() -
#Model.Property.ToString()
#Model.Property.ToString()
#Model.Property.ToString()
</span>
</li>
<li>
#Html.ActionLink("Details", "Details", "First", new { area = "", id = #Model.FirstId }, new { #class = "button" })
</li>
</ul>
</div>
}
}
Controllers > HomeController.cs
public ViewResult Index(int page = 1)
{
FirstListViewModel viewModel = new FirstListViewModel
{
First = repository.First
.OrderByDescending(m => m.FirstId)
.Skip((page - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = repository.First.Count()
}
};
return View(viewModel);
}
Models > FirstListViewModel.cs
using System.Collections.Generic;
using MovinMyStuff.Domain.Entities;
namespace MovinMyStuff.WebUI.Models
{
public class FirstListViewModel
{
public IEnumerable<First> Firsts { get; set; }
public PagingInfo PagingInfo { get; set; }
}
}
ViewModels > FirstAndSecond.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MovinMyStuff.WebUI.Models;
namespace MovinMyStuff.WebUI.ViewModels
namespace MovinMyStuff.WebUI.ViewModels
{
public class MovesAndMovers
{
public MovesListViewModel movesList { get; set; }
public MoversListViewModel moversList { get; set; }
public MovesAndMovers()
{
movesList = new MovesListViewModel();
moversList = new MoversListViewModel();
}
}
}
I FIGURED IT OUT! This was quite a beast of a task...I have spent nearly 10 hours trying to solve this so please stay tuned as I WILL GIVE WHO EVER WANTS THIS the answer. Now all I have to do is prevent the refresh from sending me back to the top of the page. Yikes! That will hopefully not be so bad... ( :

MVC3 Display a dropdown list from one datasource and save to another datasource

I'm getting back to an MVC3 project after a 3 month hiatus. I need to display a drop down list that pulls from Database A, but saves to Database B. The property I need to persist is the NAICS/SIC code. Right now I just provide the user a text box to key in freeform text. So, I have the mechanics of that down. But instead it should provide only a valid list of codes from a source database.
The tricky thing to is I'm using a custom model binder to generate my ViewModels on the fly, so I don't have a distinct .cshtml file to customize.
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")]
public String BusinessTypeCode { get; set; }
Tricky ViewModel
#using Microsoft.Web.Mvc;
#using Tangible.Models;
#model Tangible.Models.WizardViewModel
#{
var currentStep = Model.Steps[Model.CurrentStepIndex];
var progress = ((Double)(Model.CurrentStepIndex) / Model.Steps.Count) * 100;
}
<script type="text/javascript">
$(function () {
$("#progressbar").progressbar({
value: #progress
});
});
</script>
<div id="progressbar" style="height:20px;">
<span style="position:absolute;line-height:1.2em; margin-left:10px;">Step #(Model.CurrentStepIndex + 1) out of #Model.Steps.Count</span>
</div>
#Html.ValidationSummary()
#using (Html.BeginForm())
{
#Html.Serialize("wizard", Model)
#Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
#Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<input type="submit" value="Previous" name="prev" />
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<input type="submit" value="Save & Continue" name="next" />
}
else
{
<input type="submit" value="Finish" name="finish" />
}
#*<input type="submit" value="Save" name="Save" />*#
}
Controller
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard, IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Upload", obj);
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
WizardViewModel
[Serializable]
public class WizardViewModel
{
public String AccountNumber { get; set; }
public int CurrentStepIndex { get; set; }
public Boolean IsInitialized { get { return _isInitialized; } }
public IList<IStepViewModel> Steps { get; set; }
private Boolean _isInitialized = false;
public void Initialize()
{
try
{
Steps = typeof(IStepViewModel)
.Assembly.GetTypes().Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t)).Select(t => (IStepViewModel)Activator.CreateInstance(t)).ToList();
_isInitialized = true;
//rewrite this. get the profile and wire them up or something.
this.AccountNumber = Tangible.Profiles.DR405Profile.CurrentUser.TangiblePropertyId;
}
catch (Exception e)
{
_isInitialized = false;
}
}
}
You can specify a template for a specific property on your view model by adding the UIHint attribute to the field. Since your view calls EditorFor on the model it will use the template you specified with UIHint.
BusinessTypeDropdown.ascx - (placed in Views/Shared/EditorTemplates
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<% var businessTypes = ViewData["businessTypes"] as IEnumerable<string>; %>
<%= Html.DropDownListFor(m => m , new SelectList(businessTypes, Model))%>
In your View Model
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")][UIHint("BusinessTypeDropdown")]
public String BusinessTypeCode { get; set; }
Then in your controller just set ViewData["businessTypes"] to your list of business types.
Without understanding your "tricky" view model code, it will be hard to make helpful suggestions.
However, there shouldn't be much problem here. You need to somehow create your dropdown list in yoru view, and populate it from data passed from your controller.
All the work happens in your controller. Populate your list or IEnumerable or whatever data source from your first database, then in your post handler save the selection it to your second database (the second part should not be much different from what you already have).

Resources