Does Spark view engine support anonymous types as viewdata - spark-view-engine

I'm setting an anonymous type like this:
PropertyBag["model"] = new
{
IsHome = areaId == "Home",
IsGroups = areaId == "Groups",
IsUsers = areaId == "Users",
IsComputers = areaId == "Computers",
};
And in my view I want to be able to call model.IsHome from inside a condition attribute output like this:
Home
But this doesn't seem to work with anonymous types!
I've tried to set my viewdata to object:
<viewdata model="object" />
Any ideas if this is supported at all?

There's an example of doing something similar to what you're trying here: Late-bound Eval – email templates revisited. Although it's related to using Spark for email templates, you should be able to do the same in your views. It may or may not have an effect on performance though, may want to profile it before and after to make sure.

Related

Using ASP.NET MVC ViewModels with Durandal without boilerplate

I'm writing a small app based on John Papa's "Hot Towel" template, meaning MVC 4 and Durandal.
As someone with mostly MVC experience it seems most "correct" to use MVC's "Models" with their Data Annotations for validation.
I also don't want to write the viewmodels OR the validation twice, which seems a reasonable enough desire.
So far I've easily figured out how to use ko.mapping for loading the ViewModel and its data from the server side using JSON, and binding my form to it. Great. But what about the validation part? For the life of me I can't find a single solution for this on the internet, as I'm not interested in making use of Razor (I've seen some solutions using its HtmlHelpers).
At the moment, the best way I see is to transform the ViewModel objects on the server to a simpler JSON object using reflection, where these annotations will be represented as members. Like so:
public object TransformVM(object vm)
{
var properties = vm.GetType().GetProperties();
var result = new Dictionary<string,object>();
foreach (var p in properties)
{
var attributes = p.GetCustomAttributes(true);
var displayName = attributes.FirstOrDefault(a => a is DisplayNameAttribute) as DisplayNameAttribute;
result.Add(p.Name, new
{
value = p.GetValue(vm),
displayName = displayName != null ? displayName.DisplayName : ""
});
}
return result;
}
meaning instead of getting the view model as this json
{ Email: 'something#somewhat' }
I get a two-levelled object like
{ Email: { value : 'something#somewhat', required: true, displayName: 'e-mail' } }
and then using ko's binding this way:
<label data-bind="text: post.Email.displayName"></label>
<input type="text" data-bind=" value: post.Email.value" />
This of course means writing a "translation" for every single DataAnnotation I wish to use, which can get cumbersome.
So is this the right way or am I pitching myself into a hole too deep to get out of?
If you are going with the same architecture as John's HotTowel, why not use Breeze JS validations? Your model will have the basic validation rules from EF/database, then you can enhance using custom validators.
Read more here: http://www.breezejs.com/documentation/validation

MVC3 -- Adding additional items to List<> with Jquery

I have a view model that looks like this:
HomeViewModel hvm = new HomeViewModel();
hvm.Applicant = new Person();
hvm.Applicant.Residences = new List<Residence>();
hvm.Applicant.Residences.Add(new Residence() { Type = "Current" });
In my .cshtml page, I have:
<label>Street # *:</label> #Html.TextBoxFor(m => m.Applicant.Residences[0].StreetNumber)
And so on and so forth for my properties in my Residence model. However, I want the user to be able to add multiple residences(previous, secondary, other, etc). I can add the necessary form fields via jquery, however, since the model doesn't know about the new list items, I get an error about null objects. The front end may be adding numerous residences via jquery.
This is a surprisingly complex topic. Check out the blog series starting with http://ivanz.com/2011/06/16/editing-variable-length-reorderable-collections-in-asp-net-mvc-part-1/
Try this: http://archive.plugins.jquery.com/project/jquery-dynamic-form
View demo here: http://sroucheray.org/blog/jquery-dynamic-form/

How to access objects inside a List, saved in a Session. ASP.NET MVC 3

