Using AngularJS directives in Sitecore edit mode

It is quite a standard approach that you can see in many CMS project now – designs, UX, and HTML is done upfront or way in advance in comparison with CMS development. You might think now about Agile and say that all done almost simultaneously within one iteration, but even in this case, you would need to have HTML done before it would be converted into your Sitecore rendering.

In addition to that, there is a huge shift towards front-end technologies which affect Sitecore projects as well. Being a big advocate of a full stack developers that are knowledgeable enough in both Sitecore and front-end technologies used, I still see a lot of cases when Sitecore and front-end code are done by different developers with completely different skill sets.

CONTEXT

All of the above creates a problem of integration of deliverables and maintenance from Sitecore and front-end developers during a project timeline:

  • double work of updating HTML and Sitecore views
  • update HTML directly in rendering or in HTML repository and ask Sitecore developer to add his changes
  • sometimes work even done in separate repositories which make versioning and branching look like “mission impossible”
  • testability of a front-end code isn’t great and not always make sense if errors might be introduced during copying of HTML to renderings.

To address those issues there should be a better way of front-end code integration into Sitecore.

APPROACH

First of all, there should not be a situation when an HTML/JS code is managed separately from back-end code. There should be one repository for JS, CSS or its pre-processors, HTML, and Sitecore code.

Secondly, all of it should be automatically compiled and processed, moved to your web root.

Finally, front-end code should be reused as is in Sitecore and I’m not talking only about CSS and JS but also about HTML.

While the first point doesn’t require explanations to my mind and the second I would leave for a separate post. The last one is what would like to talk about here.

Component approach

In general, an idea is not new, it’s been around for a while (I assume you’ve heard about COM technology for example). The main reason of componentization is a separation of concerns, to create self-contained independent black boxes with a well-defined interface for communication that could be reused: packages, modules, especially popular now microservices are all driven by this idea.

The most popular and innovating front-end frameworks like ReactJS, AngularJS, and Polymer introduce components in different ways as well: React components, directives in Angular and web components in Polymer.

As Angular is something that I’m working with now, let’s see how we could use its directives.

Implementation in AngularJS

Angular was designed to support modularity and is built in modules and a dependency injection. Modules in AngularJS contains different parts of an application including controllers, directives and etc. Components functionality might need any of these parts as in complex application you would have shared functionality, access to data, formatting and it would be logical to combine them in one module. One module is created it could be injected as a dependency into the main application, also it would be possible to test in independently.

To keep it simple we do not need a complex logic, so the module would contain a very few things

angular.module('sc.sample', [
  'ngSanitize'
]).directive('sampleComponent', function () {
  return {
    scope: {data: '=sampleComponent'},
    templateUrl: '/sc/sample/sampleComponent.html'
  };
}).controller('SampleDemoController', function ($scope) {
  $scope.data = {
    Title: 'Some test title',
    Text: 'Some long-long text goes here',
    Image: '<img src="/sc/sample/sample-image.jpg" alt="meaningful text here"/>'
  };
}).filter('asTrusted', ['$sce', function($sce) {
  return function(text) {
    return $sce.trustAsHtml(text);
  };
}])

The module itself – it references all required dependencies for the component.

Directive – it lets us extend an HTML and attach component behavior to our page. It would also bind Sitecore data to HTML template (see code snippet below) via ng-bindhtml directive. This is required as a field might be rich text or image and Sitecore would generate HTML as an output, also a lot of HTML would be generated in CMS editing mode.

<section class="row page-header sampleComponent spaced-row" ng-show="data">
<div class="col-xs-12">
<h1 class="header" ng-bind-html="data.Title | asTrusted"></h1>
</div>
<article class="col-md-8">
<p ng-bind-html="data.Text | asTrusted"></p>

</article>
<figure class="col-md-4">
<p ng-bind-html="data.Image | asTrusted"></p>

</figure>
</section>

Filter – this filter will ensure that it is possible to output raw HTML via Angular. If you would not use it, a markup would be correct for a live site but your EE experience would be screwed, as Sitecore is not using valid HTML markup in EE (it wraps other components with spans e.g.)$sce.trustAsHtml(text) helps to prevent the markup validation and fixing.

Implementation in Sitecore

Once we have Angular components wired up we need to use them in Sitecore. We could use view or controller rendering for this, in both cases, we would need them only to init angular component as all presentation code and logic would be in it.

<div sample-component='{
Title: "@Html.RawJsEncodedString(Editable(Model, t => t.Title))",
Text: "@Html.RawJsEncodedString(Editable(Model, t => t.Text))",
Image: "@Html.RawJsEncodedString(Editable(Model, t => t.Image))"
}'></div>

This would output JS object into a directive attribute. HtmlExtention method RawJsEncodedString would ensure that MVC would not encode an output as well as all quotes are encoded for JavaScript.

public static IHtmlString RawJsEncodedString<T>(this HtmlHelper<T> htmlHelper, string str)
{
  if (str.IsEmptyOrNull())
  {
    return new HtmlString(string.Empty);
  }
  return new HtmlString(HttpUtility.JavaScriptStringEncode(str));
}

Once you put all this together, Angular will load your component in preview mode or on a live site, as well as in Experience Editor mode. In later mode during server rendering your component would be wrapped in chrome data tags required to show CMS controls (a component selection and other buttons), while Editable method generates an editing markup for each field and it would be rendered via Angular binding.

Strength & Weaknesses

Proc

  • This approach helps to improve componentization, testability, and separation of concerns in complex applications with heavy front-end frameworks usage.
  • It would also force code reuse and reduce an amount of double work, as Sitecore developers would not need to duplicate HTML generated by front-end and HTML markup would be maintained together with an application going forward.

Cons

  • SEO might be a concern in this approach, as page content would be rendered by Javascript and it isn’t something that crawlers really like. However, there is something you could do to improve SEO.
  • Also adding Edit Frames might be tricky as it would be difficult to pass in a directive component that should wrap one.

Follow me on twitter @true_shoorik. Would be glad to discuss ideas above in comments.

Using AngularJS directives in Sitecore edit mode

11 thoughts on “Using AngularJS directives in Sitecore edit mode

  1. I am trying to test it first and putting it all into one cshtml file, and I am having problems with the Editable field (Title: “@Html.RawJsEncodedString(Editable(Model, t => t.Title))”,)

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s