Nesing Pagemodel in ABP.io MVC - asp.net-mvc

I've got a page model that has an abp-tabs defined as
}
<abp-dynamic-form abp-model="Item" asp-page="/Anagrafiche/Clienti/EditModal">
<abp-modal size="Large">
<abp-modal-header title="#L["Update"].Value"></abp-modal-header>
<abp-modal-body>
<abp-tabs>
<abp-tab title="Generale">
<abp-form-content />
</abp-tab>
<abp-tab title="Scontistiche">
#{
await Html.RenderPartialAsync("_Scontistiche");
}
</abp-tab>
<abp-tab title="Clienti con stesso comune">
</abp-tab>
<abp-tab title="Clienti con stessa provincia">
Clienti con stessa provincia
</abp-tab>
</abp-tabs>
</abp-modal-body>
<abp-modal-footer buttons="#(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
I've defined the _Scontistiche.cshtml as
public class _ScontisticheModel : xxxPageModel
{
public _ScontisticheModel()
{
}
public void OnGet()
{
}
}
But when I run it I got
System.InvalidOperationException: 'The model item passed into the ViewDataDictionary is of type 'xxx.Web.Pages.Anagrafiche.Clienti.EditModalModel', but this ViewDataDictionary instance requires a model item of type 'xxx.Web.Pages.Anagrafiche.Clienti._ScontisticheModel'.'
What am I doing wrong?
Thanks

The error is saying you are passing the wrong model to your partialview. Looking at the code, this error is indeed correct. The following line is causing the issue.
#{
await Html.RenderPartialAsync("_Scontistiche");
}
By default the model of your parent page will be sent to the partial view, unless you define a different model. You can do this by passing a _ScontisticheModel model like so.
#{
await Html.RenderPartialAsync("_Scontistiche", Item);
}
Considering Item is the correct model you are referring to. If you don't have a model, you can also pass a new class like so:
await Html.RenderPartialAsync("_Scontistiche", new _ScontisticheModel());

Related

"Object Does not Contain definition for Obtained" ASP.Net MVC [duplicate]

can someone tell me what I'm doing wrong? :-)
I have this simple query:
var sample = from training in _db.Trainings
where training.InstructorID == 10
select new { Something = training.Instructor.UserName };
And I pass this to ViewBag.
ViewBag.Sample = sample;
Then I want to access it in my view like this:
#foreach (var item in ViewBag.Sample) {
#item.Something
}
And I get error message 'object' does not contain a definition for 'Something'. If I put there just #item, I get result { Something = SomeUserName }
Thanks for help.
This cannot be done. ViewBag is dynamic and the problem is that the anonymous type is generated as internal. I would recommend you using a view model:
public class Instructor
{
public string Name { get; set; }
}
and then:
public ActionResult Index()
{
var mdoel = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
return View(model);
}
and in the view:
#model IEnumerable<Instructor>
#foreach (var item in ViewBag.Sample) {
#item.Something
}
If you want to send in ViewData For example and don't want to send in model
you could use the same could as in the upper answer
and in the Controller
enter code here
ViewData[Instractor] = from training in _db.Trainings
where training.InstructorID == 10
select new Instructor {
Name = training.Instructor.UserName
};
and in the view you need to cast this to
`IEnumerable<Instructor>`
but to do this you should use
#model IEnumerable<Instructor>
Then you could do something like this
IEnumerable<instructors> Instructors =(IEnumerable<Instructor>)ViewData[Instractor];
then go with foreach
#foreach (var item in Instructors ) {
#item.Something
}

'object' does not contain a definition for 'CategoryName'

