We have moved to GitHub Issues
You are viewing the read-only archive of Umbraco's issue tracker. To create new issues, please head over to GitHub Issues.
Make sure to read the blog posts announcing the move for more information.
Created by Sebastiaan Janssen 03 Sep 2013, 07:36:06 Updated by Sebastiaan Janssen 17 Jan 2014, 15:32:08
Relates to: U4-3049
Request from Douglas Robar:
This would be a nice addition to Umbraco: http://haacked.com/archive/2011/03/05/defining-default-content-for-a-razor-layout-section.aspx
Additional requests: Ability to enter strings and @helper results as defaultContents:
@inherits UmbracoTemplatePage
@RenderSection("Footer", "String: Default Content")
@RenderSection("Footer", DefaultContent())
@RenderSection("Footer", @Inline Html: Default content)
@helper DefaultContent() { Helper: Default Content }
@RenderBody()
Will display: String: Default Content Helper: Default Content Inline Html: Default content
If you redefine the footer section in your child template then that overrules the default content.
@section Footer { Overrule default }
Will display: Overrule default
This is instead of one of the three default strings from the first code block.
This is in addition to just making the RenderSection required or not, so these will still work:
@RenderSection("Footer") @RenderSection("Footer", false) @RenderSection("Footer", true)
In the first two cases you HAVE to implement the Footer section in your child templates.
@section Footer { Footer text }
In the last case, implementing the footer is not required and will not render at all.
Implemented in rev 25d2b56ed7c75599d6bd370353460ae9cec674f2
It would be nice to use a Partial, too, which could be reused.
Will have a look at that Dan, thanks! :)
@Dan what would you expect the notation to be then? @RenderSection("Footer", ...?)
Mmm, not sure, Seb! That's for you clever guys to work out :) I guess you can't have the partial name as a string else the signature will be the same. Could you do something like:
@RenderSection("Footer", new { partial = "mypartial", model = new MyModel() })
Yeah, I thought of that, too, re-using the syntax from macro parameters. But that's totally geeky and beyond people without VS or until there is a UI for it.
In the "convention over configuration" department... @RenderSection("Footer", MyPartialView) is not ambiguous because it would require parenthesis after it to know it is an @Helper, and a string without quotes or a leading @ sign can then only be the name/alias of a partial view. The only potential difficulty is if the partial view's name/alias has spaces in it. If that's possible then this is a non-starter.
In the same department, if the above isn't workable, would it be acceptable to say that @RenderSection("Footer", "My Partial View") would look first for a partial view of the string's name/alias but if not found treats it as a literal string to be output?
I agree Doug that syntax is geeky and I don't particular like it, either. I reckon what you say could work.
I also wonder whether if you wanted to pass an HTML string in then it would have to be an IHtmlString and thus leaving a plain string as presumed to be a partial?
What about:
{{@RenderSection("Footer", new Partial("myPartialName", myModel))}}
Yes this looks very similar to what Dan suggested three hours ago, which I might not totally agree is "beyond anyone without VS", but is also almost identical to exactly what you do when you say {{@Html.Partial("myPartialName", myModel))}}. Except of course here, {{Partial}} would be a dummy class (not an actual helper method) that you new-up and pass the partial name and model into as constructor parameters. Then you could overload the RenderSection signature to accept a "Partial" class and you'd know what to do with it. Plus, one other tiny advantage is that you'd get strong typing on this instead of the anonymous object.
A related alternative to creating and newing-up a {{"Partial"}} class is to make some other method that returns something similar you could look for, such as:
{{@RenderSection("Footer", DefaultPartial("myPartialName", myModel))}}
Or what about adding 3rd param for the model and you could stick with strings:
{{@RenderSection("Footer", "myPartialName", myModel)}}
This would require you to pass a model which I believe is optional when you use normal Html.Partial, hence it's my least favorite of these suggestions so far.
I like
@RenderSection("Footer", new Partial("myPartialName", myModel))
How about @RenderSection("Footer", "~/Views/Partials/Footer.cshtml") We can just test if the file exists, if not we render the string. This way you can also put your partials wherever you want as you would provide a full path in the @RenderSection call.
Don't forget the optional third param to allow you to pass in a different model?
Ah yes! Thanks Funka
I like it. My only suggestion would be, for simplicity, to always look in the ~/Views/Partials/ folder for the cshtml file by default and not require the path to be specified unless the view is not in the default location. In fact, aren't there a number of places partials are looked for internally already? If so, all of those places should be looked at before assuming it's a custom path.
The logic for a string in the second parameter then would follow this general sequence:
Assume the string is a filename and look for it in the ~/Views/Partials folder. If found, return the result of the cshtml file. @RenderSection("Footer", "footer.cshtml")
If file is not found in step 1, assume the string contains a fully qualified path (not allowing 'dangerous' paths for security reasons) and if the cshtml file is found return the result. @RenderSection("Footer", "~/views/custom/footer.cshtml", myModel)
If neither step 1 or 2 finds a file, treat it as a string and return the string. @RenderSection("Footer", "copyright 2013")
That's too much "magic" for my liking and this is how we end up with weird edge-case bugs. "Oh, you want it to be a string but you also have a view of the same name? No worries, we'll just add yet another parameter and call it 'renderStringAsStringInsteadOfView'". Also, then we'll have a default path, and people will ask: "why is that the default path, I want it to be x", which will then turn into a config option... etc (I wouldn't allow that but still you see how we end up with these things throughout Umbraco).
So.. I say if people want a file, they type the path to the file. Not so hard, is it?
I appreciate the point. I was looking for simplicity and that most people will use the default location anyway so let's cater to the 80%+ of people who will use that and not force needlessly long strings with hard-coded paths except for when people have their partials in a different place. I'll let you make the call if that is too much 'simplicity'.
As long as the (eventual) UI for inserting the @RenderSection() allows selecting the section name and the optional second param in its various forms (string, inline-html, helper or partial) and generates the correct markup for users I'm content. (I think the 3rd param needn't be in the UI as it isn't really relevant when using the UI, though it should be discoverable via intellisense in VS).
I feel averse to the overloading of the string to mean either "content to be rendered" or "reference to a partial". Surely some other method signature can be found which eliminates all ambiguity? Dan's original suggestion to use the anonymous object is looking better and better to me now---which, if a button can be added to the toolbar to help you generate, seems no more challenging than the complexities of using {{@Umbraco.Field(...)}} with all of its optional params...
Maybe just drop rendering a string altogether? I can't really think of any real-world example where you would render a string as an alternative. Much better to use a partial or a @helper method, as this is more realistic of actual usage. Then the overload would never be needed.
Simple string rendering might be nice for something prominent like a page heading, where by default you display the current node's name or maybe the name of the section you're in, but still allow child or special pages to override this and supply their own heading... I don't think it sounds all that unusual, if not an uncommon thing to do?
@Sebastiaan - you've probably handled this already, but just to be sure about one point we hadn't specifically mentioned...
If I were to set default content with @RenderSection("sidebar", "I'm a sidebar!") and wanted to override the default I'd simply need to specify a @section sidebar . Easy.
It should also be that if I didn't want any output at all, even though using the default content specified in the @RenderSection() as before, I should be able to have @section sidebar . That would replace the default output with that contained in the section (no output in this case) because the section is empty. Whereas, omitting the @section entirely would cause the default output from the @RenderSection() to display.
The best I can do at the moment for the partials notation is:
@RenderSection("Footer", Html.Partial("MyTest")) And @RenderSection("Footer", Html.Partial("MyTest", new MyModel()))
I think that actually makes a lot of sense and we teach in the courses that you get the content of a partial by doing Html.Partial. I'm steering away from just providing it as a string which represents the path as it would all lead to a bit of dark magic. This makes it explicit and follows what you would "normally" do to get the contents of a partial somewhere. I also think this will be seldomly used by non-geeky, non-VS people. Eventually we can make a UI for this, but that's a seperate issue (U4-3049).
I've committed the latest changes in rev 130f6d3a427d72e1c8a82a5e11f3bab80a11ddb8
@Doug yes, that is exactly how it works.
@Seb - can this be added to v7 as well, please?
Yeah it's already been merged into 7.0.2.
Just tested in 7.0.2 nightly #240 and everything works except the one thing I tried first and therefore assumed none of it was implemented.
This situation errors in 7.0.2 nightly #240 (didn't try 6.2.0 nightly) if there is no @section Footer defined.
@RenderSection("Footer", "String: Default Content")
The error is: The following sections have been defined but have not been rendered for the layout page "~/Views/Master.cshtml": "Footer".
Everything is fine if you do define the @section but that kind of defeats the purpose of having a default string ;)
Otherwise, everything works perfectly.
Doug I can't reproduce this, in my Layout I have
@RenderSection("Footer", "Blabla")
And this shows Blabla in the footer.
If I then go to a template that has this layout defined and add @section Footer { Heeey! }
it shows: Heeey!
Maybe you've defined @section Footer
somewhere but forgot to put it in the layout file that's used by that template? This is usually what that error means.
Okay, tried it on the latest nightly and it works. Go figure. Sorry for the false alarm.
Weird! Code hasn't been touched in week :/
Priority: Normal
Type: Feature (request)
State: Fixed
Assignee:
Difficulty: Normal
Category:
Backwards Compatible: True
Fix Submitted:
Affected versions:
Due in version: 6.2.0, 7.0.2
Sprint:
Story Points:
Cycle: