Showing posts with label variants. Show all posts
Showing posts with label variants. Show all posts

Friday, February 28, 2020

SXA date variant and language fallback

Sitecore SXA DateVariantField

Note: this post assumes you know what a SXA variant is - if not, please read the documentation first.

Within a SXA variant, you can use a DateVariantField "Date" to display date and time from a date field in a chosen format. This definition is similar to the Field variant, but has an additional field "Date format" that allows you to choose a date and time format.





The date formats are stored in /sitecore/system/Settings/Foundation/Experience Accelerator/Rendering Variants/Enums/Date Formats as items (with display names).

On https://sitecore.stackexchange.com/a/8489/237 Alan explains how this works together with cultures and such. In short: SXA will render the date in the current culture with the given format.

We have a project with a few languages and language fallback is enabled with English as fallback for all other languages. Our date variants worked perfectly in Sitecore 9.0.2 with SXA 1.7.1 but after our upgrade to Sitecore 9.2 and SXA 1.9 we saw something weird...

Date variants and language fallback

So we have a site with language fallback enabled and it seems to work fine. But then we noticed weird date formats in our date variants when switching languages - although we had the format set to dd/MM/yyyy we did see a time value:


In English everything was still ok.

The workaround

First thing to check is language fallback is actually enabled on the site.

Note that since SXA 1.9 there is a checkbox to enable item language fallback. As this is an upgraded site, we also still have the fallback setting in the Other properties field, just to be very sure...



The workaround for the issue that I solved it for me was:
  1. Create language versions for /sitecore/system/Settings/Foundation/Experience Accelerator/Rendering Variants/Enums/Date Formats/date2 (which is the dd/MM/yyyy item) and give them a display name just like the English version. Repeat for every format you want to use.
  2. Remove the standard value from the variants format field. Save. Then select the dd/MM/yyyy again (this time actually selected and not as standard value). 
  3. Create language versions for the variant item.
Not all steps might be needed - Support and myself had some different conclusions on that.. and I think caching was also involved. Step 2 however is certainly needed - with a standard value it won't work (bug listed with reference number 119290)

But with this workaround all was fine again.



Wednesday, April 10, 2019

Sitecore SXA custom rendering variant field for translations

The requirement

We had to display a field (from the datasource item) and some fixed labels in a html structure.

So for a first version we just created the rendering variant and added Field and Text variants to the definition. This works..  but in our multilingual environment we have to translate the labels. This can be done as the values are not shared, but I started thinking.. 
do I want content editors that translate labels changing my rendering variants?
And the answer was no..

To fix that, I created my own variant definition to support translated labels.

 Creating a custom variant field

Template

First thing to do is create a template to define the fields of the variant. In our case that was very easy as I just wanted to create an enhanced version of "Text", so I used that template (/sitecore/templates/Foundation/Experience Accelerator/Rendering Variants/VariantText) as base template and added nothing else.

If you have another use case to create a custom variant field, check the existing ones to see what base templates you need. A good start will be: 
  • /sitecore/templates/Foundation/Experience Accelerator/Rendering Variants/Base/_Rendering Variants Base
  • /sitecore/templates/Foundation/Experience Accelerator/Rendering Variants/Base/_Data Attributes
  • /sitecore/templates/Foundation/Experience Accelerator/Rendering Variants/Base/_Link Attributes
  • /sitecore/templates/Foundation/Experience Accelerator/Variants/Base Variant Field
Remember not to place your template in the SXA folders! Use your own folder structure...

Insert Options

You want your field to be available in the insert options when people create a variant definition. The best practices tell us not to change the SXA templates, so we won't do that. We'll use the rules engine to add the insert options!

Go to /sitecore/system/Settings/Rules/Insert Options/Rules and add your insert rule:
You need to check the item template to detect the templates you want to add the insert option to (the variant definition itself and all rendering variant templates in my case), and add your template as insert option.

Corey Smith wrote an excellent blog post on organizing your insert options - a good idea, you should read this ;)

The code

First class we need is a model for your template. It should inherit from Sitecore.XA.Foundation.RenderingVariants.Fields.RenderingVariantFieldBase, but as we are building upon VariantText I inherited from that one (in the same namespace).
public class DictionaryText : VariantText
{
  public DictionaryText(Item variantItem) : base(variantItem)
  {
  }

