Oh, no, not him again!
Yes, well, me again. Feeling the need of ventilating my woes… If you’re looking for advice, you probably best go somewhere else. If you read my post and like to share your own good practice you are most welcome. Many real life karma (the ones that will get you a better salary and a bigger car in your next life) I will send through space to whoever give me some good advice on my path to understand how to code better and be more efficient.
Edit: I do have some advice to share… For XSLT : improve your Xslt skills, learn from others + use a XSLT/XML-editing tool. For C#: invest in an extension like ReSharper to help you code better and faster. And generally: Be active on forums (our.umbraco.org and stackoverflow).
A common scenario
I wanted to create a macro that presents vcard-style person data in a list with templated Html. A very common scenario, right?
For the macro language / method I wanted three things :
- readability,
- modularity and a
- separate presentation layer (that a designer could edit, for example to reorder or change tags).
Here’s what the final markup should look like:
<div class="vcard">
<div class="fn n"><span>Taichi</span> <span>Kaminogoya</span></div>
<div><a class="email" href="mailto:your@mail.address" title="Mail to Taichi Kaminogoya">your@mail.address</a></div>
<div class="tel">
<span class="type">home</span>:
<span class="value">+1.415.555.1212</span>
</div>
</div>
The data is fetched from a list of persons and no field is certain to contain information. If empty then the whole tag should be left out.
First call : beginners style Xslt
Polonius:
-What do you read, my lord?
Hamlet:
– Tags, tags, tags.
With plain Xslt I made it into a working macro quite fast. Xslt has its advantages, no doubt about that.
But with all the alternatives I needed the code became very long and hard to read, filled with lots of
<xsl:choose><xsl:when test="./data [@alias]=''">...
Guru style Xslt
“Divide your Xslt into templates you should” as Yoda would have said. Yes with
<xsl:call-template name="render-item"><xsl:with-param name="css-class" select="'tel'"><xsl:with-param "...
code got better and more readable.
But with many parameters, it was still a tag soup hard to melt, hard to find the real syntax and elements underneath all tag-names and <xsl:.
One thing that went fine was placing the render-item template in another file so I could reuse it later.
<xsl:import href="vCard-render-item.xslt"/>
Conclusion : Xslt works fine but is hard to read because of the many options and parameters. I have several similar macros running with xslt, but they are hard to maintain, small fixes sometimes takes a lot of time to do. I wanted to try out another way this time.
Edit : A real Xslt guru (Chriztian Steinmeier) gave me a great advice on this particular case, “use apply-template”. Makes sense and the syntax gets more readable.
<xsl:template match="person">
<div class="vcard">
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="name">
<div class="fn n">
<span><xsl:value-of select="." /></span>
</div>
</xsl:template>
<xsl:template match="email">
...
DLR and IronPython
It first felt like a breeze going back to familiar C/VB/Pascal-like syntax from my quite long-time Xslt-efforts. And for my vCard-task the code did get nice and readable. With a separate file for item-rendering I also achieved the goal to have that part reusable.
However I never got comfortable with the Html strings inside the code (instead of the other way around). String templates like s = “<div class='{classname}’>{value}</div>” feels half okay, but I wanted the templates be in a separate file with VS (or any other tool) for Html validation. I wish I had found a cool Python Html rendering engine. Anyone?
Also – and perhaps the biggest disadvantages with IronPython as I see it is 1) there’s almost no IronPython-activity on the Umbraco forums and 2) already using xslt, cs and vb, a fourth language just feels wrong.
Conclusion : Python is okay but Html is included in code. Also you dont see much Python in Our forums, and the syntax lacks full support in Visual Studio.
Back to .Net-User Controls
Why not choose the easiest way? Plain comfortable C#!
I first tried using a Web User Control and a DataList for my job.
But I first stumbled on the part where I was to build my Html with some simple conditions.
if (obj.Phone!="") <div...>
With the help of Stackoverflow i did I get a nice solution to omit tags with empty values:
<div runat="server" visible="<%# Eval("MyProp")!==%>">nnn</div>
But Evals are just not cool. Late data binding in this day and age? No help from Visual Studio finding the right names and (slightly) bad performance.
Going for the data item is better,
<%# ((Customer)Container.DataItem).CustomerName %>
or via a helper function in my case – since I had to to two casts and that got messy:
<%# person(Container.DataItem).CustomerName %>
The only drawback is that in strings we loose the IntelliSense, and passing parameters I need strings… 😮 Stackoverflow again (no solution).
Conclusion : works fine but I do miss my IntelliSense.
Edit: The Visual Studio extension ReSharper does a beautiful job with code in strings. Syntax checking, IntelliSense, go to declaration, create property stub – everything works.
Update: this is my current solution, look below.
Code blocks
Next I went to old asp-style Code blocks syntax,
<% if (obj.MyProp!=""){ %><div ... <% } %>
and was happy for a while. Nice and readable, and does the job. Markup mixed with presentation logic. Beautiful!
At least so I thought until I decided to move the item-rendering part to a separate Web User Control. A most valid decision.
Using codeblocks in both the main Web User Control and the included Web User Control just will not render. I got <%=obj.Name%> in my rendered markup. Guess that has to do with the page life cycle. Stackoverflow again.
Conclusion : looks good, but does not work nicely with a sub-control.
Code blocks and Server side sub user control
Server Controls was a new experience to me. A good part of it is that it gives a possibility to code the control is design time presentation aswell as the runtime. Plus it fits nicely in Visual Studio Toolbox.
A Server Control for the Item renderer worked fine. And it could be mixed with code blocks in the calling Web User Control. But here I was dropping in yet another technology again. Plus the Server Control only has a cs-file, so back to building Html strings…
Conclusion : works fine, but needs string html building and adds one extra technology.
A combination with codeblocks and a helper function
Code block syntax combined with an item-rendering-function in a App_Code class:
<% foreach (person in GetPersonList()) {>%>
<div class="vcard">
<%=library.RenderVcardItem("n",person.FullName) %>
<%=library.RenderVcardItem("phone",person.Phone)%>
</div>
<%}%>
With that I think I achieved very good readability. However the goal to have all markup editable I did not reach. I’ll leave that for the MVC future.
VB or C#?
Another thought – using code blocks with C# syntax, the brackets are quite hard to find between the % and > so I stongly consider using VB in code blocks to get better readablity:
<% For Each person In GetPersonList() >%>
<div>
<%=library.RenderVcardItem("n",person.FullName) %>
<%=library.RenderVcardItem("phone",person.Phone)%>
</div>
<% Next %>
Update: Current solution using databinding control
Using an Ascx with a databound ListView combined with an item rendering Ascx using code blocks I actually achieved what I wanted (only with a bit of mix of techniques and some extra overhead writing properties and the function to cast the dataitem). Also with the Resharper extension I got my dear Intellisense and syntax check. Not 100% on the syntax check tho, but a good step forward.
<asp:ListView ID="ListView1" runat="server">
<LayoutTemplate>
<ul class="contact-list">
<li id="itemPlaceHolder" runat="server"></li>
</ul>
</LayoutTemplate>
<ItemTemplate>
<li class="vcard">
<uc1:DSS runat="server" Label="Name:" CssClass="fn n" Text='<%# db(Container.DataItem).Firstname +" "+ db(Container.DataItem).Lastname %>' />
<uc1:DSS runat="server" Label="Address:" CssClass="adr" Text="<%# db(Container.DataItem).Address %>" />
<uc1:DSS runat="server" Label="Phone:" CssClass="tel" Text="<%# db(Container.DataItem).Phone%>" />
</li>
</ItemTemplate>
</asp:ListView>
The item rendering Web User Control (DSS):
Properties like Text, Label and CssClass is declared in the codebehind.
<%if (Text != "")
{%>
<div class="<%=CssClass%>">
<% if (string.IsNullOrEmpty(AHrefLink)){ %>
<strong class="type" title="<%=SubClass %>"><%=Label %></strong> <span class="value"><%=Text%></span>
<%} else {%>
<strong class="type" title="<%=SubClass %>"><%=Label %></strong> <a href="<%=AHrefLink %>"><%=Text%></a>
<%} %></div>
<%} %>
Okay with this?
Lets check my list again:
- readability I think so Yes – but the mix of databinding and code block syntax is not perfect.
- modularity Yes.
- separate presentation layer Even though I doubt a HTML-designer person would be perfecly happy with this he/she is able to see and change the tags in the Ascx files, so Yes.
Thanks
Thank you for reading. And thanks to people that helped me out with this task.
Please do post a comment if you feel like it.
(All code in this post are written directly into the WP text editor, not tested and only intended to show the ideas.)