public ActionResult Index()
{
var groups = db.SHP_Products
.GroupBy(c => c.SHP_Category.Name,
(category, items) => new
{
CategoryName = category,
ItemCount = items.Count(),
Items = items
}
);
ViewBag.group = groups.ToList();
return View();
}
When running this it will show an error like this:
<ul>
#foreach (var m in ViewBag.group)
{
<h2>#m.CategoryName</h2>
PreviousNext
<li></li>
}
</ul>
'object' does not contain a definition for 'CategoryName'
You are passing a list of anonymous objects to the View.
Take a look at this answer Dynamic Anonymous type in Razor causes RuntimeBinderException
i think you are trying to access the <h2>#m.CategoryName</h2> directly, may be you can access it like #m.SHP_Category.Name i don't really know you the sequence on class in your code. try #m.
See this answer MVC Razor dynamic model, 'object' does not contain definition for 'PropertyName'
The reason for the error is that the dynamic type created by your GroupBy statement has an access level of "internal", which is not visible to the View. You can rectify by declaring a type or using an Explando - as discussed in this and other answers.
From
The reason for this is that the anonymous type being passed in the controller in internal, so it can only be accessed from within the assembly in which it’s declared. Since views get compiled separately, the dynamic binder complains that it can’t go over that assembly boundary.
One way to solve this is to use System.Dynamic.ExpandoObject.
public static ExpandoObject ToExpando(this object obj)
{
IDictionary<string, object> expandoObject = new ExpandoObject();
new RouteValueDictionary(obj).ForEach(o => expandoObject.Add(o.Key, o.Value));
return (ExpandoObject) expandoObject;
}
Then:
ToExpando(groups); // might need toList() it too.
Please use ViewData instead of ViewBag here like.
Controller:
public ActionResult Index()
{
var groups = db.SHP_Products
.GroupBy(c => c.SHP_Category.Name,
(category, items) => new
{
CategoryName = category,
ItemCount = items.Count(),
Items = items
}
);
ViewData["groups"] = groups.ToList();
return View();
}
View:
<ul>
#foreach (var m in (dynamic) ViewData["groups"])
{
<h2>#m.CategoryName</h2>
PreviousNext
<li></li>
}

MVC3 - RenderPartial inside RenderSection not working

I'm working on an MVC3/Razor page, and in my _layout I have
#RenderSection("relatedBooksContainer", false)
In another page I use that section with:
#section relatedBooksContainer
{
#{ Html.RenderPartial("~/Views/Shared/Bookshelf.cshtml", Model.Books);}
}
This doesn't work. From what I've read, RenderSection will only ever go one layer deep - it has no concept of the Html.RenderPartial in the related section and will just return a blank area. The workaround I read at http://forums.asp.net/t/1590688.aspx/2/10 is to use RenderPage and commit the returned HTML to a string, then outout that string in the render section...which works! That is, until I pass a model to the partial page, then it throws an error saying:
The model item passed into the
dictionary is of type
'TheBookshelf.ViewModels.BookshelfViewModel',
but this dictionary requires a model
item of type
'System.Collections.Generic.List`1[TheBookshelf.EntityModel.Book]'.
Anyone have any idea why this might be happening? Are there any other ways to achieve this?
Try the #Html.Partial instead
#section relatedBooksContainer
{
#{ Html.Partial("~/Views/Shared/Bookshelf.cshtml", Model.Books);}
}
The error message is regarding the type and return type of the Bookshelf from the model.
public IEnumerable<Book> Bookshelf()
{
var q = from book in bookshelf
select book;
IEnumerable<Book> myBooks = q.ToList<Book>();
return myBooks;
}
did the solution in the provided link work for you?
I couldn't get it to work. That is I couldn't get ViewData["MainView"] to pass the data from Layout.cshtml to partialview. This aparently is a feature as every view is supposed to have it own ViewData obj. It seems ViewData is not global like I have thought. So what I get in ViewData["MainView"] from Layout in my partial view is null......I eventually found a work around for this and was able to pass the page reference from Layout to Partialview via a #Html.Action call from Layout -> Controller -> PartialView. I was able to get my partialview to access and write to the correct rendersection. However I want to call the same partialview many times in my Layout.cshtml. A subsequent call to the same Partialview again in the Layout, does not work, as the reference to layout has changed since the first call and rendersection update. So the code looks like this:
Layout.cshtml:
#RenderSection("Top", false)
#Html.Action("Load", "Home", new { viewname = "_testPartialView", pageref = this })
#Html.Action("Load", "Home", new { viewname = "_testPartialView", pageref = this })
Partial View:
#Model Models.testModel
#Model.Content
#{
var md = (System.Web.Mvc.WebViewPage)#Model.pageRef;
#*This check fails in subsequent loads as we get null*#
if(md.IsSectionDefined("Footer")) {
md.RenderSection("Footer");
}
else {
md.DefineSection("Footer", () => { md.WriteLiteral("<div>My Contents</div>"); });
}
}
}
controller:
public ActionResult Load(string viewname, System.Web.Mvc.WebViewPage pageRef)
{
var model = new Models.testModel { Content = new HtmlString("time " + i++.ToString()), pageRef = pageRef };
return PartialView(viewname, model);
}