  public static new string DisplayName => "Dictionary Text";
}

Creating instances of this DictionaryText class is done with a processor class that inherits from Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.ParseVariantFields.ParseVariantFieldProcessor.  In that class you need to define the supported template (the guid of the template you created earlier) and the TranslateField function that translates the variant field arguments to the model class. I checked the VariantText implementation (as we have the same model):
public class ParseDictionaryText : ParseVariantFieldProcessor
{
  public override ID SupportedTemplateId => new ID("{AAD9B54E-C7B2-4193-975E-954C0AD5A922}");

  public override void TranslateField(ParseVariantFieldArgs args)
  {
    var variantFieldArgs = args;
    var variantText = new DictionaryText(args.VariantItem);
    variantText.ItemName = args.VariantItem.Name;
    variantText.Text = args.VariantItem[Sitecore.XA.Foundation.RenderingVariants.Templates.VariantText.Fields.Text];
    variantText.Tag = args.VariantItem.Fields[Sitecore.XA.Foundation.RenderingVariants.Templates.VariantText.Fields.Tag].GetEnumValue();
    variantText.IsLink = args.VariantItem[Sitecore.XA.Foundation.RenderingVariants.Templates.VariantText.Fields.IsLink] == "1";
    variantText.LinkField = args.VariantRootItem[Sitecore.XA.Foundation.Variants.Abstractions.Templates.IVariantDefinition.Fields.LinkField];
    variantText.CssClass = args.VariantItem[Sitecore.XA.Foundation.RenderingVariants.Templates.VariantText.Fields.CssClass];
    variantFieldArgs.TranslatedField = variantText;
  }
}
Note that the ID in this example is the ID of my template - you will probably have another one ;)

Next step is to create the processor that actually renders the variant. Create a class that inherits from Sitecore.XA.Foundation.Variants.Abstractions.Pipelines.RenderVariantField.RenderRenderingVariantFieldProcessor. You need to set a few properties to define the supported template and the render mode (html/json) but the main part is the RenderField function.
In this RenderField you can add any logic you want and render the output. In our case, we stay very close to the code from the ootb VariantText but just translate the value inside:
public class RenderDictionaryText : RenderRenderingVariantFieldProcessor
{
  public override Type SupportedType => typeof(DictionaryText);

  public override RendererMode RendererMode => RendererMode.Html;

  public override void RenderField(RenderVariantFieldArgs args)
  {
    if (!(args.VariantField is DictionaryText variantField))
    {
      return;
    }

    var dictionaryRepository = ServiceLocator.ServiceProvider.GetService<IDictionaryRepository>();
    var control = (Control)new LiteralControl(dictionaryRepository.GetValue(variantField.Text));
    if (variantField.IsLink)
    {
      control = InsertHyperLink(control, args.Item, variantField.LinkAttributes, variantField.LinkField, false, args.HrefOverrideFunc);
    }

    if (!string.IsNullOrWhiteSpace(variantField.Tag))
    {
      var tag = new HtmlGenericControl(variantField.Tag);
      AddClass(tag, variantField.CssClass);
      AddWrapperDataAttributes(variantField, args, tag);
      MoveControl(control, tag);
      control = tag;
    }

    args.ResultControl = control;
    args.Result = RenderControl(args.ResultControl);
  }
}
Note that I am using a custom DictionaryRepository here.. you'll need to change those two lines of code with your own code to translate the text.

Configuration
Last step is adding the processors to the correct pipelines in a config patch - e.g.:
<sitecore>
  <pipelines>
    <parseVariantFields>
      <processor type="Foundation.Dictionary.RenderingVariants.ParseDictionaryText, Foundation.Dictionary" resolve="true"/>
    </parseVariantFields>
    <renderVariantField>
      <processor type="Foundation.Dictionary.RenderingVariants.RenderDictionaryText, Foundation.Dictionary" resolve="true"/>
    </renderVariantField>
  </pipelines>
</sitecore>


Conclusion

My editors are happy. They can change and translate all labels as they are used to and don't need to think about the variants.

My developers are happy because the editors don't mess around in the variants just to translate a label.

And I did not have to spend so much time doing this...  but it seemed worth to share the idea (and the code).

