Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Mastering Angular Components

You're reading from   Mastering Angular Components Build component-based user interfaces with Angular

Arrow left icon
Product type Paperback
Published in Jul 2018
Publisher Packt
ISBN-13 9781788293532
Length 402 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Gion Kunz Gion Kunz
Author Profile Icon Gion Kunz
Gion Kunz
Arrow right icon
View More author details
Toc

Table of Contents (12) Chapters Close

Preface 1. Component-Based User Interfaces FREE CHAPTER 2. Ready, Set, Go! 3. Dealing with Data and State 4. Thinking in Projects 5. Component-Based Routing 6. Keeping up with Activities 7. Components for User Experience 8. Time Will Tell 9. Spaceship Dashboard 10. Putting Things to the Test 11. Other Books You May Enjoy

Components – the organs of user interfaces

"We're not designing pages, we're designing systems of components."
- Stephen Hay

This quote by Stephen Hay from BDConf in Orlando 2012 brings it to the point. Interface design is really not about pages. To create efficient user interfaces for not only the users but also the developers who maintain them, we need to think in systems of components. Components are independent, but they can interact with each other and create larger components when they are arranged together. We need to look at user interfaces holistically, and using components enables us to do this.

In the following topics, we're going to explore a few fundamental aspects of components. Some of these are already known from other concepts, such as object-oriented programming (OOP), but they appear in a slightly different light when thinking about components.

Encapsulation

Encapsulation is a very important factor when thinking about maintenance in a system. Having a classical OOP background, I've learned that encapsulation means bundling logic and data together into an isolated container. This way, we can operate on the container from the outside and treat it like a closed system.

There are many positive aspects of this approach when it comes to maintainability and accessibility. Dealing with closed systems is important for the organization of our code. However, this is even more important because we can organize ourselves while working with code:

Organizing a system in encapsulated components allows us to reason about it much more easily

I have a pretty bad memory, and it's very important for me to find the right focus level when working on code. Immediate memory research has tells us that the human brain can remember about seven items at once on average. Therefore, it's crucial for us to write code in such a way that it allows us to focus on fewer and smaller pieces at once.

A clear encapsulation helps us in organizing our code. We can perhaps forget all the internals of the closed system and about the kind of logic and data that we've put into it. We should focus only on its surface, which allows us to work on a higher-abstraction level. Similar to the previous diagram, without using a hierarchy of encapsulated components, we'd have all our code cobbled together on the same level.

Encapsulation encourages us to isolate small and concise components and build a system of components. During development, we can focus on the internals of one component and only deal with the interface of other components.

Sometimes, we forget that all the organization of the coding we actually perform is for ourselves and not for the computer that runs this code. If this was for the computer, then we would probably all start writing in machine language again. A strong encapsulation helps us access specific code easily, focus on one layer of the code, and trust the underlying implementations within capsules.

The following TypeScript example shows you how to use encapsulation to write maintainable applications. Let's assume that we are in a T-shirt factory, and we need some code to produce T-shirts with a background and foreground color. This example uses some language features of TypeScript. If you're not familiar with the language features of TypeScript, don't worry too much at this point. We will learn about these later in this chapter:

// This class implements data and logic to represent a color 
// which establishes clean encapsulation.
class Color {
constructor(private red: number, private green: number, private blue: number) {}

// Using this function we can convert the internal color values
// to a hex color string like #ff0000 (red).
getHex(): string {
return '#' + Color.getHexValue(this.red) + Color.getHexValue(this.green) +
Color.getHexValue(this.blue);
}

// Static function on Color class to convert a number from
// 0 to 255 to a hexadecimal representation 00 to ff
static getHexValue(number): string {
const hex = number.toString(16);
return hex.length === 2 ? hex : '0' + hex;
}
}

// Our TShirt class expects two colors to be passed during
// construction that will be used to render some HTML
class TShirt {
constructor(private backgroundColor: Color, private foregroundColor: Color) {}

// Function that returns some markup which represents our T-Shirts
getHtml(): string {
return `
<t-shirt style="background-color: ${this.backgroundColor.getHex()}">
<t-shirt-text style="color: ${this.foregroundColor.getHex()}">
Awesome Shirt!
</t-shirt-text>
</t-shirt>
`;
}
}