RazorEngine and parsing physical view files always causes exception

I have the following RazorEngine call:
public class RazorEngineRender
{
public static string RenderPartialViewToString(string templatePath, string viewName, object model)
{
string text = System.IO.File.ReadAllText(Path.Combine(templatePath, viewName));
string renderedText = Razor.Parse(text, model);
return renderedText;
}
}
This is called from:
_emailService.Render(TemplatePath, "Email.cshtml", new { ActivationLink = activationLink });
I also have this view file (email.cshtml):
<div>
<div>
Link: #Model.ActivationLink
</div>
</div>
When the call to Razor.Parse() occurs, I always get a:
Unable to compile template. Check the Errors list for details.
The error list is:
error CS1061: 'object' does not contain a definition for 'ActivationLink' and no extension method 'ActivationLink' accepting a first argument of type 'object' could be found
I've tried everything under the sun, including trying a concrete type as opposed to anonymous type, declaring the #Model line at the top of the view file but no luck. I'm wondering if the library is at fault or definately me?
By the way, the razorengine I am referring to is available here at codeplex:
RazorEngine
If you make the call like so:
Razor.Parse(System.IO.File.ReadAllText(YourPath),
new { ActivationLink = activationLink });
That should give you the correct output. But after I see your method posted above I'll be able to make a determination where the problem lies.
Update
Change your method to the following:
public class RazorEngineRender {
public static string RenderPartialViewToString<T>(string templatePath, string viewName, T model) {
string text = System.IO.File.ReadAllText(Path.Combine(templatePath, viewName));
string renderedText = Razor.Parse(text, model);
return renderedText;
}
}
and you can call it like you do above.
The reason it doesn't work is because you're telling the Parser that the model is of type object rather than passing in what type it really is. In this case an anonymous type.
The accepted answer was perfect in 2011 (I believe pre-v3 of RazorEngine) but this code is now marked as obsolete in latest version (in time of typing it is 3.7.3).
For newer version your method can be typed like this:
public static string RenderPartialViewToString<T>(string templatePath, string templateName, string viewName, T model)
{
string template = File.ReadAllText(Path.Combine(templatePath, viewName));
string renderedText = Engine.Razor.RunCompile(template, templateName, typeof(T), model);
return renderedText;
}
and in order for it to work you need to add
using RazorEngine.Templating;
Here are a few hints you might try:
Make your razor view strongly typed to a model:
#model Foo
<div>
<div>
Link:
<a href="#Model.ActivationLink" style="color:#666" target="_blank">
#Model.ActivationLink
</a>
</div>
</div>
When rendering it pass a Foo model:
_emailService.Render(
TemplatePath,
"Email.cshtml",
new Foo { ActivationLink = activationLink }
)
If you are trying to send emails from your Views make sure you checkout Postal before reinventing something.

Shorthand for creating a ViewDataDictionary with both a model and ViewData items?