So I have this code:
var list = new List<Carrito> {
new Carrito { ProductId = producto.ID , Cantidad = 1, PrecioUnitario = producto.Precio }
};
Session["list"] = list;
return View();
Then I load the view but I don't know how to print the the content that is inside the session. Any ideas?
This is the code I use inside the view but doesn't work:
#foreach(var item in (IEnumerable<object>)Session["list"] )
{
<p>#item.ProductId</p>
}
it's as simple as reading back the value from your session varable and cast it to the original type, then do whatever you want
example:
#{
if(Session["list"]!= null)
{
var listBackFromSession = (List<Carrito>)Session["list"];
// do what you want
}
}
My recommendation is to use the more elegant way of ViewBag.
a quote from official asp.net mvc website about Viewbag:
New "ViewBag" Property
MVC 2 controllers support a ViewData property that enables you to pass
data to a view template using a late-bound dictionary API. In MVC 3,
you can also use somewhat simpler syntax with the ViewBag property to
accomplish the same purpose. For example, instead of writing
ViewData["Message"]="text", you can write ViewBag.Message="text". You
do not need to define any strongly-typed classes to use the ViewBag
property. Because it is a dynamic property, you can instead just get
or set properties and it will resolve them dynamically at run time.
Internally, ViewBag properties are stored as name/value pairs in the
ViewData dictionary. (Note: in most pre-release versions of MVC 3, the
ViewBag property was named the ViewModel property.)
Further more, This is a good article to read about the different ways you have in MVC in order to preserve data: http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications
example:
var list = new List<Carrito> {
new Carrito { ProductId = producto.ID , Cantidad = 1, PrecioUnitario = producto.Precio }
};
// use ViewBag
ViewBag.myList = list;
then inside your view, read them back like this:
var myList = (List<Carrito>)ViewBag.myList;
// your code
You're doing MVC fundamentally wrong. In MVC, Views are there only to render a model. The logic of accessing that model should be implemented in controller, or in any other place, but not in the View itself.
Thus I recommend that you simply pass your list to the view, and make your view strongly-typed by including #model List<Carrito> at the top.

Achieving product/Apple-ipad in place of product/Index/23

I wasn't able to put the write words for the Title, so I explained what I'm looking after.
Currently, I am using following ActionLink code in View.
#Html.ActionLink(#product.Name, "Index", "Product", new { id = #product.Id }, null)
This code redirects to following action method in Product Controller
public ActionResult Index(int id)
{
Product product = pe.Products.Where(p => p.Id == id).First();
ViewBag.Title = product.Name;
ViewBag.Description = product.MetaDescription;
ViewBag.Keywords = product.MetaKeywords;
return View(product);
}
Now, what i want: instead of mysite.com/Product/Index/22 , my URL should be something like mysite.com/Product/Apple-ipad.
I know, I can use product Name instead of Id and pass it to the action method. But, this way i think that the queries will get slower since Id field is indexed but Name isn't. Is this the only option at my disposal. Let me know how will you handle this requirement.
Use the product name in the route instead of ID and put a nonclustered index on the product name column in the database.
That way you don't get a performance hit on the select (although you will get one on the insert/update/delete but I suspect those happen far less than the selects).
CREATE INDEX IX_[index_name]
ON [schema].[table_name] ([column_name]);
Another way I've seen used in a few places is using both the name and the id like mysite.com/Product/Apple-ipad/22. The name is not actually used by the code, and it's just there for SEO. One big disadvantage of this is that someone that does not like your site can put various urls leading to the same content all over the internet for google to find. Google doesn't like that so your site is penalized and you are worse than before.

MVC Razor dynamic model, 'object' does not contain definition for 'PropertyName'