Monday, March 11, 2019

Working with external model data in Sitecore SXA variants

SXA and variant definitions

If you are working with SXA, I assume you heard about rendering variants (if not, you're doing it wrong).  There is some documentation on the options you get when creating your own rendering variant: https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/create-a-rendering-variant.html

There is one variant definition option that I would like to highlight here, and that is the "Model".
Model: displays properties from the current Model. The SXA renderings model inherits from Sitecore.XA.Foundation.Mvc.Models.RenderingModelBase. In the Property path field, you can enter the path to the property that you want to display.

 You can use this Model field to display any data you want. I used it to display data from a custom database, but it could also be data from a webservice or any datasource you like. How to do this..

Create your (custom) model

You should start by creating your own component: https://doc.sitecore.com/developers/sxa/18/sitecore-experience-accelerator/en/create-a-new-sxa-module.html  (or copy an existing one and change the data, but that is another topic). What you need is a custom controller, model and probably also a view. Create the necessary items in Sitecore and create your variant definition.

To write the needed code, you can refer to my blog article on custom renderings with variants or to the official docs. The important part here is actually the model class. All (custom) properties you add there will be available in the variants. Fill the data in your repository (called from the controller).
Suppose we have this model:
public class CustomModel: VariantsRenderingModel
{
  public string CustomProperty { get; set; }
  public CustomClass CustomClassProperty { get; set; }
}
and we would like to show the CustomProperty value and a property from the CustomClassProperty as well. Let's create our variant...

Create the variant definition 

In the variant definition that was created for your custom component, you can add a "Model" field.
You can select a tag (just as in other variant fields) and set a css class or other attributes. The Property Path is the really interesting one: here you can define the property from your Model you want to show. As you can see in the screenshots, you just need to add the name of the property as it is in your model class. And you can also get properties from a property..  (like the "Name" property from the CustomClass that was a property of my model).

You will notice that the variant field does not have all properties like a "Field" variant field - e.g. you cannot create a link (which makes sense of course). 

Model data

I am struggling with a few things still..  example: my model has a url and I would like to show that in the variant..  but I can only show the value of the model property - I can't seem to embed this in a "a href..".  I guess there will always be such edge cases that go just that one step further than what is possible ootb. I could go for a quick fix and have my model give me the html for the link..  and if I find a cleaner solution, I'll update the blog :)

Conclusion

Displaying all sorts of external data with variant in SXA was actually easier than expected. Not the first time the product has amazed me in a good way 😃



Monday, October 22, 2018

Using tokens in Sitecore SXA variant data attributes

Sitecore SXA variant definitions


Variants are an important part of SXA. Although SXA is so much more, learning how to use these is rather crucial for anyone who want to create sites with SXA. If you need more information on this, a good place to start are these official docs:
You can do a lot with these variants. Especially when you discover the power of the "Reference" in a variant definition, lots of scenario's become possible...  and there are some hidden gems. 

Our case

We had to create a rendering that displayed blocks of content - so far no problem, this is just displaying fields from a list of items. But the editor had to be able to set a background on each individual block (and some other properties). Short version: we had to be able to set a css class on every block. We could have asked our editors to create a rendering for each block they needed and set parameters on that rendering, but as we had reasons not to do that.

So we started looking at the variant definitions options, tried a few things without luck and ended up asking help on Stack Exchange: https://sitecore.stackexchange.com/q/12806/237

As usual, the SXA dev team came to the rescue (- thx Dawid for this one) and showed me a little hidden gem.

Using tokens in data attributes

Apparently you can use some tokens in the data attrbutes that are available in each part of the variant definition. As shown in the answer on SSE, we can use "class" as data attribute and fill that with a value from a field on the current item by using the $(fieldName) notation.

This means we can use a Reference in our variant to loop over a set of items, and for each of those items render a section (e.g. "div") with a class on it. That'll do it..

But.. we had one small issue. The default implementation will work with text fields. But for link-type fields it will generate a link to the target item. We don't want our editors to type values for fields like this - it should be a selection. So we got a patch from Support that extended this functionality to ValueLookupFields to fetch the value.

Now we could give our editors a dropdown selection of styles and use those in our variant. That's what we needed! (as soon as the site goes live, I can add a screenshot of the result)

