I wish I had intellisense before
I was just walking through all the sample web apps in the Spark project converting them from ${H(expr)} and ${expr} to ${expr} and !{expr}. One thing I hate to admit is how frequently I had demo data like product names going out without html encoding - so for me that’s kind of case in point on that particular feature.
The other thing I noticed was when I was working updating a _NavItem.spark partial file in a MonoRail web app. I saw a comparison of a value with a Context.Request.RawUrl and had an idle curiosity about what other properties might have been available.
So I thought, hey! Great time to see if that intellisense thing is working. Ctrl+space and Bam! That property is deprecated.
But even better than that is how well it informs you of the vast array of properties available at every level. I really wish we’d had this available a few projects ago. I guess you should never underestimate the value of your development tools.
Here’s a montage of how easy it is to find what I believe is the proper value to use.
This is also a pretty interesting partial file. I’d like to take some time and look at what it’s doing.
<div id="${navId}">
<!--
The "INavProvider Nav" being used here is added to
the abstract base class View in the project
If you don't want to use a project wide base class you can
add a property with global type="INavProvider" Nav="null"
in a spark file and it would also be "set" when the
Windsor container instantiates the view
-->
<ul>
<li each="var item in Nav.GetNav(Context, navId).Items"
class="${IsSelectedClass(item)}">
<a href="!{SiteRoot + item.Url}">${item.Caption}</a>
</li>
</ul>
</div>
<macro name="IsSelectedClass" item="NavItem">
#if (SiteRoot + item.Url == Context.UrlInfo.UrlRaw) return "selected";
</macro>
One thing to note is this example is using the Windsor container to instantiate the view classes, providing service and dependency injection. The views are configured to use a base class in the project which has a public INavProvider Nav {get;set;} property. That’s what the Nav.GetNav(Context, navId) is invoking.
Another thing that’s interesting is how a macro IsSelectedClass is being called. That’s probably a bad name, isn’t it… The idea is it’s seeing if the given NavItem is selected and it’s returning a css class based on that. So your tab/nav can reflect your location.
It’s a trivial example (and only works for a single exactly equal Url so that’s impractical for real application) but it’s showing how you can also return values from macros. Normally a macro will return the accumulated html of the template material it contains, but because <macro name="IsSelectedClass" item="NavItem"> is really just a string IsSelectedClass(NavItem item) you can cut to the chase and return any value you want.
When it comes right down to it you can think of a macro as a helper function declared in the Spark language.
Tags: internet, programming, spark, tech





January 4th, 2009 at 1:51 pm
A couple of things:
First, the intellisense is great. I love it, want more of it. :)
Second, I have to admit, I was weary of the auto-encoding of output at first. But now that it has grown on me, I think it is a very useful feature. I’ve made it a habit to write ${H(data)} wherever I am outputing anything, but even then, I miss things sometimes. I also think the !{} syntax is very fitting for unencoded output.
Finally, I don’t know if I’m a huge fan of the dependency container instantiating view classes - or atleast not in the way you used it here. This is a very powerful feature and I’m not sure I want my views having that much power. In your example, I would have created a ViewComponent/Controller that was testable and had the ViewComponent bind the needed data to the view. The view then just worries about displaying and formatting the data.
With the ability to bind dependencies to the view like this, it blurs the lines between the view’s and controller’s responsiblities which I do not belive is a good thing.
Maybe this could be a useful feature for a view Helper class that required configuration or a dependency of some sort - to be able to bind that directly to thew view without going through the controller. I know I have wanted that feature in Monorail before - the ability to instantiate Helpers from the dependency container. This feature from Spark could help alleviate that shortcoming from Monorail. But I don’t think I would use it for anything else other than that.
I’m not saying I don’t like the feature, I just don’t like your example. :)
January 4th, 2009 at 3:48 pm
Yeah, I’ve been on the fence about how IoC applies to views myself. I’ve never used it in production code - it was part of the sample that’s showing how to use an extensibility point for the view class activation.
The engine has an interface you can provide, and it will call a method on that interface saying “See this Type viewType I’ve compiled up here? Gimme a factory for it please.”
I wouldn’t claim to be an expert in the field of IoC, DI, and composition - however in my mind it seems like a classic A uses B uses C inversion of control problem. Controller uses View uses Service. Though I suspect if you asked four experts you’d get five answers. :)
If a view needs a service it doesn’t have the brains (or the deserve the responsibility) of instantiating and configuring an instance properly. Especially because it shouldn’t know if there are things that service relies on (like a repository access layer). And those might change in the future.
When MonoRail is instantiating helpers it will use Windsor if it’s set up to do so. So that helps for downstream dependencies but it still leaves one problem:
It seems that by putting those into helpers on the controller I think you’re still putting the concern in the wrong place. Why wouldn’t a view - just like any other component that needs a service - state that need and use the service?
From a site nav standpoint I think it makes sense… The nav service is something which itself can be tested, it’s all over the place and you’d need have the helper or action filter *everywhere* or in just one place: a service the master view uses.
I don’t think keeping it out of the view makes your tests better because it’s not like you’d be asserting that every controller action had called for nav data, or checking html for presense of nav tags. On the other hand a pretty good test might be to take a a stub nav service, a dummy view, and the real layout - rendering it - and asserting that nav had been called for appropriately.
Anyway - don’t mean to sound like I’m arguing the point. Just presenting one side of the case. After all it is something that could easily be abused or misunderstood and you’re walking right back into the world of code-behind.
January 4th, 2009 at 4:33 pm
I think my new year’s resolution should be about practicing terseness. :)
January 4th, 2009 at 6:17 pm
i only have partial spark support in vs2k8 standard
it close the ${} tag but it don’t give me and help if i type ${Html.}
January 14th, 2009 at 7:48 pm
I second that Subnis. The Html comes up in intellisense but nothing after you hit the dot. Very painful.
Also if i try and use <% tags they do not auto complete like they do on a aspx page.
I am running vs2008 standard also.
January 22nd, 2009 at 4:48 pm
I can confirm as well that Intellisense does not work when using ${Html.} or anything after dot.
How can one change the colors for the .spark file tags? I am having a dark background and blue and red for tags look quite unreadable.
January 22nd, 2009 at 8:50 pm
Intellisense for ${Html. } is not working for me as well. VS Professional.. only this one, all the other Intellisense seems to be working fine.
@Robert - there are separate VS Settings for Spark’s display items list under File|Options -> Environment|Fonts and Colors”.. each item starts with “Spark”… easy to find.
January 22nd, 2009 at 10:58 pm
Hey! I just created a web app from scratch in a new solution and I’m seeing the same thing. I’ll backtrack to see if I can find a place where it was working and try to determine the difference.
January 23rd, 2009 at 12:56 am
It started working after mucking with references to System.Web.Routing, System.Web.Abstractions, or Microsoft.Web.Mvc… Tried changing copy local to true on some…
${Html.} started working after closing and re-opening index.spark. Difficult to reproduce exactly what difference caused it to work.
January 23rd, 2009 at 1:22 am
That actually does seem to make a difference having those reference and making them “copy local”. I also have never seen ${Context.} bring up intellisense - System.Web.Abstractions.dll didn’t seem to be available. After adding a reference with “Copy Local” true, rebuilding the web app, and re-opening the file ${Context.} finally worked.
You can also add a line like
#System.Web.HttpContextBase x = null;
to see if a type is being understood. The token HttpContextBase will be colored as a class name only if that assembly is available to the intellisense engine.
March 11th, 2010 at 1:56 pm
This is a handy blog, im pleased I stumbled onto this. Ill be back later on to check out other posts that you have on your blog.