Leveraging the power of SVG

SVG has been a part of the Open Web Platform standards since 1999 and was first recommended in 2001 under the SVG 1.0 standard. SVG is a consolidation of two independent proposals for an XML-based vector image format. Precision Graphics Markup Language (PGML)—mainly developed by Adobe and Netscape—as well as Vector Markup Language (VML)—which was mainly represented by Microsoft and Macromedia—were both different XML formats that served the same purpose. The W3C consortium declined both the proposals in favor of the newly developed SVG standard that unified the best of both worlds into a single standard:

Leveraging the power of SVG

Timeline showing the development of the SVG standard

All three standards had a common goal, which was to provide a format for the Web to display vector graphics in the browser. SVG is a declarative language that specifies graphical objects using XML elements and attributes.

Let's look at a simple example on how to create an SVG image with a black circle, using SVG:

<?xml version="1.0" encoding="utf-8"?>  
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
     width="20px" height="20px">
  <circle cx="10" cy="10" r="10" fill="black" />
</svg>

This rather simple example represents an SVG image with a black circle, whose center is located at x = 10 px and y = 10 px. The radius of the circle is 10 px, which makes this circle 20 px in width and height.

The origin of the coordinate system in SVG sits on the top-left corner, where the y axis faces the south direction and the x axis eastward:

Leveraging the power of SVG

The coordinate system within SVG

Using not only primitive shapes, such as circles, lines, and rectangles, but also complex polygons, the possibilities for creating graphical content are nearly unlimited.

SVG is not only used within the Web, but has also become a very important intermediate format for exchanging vector graphics between different applications. Almost any application that supports vector graphics also supports the import of SVG files.

The real power of SVG comes to the surface when we do not include an SVG file as an HTML image, but rather include the SVG content directly within our DOM. Since HTML5 directly supports the SVG namespace within an HTML document and will render the graphics we define within our HTML, a whole bunch of new possibilities spring up. We can now style our SVG with CSS, manipulate the DOM with JavaScript, and easily make our SVG interactive.

Taking the previous example of our circle image to the next level, we could make it interactive by changing the circle color by clicking it. First, let's create a minimal HTML document and include our SVG elements directly within the DOM:

<!doctype html>
<title>Minimalistic Circle</title>
<svg width="20px" height="20px">
  <circle id="circle" cx="10" cy="10" r="10" fill="black">
</svg>
<script>
  document
    .getElementById('circle')
    .addEventListener('click', function(event) {
      event.target.setAttribute('fill', 'red');
    });
</script>

As you can see, we can get rid of the version and the XML namespace declaration when we use SVG directly within the DOM of our HTML document. What's interesting here is that we can treat SVG very much like regular HTML. We can assign an ID and even classes to SVG elements and access them from JavaScript.

Within the script tag of our HTML document, we can directly access our circle element using the ID we've previously assigned to it. We can add event listeners, the way we already know, from regular HTML elements. In this example, we added a click event listener and changed the color of our circle to red.

For the sake of simplicity, we used an inline script tag in this example. It would of course be much cleaner to have a separate JavaScript file to do the scripting.

Styling SVG

I'm a purist when it comes to the separation of concerns within the Web. I still strongly believe in the separation of structure (HTML), appearance (CSS), and behavior (JavaScript), as well as producing the most maintainable applications when following this practice.

First, it seems weird to have SVG in your HTML, and you might think that this breaks the contract of a clean separation. Why is this graphical content, consisting of only appearance-relevant data, sitting in my HTML that is supposed to only contain raw information? After dealing with a lot of SVGs within a DOM, I have come to the conclusion that we can establish a clean separation when using SVG by dividing our appearance responsibilities into the following two subgroups:

  • Graphical structure: This subgroup deals with the process of defining the basic structure of your graphical content. This is about shapes and layout.
  • Visual appearance: This subgroup deals with the process of defining the look and feel of our graphical structures, such as colors, line widths, line styles, and text alignment.

If we separate the concerns of SVG into these groups, we can actually gain great maintainability. Graphical structure is defined by the SVG shapes themselves. They are directly written within our HTML but don't have a particular look and feel. We only store the basic structural information within HTML.

Luckily, all the properties of visual appearance, such as colors, cannot only be expressed through the attributes in our SVG elements; however, there's a corresponding CSS property that allows us to offload all the look-and-feel-relevant aspects of the structure to CSS.

Go back to the example where we drew a black circle; we'll tweak this a bit to fit our demands of separation of concerns so that we can distinguish graphical structure from graphical appearance:

<!doctype html>
<title>Minimalistic Circle</title>
<svg width="20px" height="20px">
  <circle class="circle" cx="10" cy="10" r="10">
</svg>

Styling our graphical structures can now be achieved using CSS by including a stylesheet with the following content:

.circle {
  fill: black;
}

This is fantastic, as we can now not only reuse some graphical structures, but also apply different visual appearance parameters using CSS, similar to those enlightening moments when we managed to reuse some semantic HTML by only changing some CSS.