// Instantiate a blue colour
const blue: Color = new Color(0, 0, 255);
// Instantiate a red color
const red: Color = new Color(255, 0, 0);
// Create a new shirt using the above colours
const awesomeShirt: TShirt = new TShirt(blue, red);
// Adding the generated markup of our shirt to our document
document.body.innerHTML = awesomeShirt.getHtml();

Using a clean encapsulation, we can now work with the abstraction of color in our T-shirt. We don't need to worry about how to calculate the hexadecimal representation of colors at the T-shirt level because this is already done by the Color class. This makes your application maintainable and keeps it very open for change.

I strongly recommend that you read about the SOLID principles if you haven't done so already. As the name already suggests, this assembly of principles is a solid power tool that can change the way you organize code tremendously. You can learn more about the SOLID principles in the book Agile Principles, Patterns, and Practices, by Robert C. Martin.

Composability

Composition is a special kind of reusability. You don't extend an existing component, but you create a new, larger component by composing many smaller components together into a system of components.

In OOP languages, composition is often used to get around the multiple inheritance issues that most OOP languages have. Subclass polymorphism is always great until you reach the point where your design does not match the latest requirements in your project. Let's look at a simple example that illustrates this problem.

You have a Fisher class and a Developer class, both of which hold specific behaviors. Now, you'd want to create a FishingDeveloper class that inherits from both Fisher and Developer. Unless you're using a language that supports multiple inheritance (such as C++, which does this to a certain extent), you will not be able to reuse this functionality using inheritance. There is no way to tell the language that your new class should inherit from both superclasses. Using composition, you can easily solve this problem. Instead of using inheritance, you're composing a new FishingDeveloper class that delegates all behavior to an internal Developer and Fisher instance:

interface IDeveloper {
code(): void;
}

interface IFisher {
fish(): void;
}

class Developer implements IDeveloper {
constructor(private name: string) {}

code(): void {
console.log(`${this.name} writes some code!`);
}
}

class Fisher implements IFisher {
constructor(private name: string) {}

fish(): void {
console.log(`${this.name} catches a big fish!`);
}
}

class FishingDeveloper implements IFisher, IDeveloper {
constructor(private name: string) {
this.name = name;
this.developerStuff = new Developer(name);
this.fisherStuff = new Fisher(name);
}

code(): void {
this.developerStuff.code();
}

fish(): void {
this.fisherStuff.fish();
}
}

var bob: FishingDeveloper = new FishingDeveloper('Bob');
bob.code();
bob.fish();

Experience has taught us that composition is probably the most efficient way to reuse code. In contrast to inheritance, decoration, and other approaches to gain reusability, composition is probably the least intrusive and the most flexible.

Recent versions of some languages also support a pattern called traits, that is, mixins. Traits allow you to reuse certain functionality and attributes from other classes in a way that is similar to multiple inheritance.

If we think about the concept of composition, it's nothing more than designing organisms. We have the two Developer and Fisher organisms, and we unify their behaviors into a single FishingDeveloper organism.

Components, invented by nature

Components, embracing encapsulation, and composition are an effective way to build maintainable applications. Composed from components, applications are very resistant to the negative implications of change, and change is a necessary thing that will happen to every application. It's only a matter of time until your design will be challenged by the effects of change; therefore, it's very important to write code that can handle change as smoothly as possible.

Nature is the best teacher. Almost all the achievements in technological developments have their origin in observations of how nature solves problems. If we look at evolution, it's an ongoing redesign of matter by adapting to outer forces and constraints. Nature solves this by constant change using mutation and natural selection.

If we project the concept of evolution onto developing an application, we can say that nature does actually refactor its code in every single moment. This is actually the dream of every product manager—an application that can undergo constant change but does not lose any of its efficiency.

I believe that there are two key concepts that play a major role in nature that allows it to apply constant change in its design without losing much efficiency. This uses encapsulation and composition. Coming back to the example of our bodies, we can actually tell that our organs use a very clear encapsulation. They use membranes to create isolation, veins to transport nutrition, and synapses to send messages. Also, they have interdependencies, and they communicate using electrical and chemical messages. Most obviously, they form larger systems, which is the core concept of composition.