Is there any way to create a ViewDataDictionary with a model and additional properties with a single line of code. I am trying to make a RenderPartial call to a strongly-typed view while assembling both the model and some extra display configuration properties without explicitly assembling the ViewDataDictionary across multiple lines. It seems like it would be possible given the RenderPartial overload taking both a model object and a ViewDataDictionary but it looks like it simply ignores the ViewDataDictionary whenever they are both populated.
// FAIL: This will result in ViewData being a ViewDataDictionary
// where Model = MyModelObject and there are no other parameters available.
this.Html.RenderPartial("SomePartialView", MyModelObject, new ViewDataDictionary(new { SomeDisplayParameter = true }));
I found someone else with the same problem, but their solution is the same multi-line concept I found: create a discrete ViewDataDictionary with the model, add the new parameter(s) and use it in the RenderPartial call.
var SomeViewData = new ViewDataDictionary(MyModelObject);
SomeViewData.Add("SomeDisplayParameter", true);
this.Html.RenderPartial("SomePartialView", SomeViewData);
I can always wrap that logic into a ChainedAdd method that returns a duplicate dictionary with the new element added but it just seems like I am missing some way of creating a ViewDataDictionary that would do this for me (and that is a bit more overhead than I was hoping for).
this.Html.RenderPartial("SomePartialView", new ViewDataDictionary(MyModelObject).ChainedAdd("SomeDisplayParameter", true));
public static ViewDataDictionaryExtensions {
public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, string key, object value) {
return source.ChainedAdd(new KeyValuePair<string,object>(key, value));
}
public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, KeyValuePair<string, object> keyAndValue) {
ViewDataDictionary NewDictionary = new ViewDataDictionary(source);
NewDictionary.Add(keyAndValue);
return NewDictionary;
}
}
As well, trying to assemble a ViewDataDictionary with an explicit Model and ModelState simply causes a compilation error because the ModelState is read-only.
// FAIL: Compilation error
this.Html.RenderPartial("SomePartialView", new ViewDataDictionary { Model = MyModelObject, ModelState = new ViewDataDictionary( new { SomeDisplayParameter = true }});
ANSWER(S): It looks like Craig and I ended up finding two separate syntaxes that will get the job done. I am definitely biased in this case, but I like the idea of setting the model first and "decorating" it afterwards.
new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{ Model = MyModelObject };
Of course, I would still be spinning my wheels without his [eventually spot-on] answer, so, circle gets the square.
Use an object initializer and collection initializers:
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{
Model = MyModelObject
}
The inner ViewDataDictionary gets its collection initialized, then this populates the "real" ViewDataDictionary using the constructor overload which takes ViewDataDictionary instead of object. Finally, the object initializer sets the model.
Then just pass the whole thing without setting MyModelObject separately:
this.Html.RenderPartial("SomePartialView", null,
new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
{ Model = MyModelObject });
Using Craig's answer as a starting point--I didn't even know you could combine both a constructor call and an object initializer--I stumbled on this snippet from Palermo that leads to a combination that works. He uses some sort of dictionary shorthand that somehow ends up populating the ModelState when consumed by the ViewDataDictionary object initializer.
new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
// Of course, this also works with typed ViewDataDictionary objects (what I ended up using)
new ViewDataDictionary<SomeType>(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
I still don't see how this ends up working given that you cannot set the ModelState explicitly in an initializer, but it does seem to maintain both the original model object and the "appended" parameters for the view. There are definitely a number of other permutations of this syntax that do not work--you cannot combine the model with the dictionary in a single object or use object-initializer syntax for the dictionary values--but the above version seems to work.
I created an extension method on HtmlHelper to copy the property names and values from an anonymous object to a ViewDataDictionary.
Sample
Html.RenderPartial("SomePartialView", MyModelObject, new { SomeDisplayParameter = true })
HtmlHelper Extension
public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model, object viewData)
{
var vdd = new ViewDataDictionary(model);
foreach (var property in viewData.GetType().GetProperties()) {
vdd[property.Name] = property.GetValue(viewData);
}
htmlHelper.RenderPartial(partialViewName, vdd);
}
This is what worked for me in old style mvc aspx view:
<% Html.RenderPartial("ContactPartial", Model.ContactFactuur, new ViewDataDictionary(this.ViewData ) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Factuur" } }); %>
the thing here is that in the constructor I use the current viewdata "new ViewDataDictionary(this.ViewData)" which is a viewdatadictionary containing the modelstate that I need for the validationmessages.
I came here with the exact same question.
What I thought might work was this (pardon the VB Razor syntax)
#Code Html.RenderPartial("Address", Model.MailingAddress, New ViewDataDictionary(New With {.AddressType = "Mailing Address"}))End Code
But of course you get this error at run-time:
System.InvalidOperationException was unhandled by user code
Message=The model item passed into the dictionary is of type 'VB$AnonymousType_1`1[System.String]', but this dictionary requires a model item of type 'ViewModel.Address'.
But what I found, is that what I really wanted was to use was Editor Template anyway.
Instead of RenderPartial use:
#Html.EditorFor(Function(model) model.MailingAddress, "Address", New With {.AddressType = "Mailing Address"})
An editor template is just a partial view that lives
~/Views/{Model|Shared}/EditorTemplates/templatename.vbhtml
My template for Address is a strongly-typed partial view, but the EditorFor method gives the ability to add additional view data items easily with an anon object.
In the example above I didn't need to include the template name "Address", since MVC would would look for a template with the same name as the model type.
You can also override the display template in the same way.

Resources