Using MVC 3 with Razor view engine.
I have this View:
#model dynamic
#{
var products = (List<ListItemBaseModel>)Model.Products;
var threshold = (int)(Model.Threshold ?? 1);
var id = Guid.NewGuid().ToString();
}
It is called from another view using this code:
#Html.Partial("PartialViewName", new { Products = Model, Threshold = 5 })
In both Views, when I debug them and watch Model, it seems to contain the correct object.
When I execute the code I get an error on the var products = line saying:
'object' does not contain a definition for 'Products'
Why do I see this error?
When I watch the Model object in debugging mode it looks all right (having 2 properties: Products and Threshold)
I just tried this (dynamic view model in CSHTML) and got the same error as your when using an anonymous class, but it worked fine if I created a named class. I searched but haven't seen this documented anywhere.
// error
return View(new { Foo = 1, Bar = "test" });
// worked
return View(new TestClass { Foo = 1, Bar = "test" });
David Ebbo clarified that you can't pass an anonymous type into a dynamically-typed view because the anonymous types are compiled as internal. Since the CSHTML view is compiled into a separate assembly, it can't access the anonymous type's properties. Due to this forum post, David Ebbo clarified on (Dec 22 2011) that MVC 3 now has direct support for dynamic.
On .NET 4.0 Anonymous types can easily be converted to ExpandoObjects and thus all the problems are fixed with the overhead of the conversion itself.
Check out here
This has nothing to do with anonymous types having internal properties
It is perfectly possible to pass anonymous types from a view to a partial view
I encountered the same problem today and it was nothing (directly) to do with the problem of passing anonymous types and their inherent internal properties.
As such, in relation to the OPs question, the answer by #Lucas is irrelevant - even though the workaround will work.
In the OPs question, an anonymous type is being passed from a view in assembly X to a partial in assembly X, therefore the problem that David Ebbo outlined of the properties being internal for anonymous types is of no consequence; the types compiled for the view, the partial and the anonymous type are all contained in the same assembly.
So what is causing the sudden failure to pass an anonymous type from a view to a partial?
At least in my situation, I discovered that it was due to having another view in the SAME FOLDER that specifies a model type that cannot be resolved. Views get compiled at runtime, and so it would make sense as a failure at runtime to compile the views would also mean a failure to compile the dynamic types and the partial would simply receive an object. It's not immediately obvious what is going on, but in the OPs specific example (and mine) this is more than likely the cause of the problem.
It is interesting to note that if the model type is correct but another part of the view doesn't compile then anonymous types are not affected in the same way. This must be down to how Razor breaks up the dynamic compilation of the component parts of the view.
Once you correct the offending view, either rebuild the whole solution or clean and rebuild the project before checking to see if it's fixed.
To ensure you are not caught out by this again you can enable compile time compilation of your Razor views by adding this to your csproj file:
<PropertyGroup>
<MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
Add the following class anywhere in your solution (use System namespace, so its ready to use without having to add any references) -
namespace System
{
public static class ExpandoHelper
{
public static ExpandoObject ToExpando(this object anonymousObject)
{
IDictionary<string, object> anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousObject);
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in anonymousDictionary)
expando.Add(item);
return (ExpandoObject)expando;
}
}
}
When you send the model to the view, convert it to Expando :
return View(new {x=4, y=6}.ToExpando());
Instead of using the dynamic Model type within the partial view.
You can call the anonymous object attributes using #ViewData.Eval("foo") instead of #Model.foo.
Then you can remove #Model dynamic from the view.
I came across this issue recently when passing some attributes between views for the Facebook Social Comments Integration. Example code:
Html.RenderPartial(#"Layouts/Partials/_Comments", new {currentUrl = Model.CurrentPage.GetAbsoluteUrl(), commentCount = 5 });
Then in my view I just had this div:
<div class="fb-comments" data-href="#ViewData.Eval("currentUrl")" data-numposts="#ViewData.Eval("commentCount")" data-width="100%"></div>
i am not sure that you are getting this error because you are not implementing the work-around. i got the same error in a partial view. the solution was just to clean the build and rebuild it. if the syntax is correct, the code should work, but the razor engine may not be updating the code changes properly.
I worked around this issue by using a Dictionary.
#Html.Partial("_Partial", new Dictionary<string, string> { { "Key1", "Val1" }, { "Key2", "Val2" }, { "Key3", "Val3" } });
To use dynamic type you need to reference Microsoft.CSharp assembly

Resources