Of course, there are many other factors, and I'm not a professor in biology. However, I think it's a fascinating thing to see that we have learned to organize our code very similarly to how nature organizes matter.

The idea of creating reusable UI components is quite old, and it was implemented in various languages and frameworks. One of the earliest systems that used UI components was probably the Xerox Alto system back in the 1970s. It used reusable UI components that allowed developers to create an application by composing them on a screen where users could interact with them:

The user interface of file manager on the Xerox Alto system from the 1970s

Early frontend UI frameworks, such as DHTMLX, Ext JS, or jQuery UI implemented components in a more limited fashion that didn't provide great flexibility or extensibility. Most of these frameworks just provided widget libraries. The problem with UI widgets is that they mostly don't embrace the pattern of composition enough. You can arrange widgets on a page and they provide encapsulation, but with most toolkits, you can't create larger components by nesting them inside each other. Some toolkits solve this by providing a special kind of widget which was mostly called a container. However, this is not the same as a full-fledged component tree that allows you to create systems within systems. Containers were actually meant to provide a visual layout container rather than a composite container to form a larger system.

Usually, when working with widgets on a page of our application, we'd have a large controller that controls all these widgets, user input, and states. However, we are left with two levels of composition, and there's no way that we can structure our code more granularly. There is the page and there are the widgets. Having a bunch of UI widgets is simply not enough, and we are almost back to the state where we create pages plastered with form elements.

I've been a user of JavaServer Faces for years, and besides all its problems, the concept of having reusable custom elements was groundbreaking. Using XHTML, one could write so-called composite components that consisted of other composite components or native HTML elements. A developer could gain a fantastic level of reusability using composition. In my view, the big issue with this technology was that it did not address the concerns in the frontend enough to become really usable for complex user interactions. In fact, a framework like this should live completely within the frontend.

My UI framework wishlist

Usually, when UI frameworks get compared, they get measured against each other based on metrics, such as widget count, theming capabilities, and asynchronous data retrieval features. Each framework has its strengths and weaknesses, but leaving all the extra features aside and reducing it to the core concerns of a UI framework, I only have a few metrics left that I'd like to be assessed. These metrics are, of course, not the only ones that are important in today's UI development, but they also are the main factors toward building a clean architecture that supports the principle of change:

  • I can create encapsulated components with clear interfaces
  • I can create larger components by using composition
  • I can make components interact with each other within their hierarchy

If you're looking for a framework which enables you to take full advantage of component-based UI development, you should look for these three key measures.

First of all, I think it's very important to understand the main purpose of the web and how it evolved. If we think of the web in its early days in the 1990s, it was probably only about hypertext. There were very basic semantics that could be used to structure information and display them to a user. HTML was created to hold structure and information. The need for custom visual presentation of information led to the development of CSS right after HTML started being widely used.

It was in the mid 1990s when Brendan Eich invented JavaScript, and it was first implemented in Netscape Navigator. By providing a way to implement behavior and state, JavaScript was the last missing piece for a full web customization:

Technology

Concern

HTML

Structure and information

CSS

Presentation

JavaScript

Behavior and state

We have learned to keep these concerns as separate as possible in order to maintain a clean architecture. Although there are different opinions on this and some recent technologies also move away from this principle, I believe that a clean separation of these concerns is very important to create a maintainable application.

Leaving this view aside, the standard definition of encapsulation from OOP is just concerned about coupling and isolation of logic and data. This probably applies well to classic software components. However, as soon as we consider a user interface as part of an architecture, there is a new dimension that is added.

Classical MVC frameworks are view centric, and developers organize their code based on pages. You'll probably go ahead and create a new view that represents a page. Of course, your view needs a controller and model, so you'll also create them. The problem with organization by pages is that there's little to no gain of reusability. Once you've created a page and you'd like to reuse only some parts of the page, you will need a way to encapsulate only a specific part of this model—the view and the controller.

UI components solve this problem nicely. I like to see them as a modular approach to MVC. Although they still embrace the MVC pattern, they also establish encapsulation and composability. This way, a view is a component itself, but it also consists of components. By composing views of components, one can gain a maximum amount of reusability:

