The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[MvcImport.Models.CourseImport]', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'.
CASE 3
1.
In ImportController I do
return PartialView("CoursesList", courses);
2.
I'm now using partial view which it similar to the view in the above points and also is strongly typed eg.
@model IEnumerable<MvcImport.Models.CourseImport>
3.
In this case the data is displayed but without my umbraco template shape, no styling etc.
If in the partial view I include
@{
Layout = "~/Views/Master.cshtml";
}
I get the same error as in Case 2
Can someone advise me how to do it? I guess there must be a solution but I am very new to MVC so I don't get how things work yet too much.
There are a few ways to achieve what you are trying to do but I think the simplest way for you would be to stick with Case 3 where you return a partial from the controller but rather than having the model as IEnumerable<MvcImport.Models.CourseImport> you can use the following:
foreach statement cannot operate on variables of type 'Umbraco.Web.Mvc.UmbracoViewPage<System.Collections.Generic.IEnumerable<MvcImport.Models.CourseImport>>' because 'Umbraco.Web.Mvc.UmbracoViewPage<System.Collections.Generic.IEnumerable<MvcImport.Models.CourseImport>>' does not contain a public definition for 'GetEnumerator'
This is my ImportController:
namespace MvcImport.Controllers
{
public class ImportController : SurfaceController
{
public ActionResult Import()
{
return View(new ImportModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ImportExcel(ImportModel model)
{
if (model.FileUpload != null && model.FileUpload.ContentLength > 0)
{
try
{
string uploadDir = MvcImport.Settings.UploadPath;
string origFileName = model.FileUpload.FileName;
string extension = origFileName.Substring(origFileName.LastIndexOf('.') + 1);
string pathToCheck = Path.Combine(Server.MapPath(uploadDir), origFileName);
// Check to see if a file already exists with the
// same name as the file to upload.
if (!System.IO.File.Exists(pathToCheck))
{
string companyId = MvcImport.Member.CompanyIdDummy;
string userId = MvcImport.Member.UserIdDummy;
string tempfileName = companyId.ToLower() + "-temp." + extension;
pathToCheck = Path.Combine(Server.MapPath(uploadDir), tempfileName);
model.FileUpload.SaveAs(pathToCheck);
var importFile = new ImportFile
{
CompanyId = companyId,
MemberId = userId,
Filename = tempfileName,
FilenameOriginal = origFileName,
FileType = extension,
AddressId = model.Location,
DateCreated = DateTime.UtcNow,
DeleteExisting = model.DeleteExisting
};
//db.Insert(importFile);
CreateImportNode(importFile);
ExcelObject excel = new ExcelObject(pathToCheck);
DataTable dt = excel.GetSchema();
string sheetName = dt.Rows[0]["TABLE_NAME"].ToString();
DataTable courseTable = excel.ReadTable(sheetName);
excel.Connection.Close();
if (courseTable != null)
{
List<CourseImport> courses = new List<CourseImport>();
int dataRowNumber = 0;
foreach (DataRow row in courseTable.Rows)
{
ViewData["categories"] = ConvertCategoryToList();
var course = new CourseImport
{
CompanyId = MvcImport.Member.CompanyIdDummy,
UserId = MvcImport.Member.UserIdDummy,
CategoryId = Convert.ToInt32(row["kategoria id"]),
TypeId = Convert.ToInt32(row["rodzaj id"]),
Modes = row["tryby"].ToString(),
AddressId = Convert.ToInt32(row["adres id"]),
Ref = row["Ref"].ToString(),
Title = row["tytul"].ToString(),
Tags = row["tagi"].ToString(),
Description = row["program"].ToString(),
Level = row["poziom"].ToString(),
Students = row["uczestnicy"].ToString(),
AdmissionInfo = row["warunki przyjecia"].ToString(),
Diploma = row["dyplom"].ToString(),
AdmissionFee = Convert.ToSingle(row["wpisowe"]),
Fee = Convert.ToSingle(row["koszt"]),
FeeNet = Convert.ToSingle(row["netto"]),
FeeForID = Convert.ToInt32(row["koszt za"]),
FeeOther = row["koszt inne"].ToString(),
Other = row["inne informacje"].ToString(),
PayOn = Convert.ToBoolean(row["plac z edu"]),
Discount = Convert.ToInt32(row["rabat"]),
Status = Convert.ToInt32(row["status"])
};
courses.Add(course);
dataRowNumber++;
}
return PartialView("CoursesList", courses);
}
}
TempData.Add("Success", true);
}
catch (Exception ex)
{
return CurrentUmbracoPage();
}
}
//redirect to current page to clear the form
return RedirectToCurrentUmbracoPage();
}
}
}
Thanks for the extra information, its a bit clearer how you have everything hooked up now and I don't think you are too far away from solving the issue. There is a bit more work to do in order to set this up so I'll go over those first. To start with you are going to need a new viewmodel class which will look like this
public class CourseImportViewModel : RenderModel {
public CourseImportViewModel() : this(new UmbracoHelper(UmbracoContext.Current).TypedContent(UmbracoContext.Current.PageId)) { }
public CourseImportViewModel(IPublishedContent content, CultureInfo culture) : base(content, culture) { }
public CourseImportViewModel(IPublishedContent content) : base(content) { }
public IEnumerable<CourseImport> Courses { get; set; }
}
The key part of this is that it inherits from the RenderModel class, this means it can contain all the Umbraco page data and can be passed into a partial view while allowing the partial to have a layout. The three constructors allow you to set the content while the IEnumerable<CourseImport> Courses property will contain your data and will be populated in your controller in the same way it currently is.
Your controller will return the partial in the same way as it does now but will now pass the view model and not just a list of your courses.
return PartialView("CoursesList", new CourseImportViewModel { Courses = courses });
Your partial will have to be slightly modified to accept this new view model, notice it uses inherits and not model and now has to use Model.Courses in the for loop
That's the case - I don't think it sees the controller at all.
The debugger doesn't stop at the breakpoints.
Also when I put simply
ViewBag.Message = "test";
in the
ActionResult Import()
and put it in the Import.cshtml view - nothing happens (ViewBag.Message value doesn't get displayed)
I'm not using Html.Action
I want the controller to perform ActionResult Import() on the page load eg. if the session is not present or empty I get upload form displayed (I pasted Import.cshtml code at the begining of my post) - the bit in
I think there is a little bit of a mix up between pure MVC and the default Umbraco way of working with templates, its a little bit different so its easy to get caught out. The best way forward is to replace this line in your Import.cshtml file
This will call the action direct from the view which is what you want in this case. You will also need to adjust your controller slightly to make it return the correct partial view, we wont be returning a full view from the action as you originally had it set up. This would work in pure MVC but not in this case where you are on an Umbraco page.
[ChildActionOnly]
public ActionResult Import()
{
ViewData["categories"] = ConvertCategoryToList();
if (Session["COURSESIMPORT"] != null)
{
List<MvcImport.Models.CourseImport> courses = (List<MvcImport.Models.CourseImport>)Session["COURSESIMPORT"];
return PartialView("CoursesList", new CourseImportViewModel { Courses = courses });
}
else
{
var locations = new List<SelectListItem>
{
new SelectListItem {Selected = false, Text = "location 1", Value = ""},
};
var fileTypes = new List<SelectListItem>
{
new SelectListItem {Selected = false, Text = "type 1", Value = ""},
};
return Partial("Upload", new MvcImport.Models.ImportModel { FileTypes = fileTypes, Locations = locations })
}
}
The attribute [ChildActionOnly] means this action can not be accessed via a url such as domain.com/Import which is what you want in this case as you are accessing it via an Umbraco page. This also means you can take most of the cs code out of your view file as its now in the controller.
Did you try removing the Layout = "~/Views/Master.cshtml"; from the Upload partial view? Since you are now calling the action from the View it will simply write out the result of the action at the location in the View where you put @Html.Action
System.InvalidOperationException: The model item passed into the dictionary is of type 'MvcImport.Models.ImportModel', but this dictionary requires a model item of type 'Umbraco.Web.Models.RenderModel'.
You should be able to make the ImportModel inherit from the Render model, the same as you do for the CourseImportViewModel is should look something like:
public class ImportModel: RenderModel {
public ImportModel() : this(new UmbracoHelper(UmbracoContext.Current).TypedContent(UmbracoContext.Current.PageId)) { }
public ImportModel(IPublishedContent content, CultureInfo culture) : base(content, culture) { }
public ImportModel(IPublishedContent content) : base(content) { }
//the other properties you need to pass to the view
}
UmbracoTemplatePage and strongly typed view not working together
How to list custom data on the page and put it in the shape of umbraco template?
I use both strongly typed view and umbraco template in one view but I get errors as I couldn't use this combination.
Here are details:
I have a script importing excel data. My website is in umbraco 7. I use Import.cshtml view which calls upload.cshtml partial
Upload.cshtml partial does the job in ImportController. The data are there inserted into nicely into
All I want to do with it now is to display them in a view or partial view. I tried several things:
CASE 1
1.
in ImportController I do:
2.
The view includes a table which displays the rows and starts with
3.
In this case I get:
CASE 2
1.
As 1. in Case 1.
2.
The view starts with (so without @inherits UmbracoTemplatePage)
3.
I get:
CASE 3
1.
In ImportController I do
2.
I'm now using partial view which it similar to the view in the above points and also is strongly typed eg.
3.
In this case the data is displayed but without my umbraco template shape, no styling etc.
If in the partial view I include
I get the same error as in Case 2
Can someone advise me how to do it? I guess there must be a solution but I am very new to MVC so I don't get how things work yet too much.
Thanks.
Hi Eva,
There are a few ways to achieve what you are trying to do but I think the simplest way for you would be to stick with Case 3 where you return a partial from the controller but rather than having the model as
IEnumerable<MvcImport.Models.CourseImport>
you can use the following:This should give you access to the Umbraco helper in the partial and allow you to access page properties in the same way you normally would in a view.
You don't say how you are calling the action in the ImportController so you might need to a make a few more changes to the code in the View to get this working how you want. There's a bit more reading on the topic here which might help a bi: http://our.umbraco.org/documentation/Reference/Mvc/partial-views#StronglytypedPartialViews
Hope this get you a bit closer to solving the problem
Thanks Paul,
I have tried that but I'm getting
This is my ImportController:
and my partial view:
Hi Eva,
Thanks for the extra information, its a bit clearer how you have everything hooked up now and I don't think you are too far away from solving the issue. There is a bit more work to do in order to set this up so I'll go over those first. To start with you are going to need a new viewmodel class which will look like this
The key part of this is that it inherits from the RenderModel class, this means it can contain all the Umbraco page data and can be passed into a partial view while allowing the partial to have a layout. The three constructors allow you to set the content while the
IEnumerable<CourseImport> Courses
property will contain your data and will be populated in your controller in the same way it currently is.Your controller will return the partial in the same way as it does now but will now pass the view model and not just a list of your courses.
Your partial will have to be slightly modified to accept this new view model, notice it uses inherits and not model and now has to use Model.Courses in the for loop
Fabulous, thanks Paul. It works nicely now.
However I have trouble to get courses back from a session variable I'm setting at the end of my import.
So in the ImportController I added
Now I replaced Import() function with:
but my view does not seem to see this at all. So it returns a standard page with form included in the view but it doesn't see anything in
Not sure I'm calling this bit properly.
Hi Eva,
Can you put a break point on the Import action to see what is in the session variable at that point and also double check the code is being hit?
Are you calling this action directly from the template in Umbraco using Html.Action?
That's the case - I don't think it sees the controller at all. The debugger doesn't stop at the breakpoints.
Also when I put simply
in the
and put it in the Import.cshtml view - nothing happens (ViewBag.Message value doesn't get displayed)
I'm not using Html.Action
I want the controller to perform ActionResult Import() on the page load eg. if the session is not present or empty I get upload form displayed (I pasted Import.cshtml code at the begining of my post) - the bit in
If it has value it displays the course list as per our previous discussion.
Instead I get Upload form displayed each time and ActionResult Import() seems to be ignored (eg. ViewBag.Error).
Am I not doing it correctly?
I think there is a little bit of a mix up between pure MVC and the default Umbraco way of working with templates, its a little bit different so its easy to get caught out. The best way forward is to replace this line in your Import.cshtml file
With this
This will call the action direct from the view which is what you want in this case. You will also need to adjust your controller slightly to make it return the correct partial view, we wont be returning a full view from the action as you originally had it set up. This would work in pure MVC but not in this case where you are on an Umbraco page.
The attribute
[ChildActionOnly]
means this action can not be accessed via a url such as domain.com/Import which is what you want in this case as you are accessing it via an Umbraco page. This also means you can take most of the cs code out of your view file as its now in the controller.Working almost great, thanks Paul!
The values uploaded from excel now display well and also I can now load the courses from the session on the new page load.
The only small problem now is on new page load.
The CourseList partial view includes
so my list of data is shaped in the template on calling partial view after posting:
However when I access page without posting eg. when the first condition of Import() action happens
the page content is inside the master template which is nested in the same master template again. If I remove
from the view, the content is not wrapped in the template when displaying the data after posting.
Did you try removing the
Layout = "~/Views/Master.cshtml";
from the Upload partial view? Since you are now calling the action from the View it will simply write out the result of the action at the location in the View where you put@Html.Action
If I do that it fails on line
saying
You should be able to make the ImportModel inherit from the Render model, the same as you do for the CourseImportViewModel is should look something like:
Paul, thank you! I would never resolve most things on my own.
I still need to learn a lot but that was a big step.
is working on a reply...