Let's look at the most important CSS properties we can use to style SVG shapes:

  • fill: While working with solid SVG shapes, there's always a shape fill and stroke option available; the fill property specifies the color of the shape fill.
  • stroke: This property specifies the color of the SVG shape's outline.
  • stroke-width: This property specifies the width of the SVG shape's outline on solid shapes. For nonsolid shapes, such as lines, this can be thought of as line width.
  • stroke-dasharray: This specifies a dash pattern for strokes. Dash patterns are space-separated values that define a pattern.
  • stroke-dashoffset: This specifies an offset for the dash pattern, which is specified with the stroke-dasharray property.
  • stroke-linecap: This property defines how line caps should be rendered. They can be rendered as square, butt, or rounded caps.
  • stroke-linejoin: This property specifies how lines are joined together within a path.
  • shape-rendering: Using this property, you can override the shape-rendering algorithm that, as the name suggests, is used to render shapes. This is particularly useful if you need crispy edges on your shapes.

For a complete reference of the available appearance-relevant SVG attributes, visit the Mozilla Developer website at https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute.

I hope this brief introduction gave you a better feeling about SVG and the great power it comes with. In this chapter, we're going to use some of that power to create nice, interactive graphical components. If you would like to learn more about SVG, I strongly recommend that you go through the great articles by Sara Soueidan.

Building SVG components

When building Angular components with SVG templates, there are a couple of things we need to be aware of. The first and most obvious one, is XML namespaces. Modern browsers are very intelligent when parsing HTML. Besides being probably the most fault-tolerant parser in the history of computer science, DOM parsers are very smart in recognizing markup and then deciding how to treat it. They will automatically decide the correct namespaces for us, based on element names, so we don't need to deal with them when writing HTML.

If you've messed around with the DOM API a bit, you would've probably recognized that there are two methods for creating new elements. In the document object, for example, there's a createElement function, but there's also createElementNS that accepts an additional namespace URI parameter. Also, every created element has a namespaceURI property that tells you the namespace of the specific element. This is important since HTML5 is a standard that consists of at least three namespaces:

  • HTML: This is the standard HTML namespace with the http://www.w3.org/1999/xhtml URI.
  • SVG: This embraces all SVG elements and attributes and uses the http://www.w3.org/2000/svg URI. You can sometimes see this namespace URI in an xmlns attribute of the svg elements. In fact, this is not really required, as the browser is smart enough to decide on the correct namespace itself.
  • MathML: This is an XML-based format to describe mathematical formulas and is supported in most modern browsers. It uses the http://www.w3.org/1998/Math/MathML namespace URI.

We can mix all these elements from different standards and namespaces within a single document, and our browser will figure out the correct namespace itself when it creates elements within the DOM.

Tip

If you want more information on namespaces, I recommend that you go through the Namespaces Crash Course article on the Mozilla Developer Network at https://developer.mozilla.org/en/docs/Web/SVG/Namespaces_Crash_Course.

As Angular will compile templates for us and render elements into the DOM using the DOM API, it needs to be aware of the namespaces when doing that. Similar to the browser, Angular provides some intelligence for deciding the correct namespace while creating elements. However, there are some situations where you need to help Angular recognize the correct namespace.

To illustrate some of this behavior, let's transform our circle example that we've been working on into an Angular component:

@Component({
  selector: 'awesome-circle',
  template: `
    <svg [attr.width]="size" [attr.height]="size">
      <circle [attr.cx]="size/2" [attr.cy]="size/2"
              [attr.r]="size/2" fill="black" />
    </svg>
  `
})
export class AwesomeCircle {
  @Input() size;
}

We've wrapped our circle SVG graphics into a simple Angular component. The size input parameter determines the actual width and height of the circle by controlling the SVG's width and height attributes and the circle's cx, cy, and r attributes.

To use our Circle component, simply use the following template within another component:

<awesome-circle [size]="20"></awesome-circle>

Note

It's important to note that we need to use attribute bindings on SVG elements, and we can't set DOM element properties directly. This is due to the nature of SVG elements that have special property types—for example, SVGAnimatedLength—that can be animated with Synchronized Multimedia Integration Language (SMIL). Instead of interfering with these rather complex element properties, we can simply use attribute bindings to set the attribute values of the DOM element.

Let's go back to our namespace discussion. Angular would know that it needs to use the SVG namespace to create the elements within this template. It will function in this way simply because we're using the svg element as a root element within our component, and it could switch the namespace within the template parser for any child elements automatically.

However, there are certain situations where we need to help Angular determine the correct namespace for the elements we'd like to create. This strikes us if we're creating nested SVG components that don't contain a root svg element:

@Component({
  selector: '[awesomeCircle]',
  template: `
      <svg:circle [attr.cx]="size/2" [attr.cy]="size/2"
                  [attr.r]="size/2" fill="black" />
  '
})
export class AwesomeCircle {
  @Input('awesomeCircle') size;
}

@Component({
  selector: 'app'
  template: `
   <svg width="20" height="20">
    <g [awesomeCircle]="20"></g>
   </svg>
  `,
  directives: [AwesomeCircle]
})
export class App {}

In this example, we're nesting SVG components, and our AwesomeCircle component does not have an svg root element to tell Angular to switch the namespace. This is why we've created the svg element within our App component and then included the AwesomeCircle component in an SVG group.

We need to explicitly tell Angular to switch to the SVG namespace within our Circle component, and we can do this by including the namespace name as a prefix separated by a colon, as you can see in the highlighted section of the preceding code excerpt.

If you have multiple elements that need to be created within the SVG namespace explicitly, you can rely on the fact that Angular does apply the namespace for child elements too and does group all your elements with an SVG group element. So, you only need to prefix the group element <svg:g> ... </svg:g>, but none of the contained SVG elements.

This is enough to know about Angular internals when dealing with SVG. Let's move on and create some real components!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset