Hi all,
I've been working on a set of Razor templates that is either Site Editable with the Experience Manager and at the same time is fully responsive for several screenwidths with the Twitter bootstrap. As a result, I need to dynamically manipulate the DOM output based on what the Experience Manager editor adds or deletes but still maintain the responsive design. Part of this construction is adhering to the rules the twitter bootstrap dictates.
The twitter bootstrap allows for excellent responsive design by introducing dynamically calculated div widths using classes. A <div class="span12> uses the entire width of the wrapper, for example. A "span6" effectively uses half, with a certain margin calculated as well to allow for another div with "span6". However, this only works if the preceding class is called <div class="row-fluid">,and as long as the span<numbers> actually add up to exactly 12. The problem arises when I need to close the <div class="row-fluid"> when this number is actually reached.
To clarify: it has to output like this
<div class="row-fluid">
<div class="span6">..code</div>
<div class="span4">..code</div>
<div class="span2">..code</div>
</div>
The <div class="span[#]"> are rendered with a component template, in order to allow for multiple components within the <div class="row-fluid>, which the Page Template renders.
However, from a component template level I cannot seem to retrieve the actual amount of components of this template actually exist on the Page itself. I calculate the width of the component used based on a schema option of the component itself. I use the following Component Template code to render it correctly:
#{
var spanClass = String.Empty;
if (Fields.HasField("component_width") && Fields.component_width != null) {
if (Fields.component_width.ToLower() == "full") {
spanClass = "span12";
} else if (Fields.component_width.ToLower() == "half") {
spanClass = "span6";
} else if (Fields.component_width == "40%") {
spanClass = "span5";
} else if (Fields.component_width == "35%") {
spanClass = "span4";
} else if (Fields.component_width == "25%") {
spanClass = "span3";
}
}
}
<div class="#spanClass">...code
To get to my question: I need to be able to close the <row-fluid> div if the number 12 has been reached. So if one component with the options 'Full' (width) is selected, the following output code needs to appear:
<div class="row-fluid">
<div class="span12">..code</div>
</div>
If there are two components on the page with the option "half" are selected, it must allow
<div class="row-fluid">
<div class="span6">..code</div>
<div class="span6">..code</div>
</div>
mind the closing div on the end. Is there some way I can reach the variable i created on the Component Template var spanClass = String.Empty; from Page Template? Something like:
#foreach (var cp in GetComponentPresentationsByTemplate("XM_Generic Content")) {
#if (cp.Component.spanClass == "span6") {
<div class="row-fluid">
#cp.RenderComponentPresentation()
#if (cp.Index == 1) {
</div>
}
}
}
I'm still getting to know Razor templates, the practicalities of Responsive design and ofcourse StackOverflow. Chances are that I completely missed something, made dumb errors in my code of just asked a silly question. By all means, let me know.
The package is not shared between template runtimes, so this behavior is normal (not being able to see variables set in one template from a different instance).
There are ways around this, but you should consider that perhaps there is a good reason why Tridion chose to isolate the template execution.
See here for one of the ways to go around this.
Standard techniques using the ContextVariables dictionary don't allow you to set something in the CT and access it from the PT. Effectively, each time a Component is rendered, the render context gets a fresh copy of the variables from the page render context. Writing back to them, therefore isn't effective. There is a technique that gets round this, which is described in detail on tridion-practice. As already noted, resorting to these kinds of techniques shouldn't be your first option, but sometimes you need to.
Currently, its seems, the user is defining the width position in the component field. I think, its quite typical, but if you create 5 Component Template which will call a same Razor TBB, and also define, a parameter schema on component Template where can set the width of component then afterwards you can easily call these different CTs in the page template.
Related
We have a component that contains a background image. Our front-end guy needs it to be loaded through CSS (i.e. background: url(/*path here*/)...). The following is a possible solution we came up with:
#string src = // Get image path from Sitecore().Field("Picture");
<div style="background: url(#src) left top no-repeat;"> ... </div>
However, there are two problems with this approach:
It makes it very difficult for the content editor to swap out the image. They will have to manually change it through edit item.
It feels like a hack/workaround.
So the question is as follows: Is there a way to edit the CSS of an element through Razor/Sitecore? Specifically, the background: field.
I had a similar case and I used :
<footer class="layout_footer" style="background-color: #Model.BackgroundColor">
on view rendering (cshtml file)
And on the model we have :
public string BackgroundColor
{
get
{
Sitecore.Data.Fields.ImageField imgField =((Sitecore.Data.Fields.ImageField)item.Fields["BackgroundImage"]);
return Sitecore.Resources.Media.MediaManager.GetMediaUrl(imgField.MediaItem);
}
}
For editing this field in page editor you can use Sitecore Field Editor from a command : http://blog.istern.dk/2012/05/21/running-sitecore-field-editor-from-a-command/
Check for edit mode, and display in edit mode a editable field. Also create a Custom Experience Button from the Field Editor Button Type. You can also display. See User friendly developing with the Sitecore Experience Editor
#string src = // Get image path from Sitecore().Field("Picture");
<div style="background: url(#src) left top no-repeat;">
#if (IsInEditingMode)
{
<h3>Backgroiund Picture: #Editable(m => m.Picture)</h3>
}
</div>
There is no Sitecore extension method which will do this out of the box (i.e. #Html.Sitecore().Field("fieldName") will not work here as it would render the entire image tag (also a load of other non-image markup in page editor mode) as you probably know.
The method that #sitecore climber mentions is useful for controller renderings (or view renderings with a custom RenderingModel). If you want to stick with simple view renderings (i.e. not create a RenderingModel) then you could create a Html extension method which can be re-used on any view rendering. This could be something like the following:
public string ImageFieldSrc(this SitecoreHelper sitecoreHelper, string fieldName, Item item = null)
{
if (item == null) {
item = sitecoreHelper.CurrentItem;
}
var imageField = new ImageField(item.Fields[fieldName]);
var mediaItem = imageField.MediaItem;
var mediaUrl = MediaManager.GetMediaUrl(mediaItem);
mediaUrl = HashingUtils.ProtectAssetUrl(mediaUrl); //if you want to use media request protection (adding the hash onto the end of the URL, use this line
return mediaUrl;
}
It's worth noting that if you are using Sitecore 7.5 or above there is a feature to protect media URLs with a hash to prevent malicious DoS type attacks described in this blog post by Adam Najmanowicz.
In summary; if you are using Sitecore 7.5+ and you use media hashing then you will need to call HashingUtils.ProtectAssetUrl on the media URL if it is to respect size parameters.
I want to be able to have one image that loads into static html pages based on a conditional argument; so if X="something" then src="something.jpg", if X="another" then src="another.jpg" and so on.
I can't use a database.
So I am looking for some other technique or method that can use some kind of array and load one image from that array depending on something unique within the page.
I'm guessing that jQuery might do the job or maybe using XML/XSLT but I'm no programmer so any suggestions/guidelines/pointers will be gratefully received :)
If you are willing to use jQuery, you can add the image once the DOM finishes loading.
Add a div tag in your html
<div id="test"></div>
and add the image with your logic using JavaScript
$(document).ready(){
yourLogic = true;
if (yourLogic){
('#test').prepend('<img id="imgId" src="path.png" />')
}else{
('#test').prepend('<img id="imgId" src="someOtherPath.png" />')
}
}
I'm using a taxonomy part in one of my custom content types, and using the shape tracer, I was able to create a custom view template for that control (Fields.Contrib.TaxonomyField.cshtml).
When I shape trace the element on the page, under template it indeed shows my custom template exactly as I have it on the external file:
#using Orchard.Utility.Extensions;
#using Orchard.ContentManagement;
#{
var terms = (IEnumerable<Contrib.Taxonomies.Models.TermPart>)Model.Terms;
string name = #Model.ContentField.Name;
}
#if (Model.Terms.Count > 0) {
#(new HtmlString( string.Join(", ", terms.Select(t => Html.ItemDisplayLink(Html.Encode(t.Name), t.ContentItem ).ToString()).ToArray()) ))
}
<div>TEST TEXT</div>
However, when I tab over to the actual outputted HTML, it is showing the standard, built in template, and isn't using my customizations.
<p class="taxonomy-field">
Coffee Shop
</p>
According to the shape tracer, it is using my custom template:
Shape Fields_Contrib_TaxonomyField
Active Template
~/Themes/Continuum/Views/Fields.Contrib.TaxonomyField.cshtml
Display Type Detail
this feels like a bug... everything looks like it's wired up correctly... is there something else I need to do to use my custom view template for taxonomy?
Many thanks!
I figured out the problem. Turns out there was another template created (likely by accident) which was url-specific:
Fields.Contrib.TaxonomyField-url-venues.cshtml
that's the url I was on, and this had the default template still in it. After deleting it, it finally used my custom template.
User error, sorry!!
I have an array of 2000 items, that I need to display in html - each of the items is placed into a div. Now each of the items can have 6 links to click on for further action. Here is how a single item currently looks:
<div class='b'>
<div class='r'>
<span id='l1' onclick='doSomething(itemId, linkId);'>1</span>
<span id='l2' onclick='doSomething(itemId, linkId);'>2</span>
<span id='l3' onclick='doSomething(itemId, linkId);'>3</span>
<span id='l4' onclick='doSomething(itemId, linkId);'>4</span>
<span id='l5' onclick='doSomething(itemId, linkId);'>5</span>
<span id='l6' onclick='doSomething(itemId, linkId);'>6</span>
</div>
<div class='c'>
some item text
</div>
</div>
Now the problem is with the performance. I am using innerHTML to set the items into a master div on the page. The more html my "single item" contains the longer the DOM takes to add it. I am now trying to reduce the HTML to make it small as possible. Is there a way to render the span's differently without me having to use a single span for each of them? Maybe using jQuery?
First thing you should be doing is attaching the onclick event to the DIV via jQuery or some other framework and let it bubble down so that you can use doSomething to cover all cases and depending on which element you clicked on, you could extract the item ID and link ID. Also do the spans really need IDs? I don't know based on your sample code. Also, maybe instead of loading the link and item IDs on page load, get them via AJAX on a as you need them basis.
My two cents while eating salad for lunch,
nickyt
Update off the top of my head for vikasde . Syntax of this might not be entirely correct. I'm on lunch break.
$(".b").bind( // the class of your div, use an ID , e.g. #someID if you have more than one element with class b
"click",
function(e) { // e is the event object
// do something with $(e.target), like check if it's one of your links and then do something with it.
}
);
If you set the InnerHtml property of a node, the DOM has to interpret your HTML text and convert it into nodes. Essentially, you're running a language interpreter here. More text, more processing time. I suspect (but am not sure) that it would be faster to create actual DOM element nodes, with all requisite nesting of contents, and hook those to the containing node. Your "InnerHTML" solution is doing the same thing under the covers but also the additional work of making sense of your text.
I also second the suggestion of someone else who said it might be more economical to build all this content on the server rather than in the client via JS.
Finally, I think you can eliminate much of the content of your spans. You don't need an ID, you don't need arguments in your onclick(). Call a JS function which will figure out which node it's called from, go up one node to find the containing div and perhaps loop down the contained nodes and/or look at the text to figure out which item within a div it should be responding to. You can make the onclick handler do a whole lot of work - this work only gets done once, at mouse click time, and will not be multiplied by 2000x something. It will not take a perceptible amount of user time.
John Resig wrote a blog on documentDragments http://ejohn.org/blog/dom-documentfragments/
My suggestion is to create a documentDragment for each row and append that to the DOM as you create it. A timeout wrapping each appendChild may help if there is any hanging from the browser
function addRow(row) {
var fragment = document.createDocumentFragment();
var div = document.createElement('div');
div.addAttribute('class', 'b');
fragment.appendChild(div);
div.innerHtml = "<div>what ever you want in each row</div>";
// setting a timeout of zero will allow the browser to intersperse the action of attaching to the dom with other things so that the delay isn't so noticable
window.setTimeout(function() {
document.body.appendChild(div);
}, 0);
};
hope that helps
One other problem is that there's too much stuff on the page for your browser to handle gracefully. I'm not sure if the page's design permits this, but how about putting those 2000 lines into a DIV with a fixed size and overflow: auto so the user gets a scrollable window in the page?
It's not what I'd prefer as a user, but if it fixes the cursor weirdness it might be an acceptable workaround.
Yet Another Solution
...to the "too much stuff on the page" problem:
(please let me know when you get sick and tired of these suggestions!)
If you have the option of using an embedded object, say a Java Applet (my personal preference but most people won't touch it) or JavaFX or Flash or Silverlight or...
then you could display all that funky data in that technology, embedded into your browser page. The contents of the page wouldn't be any of the browser's business and hence it wouldn't choke up on you.
Apart from the load time for Java or whatever, this could be transparent and invisible to the user, i.e. it's (almost) possible to do this so the text appears to be displayed on the page just as if it were directly in the HTML.
I have a block of HTML that I would like to use as the basis of a GWT widget, but am not quite sure the best way to do so. As an example, my HTML block looks something like this:
<div class="my-widget">
<div class="header">
<span class="title">Title Text</span>
</div>
<div class="body">
<span class="content">Content Text</span>
</div>
</div>
Now, I can of course paste this as a static string into an HTML widget, but in this case I need the ability to set the text of the "title" and "content" elements on the fly. This kills (or at least makes significantly more difficult) the static text option.
The first thing that comes to mind in that case is to build out the elements in GWT manually and hold references to the ones I need to alter, like so:
DivElement container = document.createDivElement();
setStyleName(container, "my-widget");
setElement(container);
DivElement header = document.createDivElement();
setStyleName(header, "header");
container.appendChild(header);
// Hold onto this element for later manipulation
DivElement title = document.createDivElement();
setStyleName(title, "title");
header.appendChild(title);
But this quickly get unmanageable for all but the simplest of layouts (which mine is not.)
What I would like is the ability to send the HTML in as static text and then do some sort of selector, like jQuery, to query the elements I want to manipulate. I'm aware of GWT-Query but I haven't been able to get it to run without error, and it seems to me to be a bit too early in it's lifespan for me to be comfortable integrating it into a professional product just yet.
I'm also aware of Google's UiBinder, which sounds exactly like what I want. The problem there is, as far as I can tell, that functionality is only available in GWT 2.0, which is still in a release candidate state and therefore unusable for me.
So, given all that (sorry for the long question!) do you have any suggestions about how best to achieve something like this?
GWT 2.0 will be out before the end of the year. So unless you need to deliver in a few days time, I would start working with the RC2 and try out the new UIBinder approach.
How about using HTML.wrap(). For example, if you added an id of "my-widget" to your outer-most div you could then do something like:
HTML myWidget = HTML.wrap(RootPanel.get("my-widget").getElement());
You can use the InlineHTML widget to produce a span and control its content.
As you probably know, GWT doesn't provide a built in widget that maps directly to a span element. If you can use a div for the title and content, then this bit of code should (no GWT on this machine, going a bit by memory) generate the DOM structure you have.
FlowPanel myWidget = new FlowPanel();
myWidget.setStyleName("my-widget");
SimplePanel header = new SimplePanel();
header.setStyleName("header");
Label title = new Label(titleText);
title.setStyleName("title");
header.add(title);
myWidget.add(header);
SimplePanel body = new SimplePanel();
body.setStyleName("body");
Label content = new Label(contentText);
content.setStyleName("content");
body.add(content);
myWidget.add(body);
From here, you can provide accessors to the content and title labels and update them as needed.
title.setText(newTitle);
content.setText(newContent);
The same as above, but getting a GWT Panel (be able to append childs). This is also useful when you need to wrapp a 3rd party toolkit GUI object as a GWT widget:
Panel gwtPanel = HTMLPanel.wrap(anElement);
The following code suites for me:
HorizontalPanel divContainer = new HorizontalPanel ();
Element div = DOM.createDiv();
div.appendChild(...); // whatever element it could fit inside
divContainer.getElement().appendChild(div);
Edit
The idea behind this is manipulating the DOM with the other widgets you have already programmed.
PS: I'm using GWT 2.4