This is a example of the angular icon system that I use, it’s aim is to keep icon naming consistent and the html needed to generate a icon small.

Displaying a icon using SVG symbols

A prerequisite for the icon system is that every icon has a unique name. The icon system relies on a single SVG with multiple symbols, we then <use> the symbol in one or more places.

This SVG is prepended to the body of our html. We are using a Internally referenced SVG in this example another approach that would work with this icon system is to use externally referenced svg files.

<svg style="position: absolute; width: 0; height: 0; overflow: hidden">
    <defs>
        <symbol viewBox="0 0 20 20" id="apartment">
            <title>apartment</title>
            <path d="..."></path>
            <path d="..."></path>
        </symbol>
        ...
    </defs>
</svg>

I am going to skip over how we got to this point where the SVG’s are already in the document body. It would be tedious to manage our svg symbols by having them statically in the body of our html.

With this icon system in its real world usage I concatenate the SVG’s with a ‘icon build system’ which I will explore in another post, the build system allows me to place all of my icons as single SVG’s into a directory, the build system then collects all of those icons and injects them into the body.

Once we have the SVG symbols injected into our html we can <use> our SVG’s.

<svg class="icon">
    <use xlink:href="#apartment"></use>
</svg>

(explain xlink href)

We are referencing the ‘apartment’ SVG that is in our SVG symbols. The above example produces our ‘apartment’ icon

Building a icon component

Now that we have got the basic version of this icon system working we can begin to build on top with angular. We are going to create a angular component which uses a variation of the above html as its template. Instead of the hard coded ‘#apartment’ we use the injected string iconName from the our svgIcon component.

.component('svgIcon', {
    template: '<svg class="icon">
                  <use xlink:href="{{$ctrl.iconName}}"></use>
              </svg>',
});

Lets define iconName as a binding, iconName references the symbol ID directly from our svg defs.

.component('svgIcon', {
    template: '<svg class="icon">
                  <use xlink:href="{{$ctrl.iconName}}"></use>
              </svg>',
    bindings: {
        iconName: '<',
    },
    controller: function (){}
});

To produce our ‘apartment’ icon we then only need the following html. Assuming we have our angular modules wired up correctly.

<svg-icon icon-name="'apartment'"><svg-icon>

Here is a working example of all of the concepts we have covered so far. There are some extras in the example that I have left out so far for simplicity. Assumed knowledge here is the $onChanges life cycle hook and ng-style used to set our icon size.

See the Pen Angular Icon System (Icon) by Sam Dawson (@Samic8) on CodePen.

Aliases

Aliases are the most powerful part of this icon system, it helps keep icons consistent across our application. Aliases provide a way for an icons unique name to be independent of their usage name, allowing the same icon to be used for multiple use cases with different naming.

Another benefit is being able to replace icons with without having to do a find and replace. Now lets get into how we go about getting aliases working for our icon system.

We want to add a extra binding to our svgIcon component called aliasName. Here we will use one-way binding (<), which allows us to watch for changes and update icons on the fly.

.component('svgIcon', {
    template: '<svg class="icon">
                  <use xlink:href="{{$ctrl.iconName}}"></use>
              </svg>',
    bindings: {
        iconName: '<',
        iconAlias: '<',
    },
    controller: function (){}
});

For our apartment icon, lets set up a alias name of ‘building’. So that we can use the component like so

<svg-icon icon-alias="'building'"><svg-icon>

We need to write some extra javascript in our controller to get this working, so far our controller has been empty. First we want to create a aliases object, the holds our alias/icon name associations.

function svgIconController() {
	const ctrl = this;
	
	const aliases = {
		building: 'apartment',
		...
	};
}

Now that we have a string that does not directly associate with a symbol in our svg, we want to make sure that we translate aliasName input to a string that is actually associated with a symbol.

Lets change the template so it is not directly using our ‘iconName’ binding

.component('svgIcon', {
    template: '<svg class="icon">
                  <use xlink:href="{{$ctrl.svgSymbolId}}"></use>
              </svg>',
    bindings: {...},
    controller: ...
});

Now we want to get the symbol id from the associated ‘aliasName’ aliases[aliasName] and set the result to the svgSymbolId. Our component is now multipurpose as it accepts both ‘iconName’ and ‘aliasName’, we are assuming the developer will use either/or and not both (our component could be extended to error etc.)

function svgIconController() {
	const ctrl = this;
	
	const aliases = {...};
	
	this.$onChanges = $onChanges;
	
	function $onChanges({iconName, aliasName, iconSize}) {
		ctrl.svgSymbolId = `#${iconName.currentValue ? iconName.currentValue : aliases[aliasName.currentValue]}`;
	}
	
}

Destructuring assignment is used as our function arguments

Usage of <svg-icon icon-alias="'building'"><svg-icon> will produce

See the Pen Angular Icon System (Aliases) by Sam Dawson (@Samic8) on CodePen.

Bonus

ng-repeat

With our icon system wired up to accept one way bindings we can use ng-repeat to produce dynamic lists of icons. In our root controller we can set up a array of iconName (we could also use iconAlias)

function rootCtrl($scope) {
    $scope.iconsToRepeat = [
        'cash-dollar',
        'bubbles',
        'calculator',
        'apartment'
    ];
}
<div ng-repeat="iconName in iconsToRepeat">
    <svg-icon icon-name="iconName"></svg-icon>
</div>

Which produces our icons

Colors

This icon system can be used with the color system outlined in my previous post.

We can use currentColor as the fill property on the svg inside our svgIcon component and then on the components element use a color class, the svg will inherit our color.

.icon {
    fill: currentColor;
}

Note: That the icons we are using in these examples are fill only icons. This sort of color inheritance will prove difficult if we needed to use icons with multiple colors, this is a limitation.

Icon sizing

In this icon system we are using icons that have equal width and height. Therefore being able to simultaneously change the width and height with a single value, we can set our icons width and height properties to 1em. The icons will now equal the current font-size, meaning if we set a font-size of 20px on the parent element or directly the svg will use the current font size as both its width and height.

.icon {
    ...
    width: 1em;
    height: 1em;
}

To use this with our icon system we could use inline styling

<svg-icon icon-name="apartment" style="font-size: 20px"><svg-icon>

If we want to do this a little cleaner we could add a iconSize binding to our component and then apply the font-size in a inline style within the component

<svg-icon icon-name="apartment" icon-size="20"><svg-icon>

Complete example

See the Pen Angular Icon System (Repeat) by Sam Dawson (@Samic8) on CodePen.

Link to post on this icon system being rebuilt for angular 2

apartment bubbles calculator cash-dollar