I had a look at what Support provided for us and although it is not designed to be expanded easily, we can do even more with this (and so we did).

public class RenderSection : Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection
{
 protected override string GetAttributeTokenValue(string fieldName, Item item)
 {
   Field field = item.Fields[fieldName];
   if (field == null)
  return string.Empty;
   ....
   return base.GetAttributeTokenValue(fieldName, item);
 }
}
<sitecore>
    <pipelines>
      <renderVariantField>
        <processor patch:instead="*[@type='Sitecore.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection, Sitecore.XA.Foundation.RenderingVariants']" type="Sitecore.Support.XA.Foundation.RenderingVariants.Pipelines.RenderVariantField.RenderSection, Sitecore.Support.00000" resolve="true" />
      </renderVariantField>
    </pipelines>
</sitecore>

We noticed they had overwritten the function that handles the tokens in case of a variant section (if you want this to work on other variant options like text or field, you need to overwrite those too). Where Sitecore/SXA is designed to be extended, they usually make it easier for us :)

So as mentioned, it is not (yet) designed to be expanded but it can. You could write any logic you want in the above funtion based on the "fieldName" (which propable doesn't even need to be an existing field name).

This opens a new set of possibilities to render everything we need with SXA variants!

Just one little request for a future version:  make this easier to extend.. ;)

Thursday, March 23, 2017

Sitecore SXA: using variants to create a list with images and links

A small post with an example on how to tweak SXA to get what you need without any code..


The requirement

We had to create a list of images that had a link on them to an internal or external page.
We couldn't use the default "Link List", as that had no image options..  We couldn't use the "Gallery" as that has no links..

Our solution

Template

We started by creating a new template for datasource items, inheriting from the existing "Link" template (/sitecore/templates/Feature/Experience Accelerator/Navigation/Datasource/Link) in the Navigation feature.