UI components embrace MVC, but they also support encapsulation and composition on a much lower level

Technically, there are some challenges when implementing components with web technologies. JavaScript was always flexible enough to implement different patterns and paradigms. Working with encapsulation and composition isn't an issue at all, and the controlling part and the model of components can easily be implemented. Approaches, such as the revealing module pattern, namespaces, prototypes, or the recent ECMAScript 6 modules, provide all the tools that are needed from the JavaScript side.

However, for the view part of our components, we face some limitations. Although HTML supports great flexibility in terms of composability because the DOM tree is nothing else than a big composition, we have no way to reuse these compositions. We can only create one large composition, which is the page itself. HTML being only the final view that was delivered from the server, this was never really a real concern. Today's applications are much more demanding, and we need to have a fully-encapsulated component running in the browser, which also consists of a partial view.

We face the same problem with CSS. There is no real modularization and encapsulation while writing CSS, and we need to use namespaces and prefixes in order to segregate our CSS styles. Still, the whole cascading nature of CSS can easily destroy all encapsulation that we try to bring in place using CSS structuring patterns.

Time for new standards

Web standards have been evolving immensely in the last couple of years. There are so many new standards, and the browser became such a big multimedia framework, that it's hard for other platforms to compete with this.

I'd even go as far as to say that web technology will actually replace other frameworks in the future, and it probably will be renamed to multimedia technology or something similar. There's no reason why we need to use different native frameworks to create user interfaces and presentations. Web technologies embed so many features that it's hard to find a reason not to use them for any kind of application. Just look at the Firefox OS or the Chrome OS, which are designed to run with web technologies. I think it's only a matter of time until more operating systems and embedded devices make use of web technologies to implement their software. This is why I believe that at some point it will be questionable whether the term web technologies is still appropriate or whether we should replace it with a more general term.

Although we usually just see new features appear in browsers, there is a very open and long-winded standardization process behind them. It's very important to standardize features, but this takes a lot of time, especially when people disagree about different approaches to solving problems.

Coming back to the concept of components, this is something where we really need support from web standards to break the current limitations. Fortunately, the W3C (World Wide Web Consortium) thought the same, and a group of developers started to work on specifications under the hood of an umbrella specification called web components.

The following topics will give you a brief overview over two specifications that also play a role in Angular components. One of Angular's core strengths is that it acts more like a superset of web standards rather than being a complete isolated framework.

Template elements

Template elements allow you to define regions within your HTML, which will not be rendered by the browser. You can then instantiate these document fragments with JavaScript and then place the resulting DOM within your document.

While the browser is actually parsing the template content, it only does so in order to validate the HTML. Any immediate actions that the parser would usually execute will not be taken. Within the content of template elements, images will not be loaded and scripts won't be executed. Only after a template is instantiated will the parser take the necessary actions, as follows:

<body> 
<template id="template">
<h1>This is a template!</h1>
</template>
</body>

This simple HTML example of a template element won't display the heading on your page. As the heading is inside a template element, we first need to instantiate the template and add the resulting DOM into our document:

var template = document.querySelector('#template'); 
var instance = document.importNode(template.content, true);
document.body.appendChild(instance);

Using these three lines of JavaScript, we can instantiate the template and append it into our document.

Template elements are used by Angular in order to instantiate dynamic parts of your user interface. This will be the case while conditionally rendering parts of your template using the ngIf directive, or by repeating a template using the ngFor directive.

Shadow DOM

This part of the web components specification was the missing piece to create proper DOM encapsulation and composition. With shadow DOM, we can create isolated parts of the DOM that are protected against regular DOM operations from the outside. Also, CSS will not reach into shadow DOM automatically, and we can create local CSS within our component.

If you add a style tag inside shadow DOM, the styles are scoped to the root within the shadow DOM, and they will not leak outside. This enables a very strong encapsulation for CSS.

Content insertion points make it easy to control content from the outside of a shadow DOM component, and they provide some kind of an interface to pass in content.

At the time of writing this book, shadow DOM is supported by most browsers, although it still needs to be enabled in Firefox.

You have been reading a chapter from
Mastering Angular Components - Second Edition
Published in: Jul 2018
Publisher: Packt
ISBN-13: 9781788293532
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime