The Theory

Let’s say, you want to develop a new web application. An important step in your technical conception is the choice of an UI – library. Beside SAPUI5, there are a vast number of great UI frameworks and technologies, so it’s quite complicated to keep an overview.

But almost all of these frameworks have conceptual insufficiencies, especially when you want to develop encapsulated, re-usable elements. Just have a look at a part of the HTML markup of the SAPUI5 ListBox control:

/wp-content/uploads/2015/06/list_box_markup_728128.png

As you can see, all single elements of the control are exposed. This behavior leads to the following problems:

  • Details of the implementation are leaking.
  • Global stylesheets can affect the look and feel of the control.
  • When you make a query for “ul” or “li” elements (e.g. document.querySelectorAll(“li”)), the mentioned markup is now part of the result.

Besides, the SAPUI5 ListBox is only re-usable within a SAPUI5 application. When you create an application with e.g. angular.js, it’s not possible to integrate a single control of the SAPUI5 library. Instead, you need to include at least the core framework to use its controls. The actual problem is, that nearly all UI – frameworks use different approaches for control development and implementation. They don’t use a common standard for components. And that means, they don’t work together.


This is where Web Components comes in. The following graphic illustrates the role of this new technology.


/wp-content/uploads/2015/06/web_components_728129.png


The layer „Web Components“ provides real encapsulated, re-usable building blocks. It’s the new basis for frameworks and applications.


The Practise


Enough theory! Let’s have a look at the code. Web Components consists of these four technologies (although each can be used separately):

  • HTML Imports
  • HTML Templates
  • Custom Elements
  • Shadow DOM

I’m going to show you in this post, how to create a simple Web Component. All what you need is a text editor, a Web Server and Google Chrome. At first, create a new HTML site with the following markup in your web servers root or project directory.


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Web Components</title>
<style type="text/css">
a{
 color: #0f0f0f;
}
</style>
</head>
<body>
 <a href="#">Standard Link</a>
</body>
</html>

When you look at this site in your Chrome, you will see a simple link. Now, our goal is to develop a custom button element, which uses internally an “a” element. The result is attached at the post.

HTML Imports

HTML Imports are something like a packaging mechanism for Web Components. When you want to use a Web Component in your HTML-site, just import it! Create the HTML-File “custom-button.html” and refer to it from your index.html. Place this code at the end of your “head” section.



<link rel="import" href="custom-button.html">


We are going to implement the component-related markup and coding in our “custom-button.html”.

HTML Templates

Of course, we want to render some HTML and Styles for our element. To do this, we place our markup and CSS into the new HTML template element. It’s important to know, that content in this tag will not be rendered when the page is loaded. The application itself needs to do this with some additional steps via JavaScript. Switch to the “custom-button.html” and add the following markup:


<template id="custom-button-template">
  <style>
  a{
      display: inline-block;
      border-radius: 2px;
      padding: 10px 25px;
      color: #fff;
      background-color: #6083c2;
      text-decoration: none;
      transition: background-color .25s;
  }
  a:hover{
  background-color: #345288;
  }
  </style>
  <a></a>
</template>

Custom Elements

We also want to create our own custom HTML tags and elements. Besides, we need something like lifecycle callbacks, so we can execute coding in different states of the element. “Custom Elements” provides us these features. The following callbacks are currently available (see MDN):

  • createdCallback: The behavior you define occurs when the element is registered.
  • attachedCallback:The behavior occurs when the element is inserted into the DOM.
  • detachedCallback: The behavior occurs when the element is removed from the DOM.
  • attributeChangedCallback: The behavior occurs when an attribute of the element is added, changed, or removed

Let’s place a new custom element in our index.html (beneath the “a” element) and add some attributes:


<custom-button text="Click me!" href="#"></custom-button>

When you define a custom element, you must always use a hyphen in the name of the control! After that, we need a little bit JavaScript to describe our element and register it. In your “custom-button.html” paste the following coding beneath the “template” section.


<script type="text/javascript">
(function() {
  // get a reference to the document element of the current script.
  // we need this later in our example
  var importDoc = document.currentScript.ownerDocument;
  // create a prototype for the new element.
// basis is the common HTMLElement Interface
  var CustomButtonProto = Object.create(HTMLElement.prototype);
  // implement a lifecycle callback
  CustomButtonProto.createdCallback = function(){
       // todo
  };
  // registers the new custom element in the browser
  document.registerElement("custom-button", {prototype: CustomButtonProto});
  })();
</script>

This is one possible approach for creating and registering a custom element. You can create the prototype of your new element in different ways. But i think, this one is pretty clear. Besides, it’s also possible to base a custom element on a native element like “a”. In other words, you can inherit from native controls. Such an element has a different tag syntax and looks like this <a is=”my-anchor”>.

Shadow DOM

Shadow DOM is the key for encapsulation the JavaScript, CSS and templating in a Web Component. With this technology, we are able to separate our component specific code from the rest of the page. Let’s see, how it works. This is the implementation of our createdCallback method for the custom element.




CustomButtonProto.createdCallback = function(){
  // select the template element
 var template = importDoc.querySelector("#custom-button-template");
  // get the attributes of our custom element and transfer the values
  // in our template
  template.content.querySelector("a").innerHTML = this.getAttribute("text");
  template.content.querySelector("a").href = this.getAttribute("href");
  // create a copy of the templates content, so we can place it in the current document
 var clone = document.importNode(template.content, true);
  // insert the content to the existing element(this reference) via shadom dom
 this.createShadowRoot().appendChild(clone);
  }

Now we are done. When you open the index.html in your browser and inspect the markup of your custom element, you should see something like this:

/wp-content/uploads/2015/06/shadow_dom_728130.png

The section „#shadow-root“ proves, that the implementation of our control is encapsulated.

  • The internal style of the “a” element does not affect the “a” element in the index.html.
  • Changing the global style of “a” elements won’t affect out custom control.
  • Performing document.querySelectorAll(“a”) in your browser console won’t return the internal “a” element of your custom button.

Because our custom element is nothing more than an ordinary HTMLElement, we can treat it like a normal element. So you can add eventlisteners, query it etc.

It’s important to understand, that a Web Component can be „viewless“, so it just contains logic. This concept should be well known for all Web Dynpro developers (model/viewless WD-Components). The idea behind Web Components is the creation of elements for all kinds of use cases.

Conclusion

Good Bye SAPUI5, Hello Web Components? No, and there are several reasons for this.

  • The browser support is pretty poor right now, because Web Components only work in Google Chrome by default. Check out the support tables at caniuse for more information.
  • Of course you can use polyfills, which provide the required functionalities even in older browsers. Unfortunately, there are some problems, especially with Shadow DOM.
    • You need a lot of code.
    • Very bad performance with some browser.
    • Some functionality can’t be emulated (e.g. NodeList).
  • SAPUI5 is still a state-of-the-art framework and well integrated in the SAP ecosystem. If you want to develop a business application, which needs an integration to your existing SAP environment(Fiori), SAPUI5 should be your primary choice.

In my opinion, Web Components are going to play a major role in future web development. Currently, i recommend to just observe this technology. When the browser support gets better and more projects like Polymer exists, then it’s really time to start with Web Components.

Sources:

WebComponents.org – A place to discuss and evolve web component best-practices

Web Components – Mozilla Developer Network

Google I/O 2015 – Polymer and modern web APIs: In production at Google scale

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. Martin English

    Hi Michael,

    I’m on Chrome Version 43.0.2357.130 m

    I extracted the two html files into the same directory and opened index.html with chrome, but there’s no button. I created an index1.html, using the simple page you first specified, and it renders identically to the index.html that references custom-button.html

    So I copied index.html to index2.html and replaced

    <link rel=”import” href=”custom-button.html”>

    with the contents of custom-button.html. No other change.

    and it works (well, I get the standard link and the button)

    I’m missing something real basic here. Or it just maybe the constantly changing specifications of bleeding edge functionality of browser (in)compatibilities….. ?

    although I’d link to think that the import directive is still valid !!

    hth

    (0) 
    1. Michael Herzog Post author

      Hi Martin,

      importing the file “custom-button.html” won’t work, if you open the index.html directly from the filesystem. You need to place both files in a web server directory. Then open the index.html via e.g. http://localhost:8080/index.html.

      When you open the index.html from filesystem, you should see the following error-message in your console (Chrome Dev Tools):

      Imported resource from origin ‘file://’ has been blocked from loading by Cross-Origin Resource Sharing policy: Received an invalid response. Origin ‘null’ is therefore not allowed access.

      Alternatively, start Chrome with the argument “–disable-web-security”.  This will suspend the web security and allow you to import the “custom-button.html”, even when the “index.html” is opened from filesystem.

      Regards

      Michael

      (0) 
      1. Martin English

        DOH! Of course, the <LINK > functionality is webserver functionality not pure html.

        Anyway, if you don’t have access to a webserver, now you have two ways to may the code example work; i.e.

        * start Chrome with the argument “–disable-web-security”, or

        * do what I did 🙂

        BTW, thanks for your feedback and explanation !!

        (0) 
  2. Tobias Hutterer

    Michael!

    Thanks for the article. I totally agree with you. Web Components will be the future. Browser support moved on in the last year and it is about time. I just hope that SAP will send UI5 into the components world eventually.

    br

    Tobi

    (0) 

Leave a Reply