We called it ImageLink and added one field: Image (placed it in the Link section so it would be combined with the existing Link field on our base -Link- template. 

Variant definition

For the Link List component, we added a new Variant definition.

We called it "Imaged" and added just one VariantField for the image with properties:
  • Field name: "Image"
  • Is link: checked 
In order to get the link using the link field as source, we filled in the name of the field containing the link ("Link") in the field "Field used as link target" on the variant definition itself.

Don't forget to select the allowed templates in the variant definition ;)

Link List variant

Now we can add a Link List on our page and select the "Imaged" variant. And that is it actually.. save, publish, ...  and magic!
Here is our output:


A list with links and images - just what we needed. We can also use the (optional) title from the Link List and other inherited features.

And we didn't write any code. 

Scary...

Thursday, March 9, 2017

Variants on your custom Sitecore SXA renderings

Sitecore Hackathon 2017

One of the possible ideas of the 2017 Sitecore Hackathon was the Sitecore Experience Accelerator (SXA). With our "No Weekend 4 Us" team we took this challenge and created a custom component that works with SXA Variants (and did other fancy stuff, but that will be explained in another blog post). [Using SXA 1.2 on Sitecore 8.2-upd2]

Sitecore Experience Accelerator Variants

SXA comes with a set of default renderings and rendering variants. Rendering variants are configurable adaptations of the default renderings. To further encourage reusability, you can also create new rendering variants. This gives authors more options in the way they present their content.
Info on creating variants can be found on the SXA dev documentation, but what if you create a custom component for SXA and want to use this technology as well. I'll try to provide a step by step overview of the things to do. Our component was about blogs, so you might find that in the code examples...

Variant model repository

We created a repository based on the VariantsRepository of SXA:
using Sitecore.XA.Foundation.RenderingVariants.Repositories;

public class BlogVariantRepository : VariantsRepository, IBlogVariantRepository
{
  public T GetModel<T>(object model) where T : IRenderingModelBase
  {
    FillBaseProperties(model);
    return (T)model;
  }
}
The reason to do this is to get a variants-aware model later on. The repository has an interface, so we will inject it with dependency injection. We will do this the SXA way, by using their <ioc> pipeline.

Dependency Injection

<pipelines>
  <ioc>
    <processor type="X.Features.Blog.Pipelines.IoC.RegisterBlogServices, X.Features.Blog" />
  </ioc>
</pipelines>
public class RegisterBlogServices : IocProcessor
{
  public override void Process(IocArgs args)
  {
    args.ServiceCollection.AddTransient<IBlogVariantRepository, BlogVariantRepository>();
    ...
  }
}
We are using build-in DI of Sitecore 8.2 as used by SXA this way. It's actually a very easy way to insert your stuff to the container. As an extra there is no need to register your controller - SXA will take care of that for you.

Model - View - Controller


Model

Let's start with the model. 
public class BlogsModel : VariantsRenderingModel
{
    ..  // custom properties
}
The model inherits from the VariantsRenderingModel. This base class provides some interesting (and needed) properties. It actually is the RenderingModelBase (with info on Item, Rendering ...) extended with VariantFields. We will use the model repository we created earlier to fill the properties of this base class. Of course, it can be extended with any properties and function.

Controller

public class BlogsController : VariantsController
{
  private readonly IBlogVariantRepository blogVariantRepository;
  ...

  public BlogsController(IBlogVariantRepository blogVariantRepository, ...)
  {
    this.blogVariantRepository = blogVariantRepository;
    ...
  }

  public ActionResult BlogList()
  {
    var model = blogVariantRepository.GetModel<BlogsModel>(new BlogsModel());
    model.X = ...;
    return View(model);
   }
}
The controller inherits from VariantsController. Our repository is injected as explained earlier. We call the GetModel on our repository to get our base model with filled base properties. We can set values for other custom properties on the retrieved model object.

View

<div @Html.Sxa().Component("bloglist", Model.Attributes)>
  <div class="component-content">
    <div class="blog-list">
      @foreach (var item in Model.Blogs)
      {
        <div data-itemid="@item.Blog.ID.Guid">
        @foreach (var variantField in Model.VariantFields)
        {
          @Html.RenderingVariants().RenderVariant(variantField, item.Blog, false)
        }
        </div>
      }
    </div>
  </div>
</div>

This is just an example of what might be possible in the view - a bit more bound to the model we used as a test (with a list of blogs and each blog object included the underlying Sitecore item).

You can see here that we loop over all variant fields and simply display them. This means of course that we need a default variant to show anything.

That's it for the code.. now lets move on to Sitecore.

Sitecore (SXA) configuration

Rendering parameters template

First step is to create a rendering parameters template to use for your rendering. The template should inherit from /sitecore/templates/Foundation/Experience Accelerator/Rendering Variants/Rendering Parameters/IComponentVariant (don't forget to also inherit /sitecore/templates/System/Layout/Rendering Parameters/Standard Rendering Parameters instead of the Standard Template).

Using this base template will give you the variants dropdown in the rendering properties. By the way, SXA has a similar base template for Styling (/sitecore/templates/Foundation/Experience Accelerator/Presentation/Rendering Parameters/IStyling).








Rendering

In /sitecore/layout/Renderings/Feature/Experience Accelerator/[Feature-Name]/[Rendering-Name] create a Controller Rendering and fill in the usual fields (Controller, Action ..). And a tip to make it complete: give your rendering an icon ;)

To make the rendering available in the toolbox, go to the Presentation part of the SXA website (will be on the same level as the Home page). Under Available Renderings create a new Available Renderings item (might want to name if like your feature again). Add your newly created rendering in the "Renderings" box. Now we have a rendering in the toolbox - lets continue by creating the variants we need.

Variants

Variants are also defined in the "Presentation" section of your site. Under Rendering Variants create a new Item of type Variants and name it exactly as your rendering (important!). Under this Variants, create a Variant Definition. You might want to call the first one "default". Under the Variant Definition, create an Item of type Field. Choose a tag (e.g. "div"), fill in the Field name and set the other fields as desired (e.g. check "Is link" if you want a link to be generated on the field). Continue creating "Fields" as much as you want. And that is it. When you add the rendering from the Toolbox, you will see your variant in the dropdown and all the fields you created will be rendered. 

To make it really useful, you might want to create more Variant Definitions with different sets of Fields. This way you can easily create different outputs of the same rendering (and even more if you combine this with Styling).

More information on what you can do with the variants and their properties in the link mentioned above on the Sitecore doc site.

ps: thx to Dawid Rutkowski for pointing us in the right direction!