Skip to content

Commit

Permalink
Introduced FaIconLibrary managed by Angular
Browse files Browse the repository at this point in the history
Usage:

```
export class AppModule {
  constructor(library: FaIconLibrary) {
    library.addIcons(faCoffee);
  }
}
```

Using DI instead of the syntax discussed in the issue
(`FontAwesomeModule.withIcons(faUser)`) as this is more flexible and
future proof given that future version of Angular renderer (Ivy) is
going make usage of modules optional.

---

Deprecated using library from `@fortawesome/fontawesome-svg-core` in
favor of the new class. Previous library was problematic in several
ways:

- global variable, which was shared by all code on the page
- more complicated for consumers as they need to know about existence of
`fontawesome-svg-core` and that it is used by `angular-fontawesome`
- library from `fontawesome-svg-core` implementation was pretty complex

This deprecation is the first step on the way to make
`fontawesome-svg-core` an implementation detail of
`angular-fontawesome`, which consumers don't need to be aware about.

Fixes FortAwesome#3
  • Loading branch information
devoto13 committed Aug 11, 2019
1 parent 048939e commit 7e85ba9
Show file tree
Hide file tree
Showing 19 changed files with 350 additions and 100 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ To get up and running using Font Awesome with Angular follow below steps:
* [In-depth usage guide](./docs/usage.md)
* [Using other styles](./docs/usage/using-other-styles.md)
* [Full feature list](./docs/usage/features.md)
* [Change the default prefix](./docs/usage/default-prefix.md)
* [Upgrading instructions](UPGRADING.md)

## Examples
Expand Down
1 change: 1 addition & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ You might also be interested in the larger umbrella project [upgrade guide](http
## Version Specific Guides
* [Font Awesome 4 or older](docs/upgrading/v4.md)
* [0.1.0 to 0.1.0-6](docs/upgrading/0.1.0-0.1.0-6.md)
* [0.4.0 to 0.5.0](docs/upgrading/0.4.0-0.5.0.md)
33 changes: 33 additions & 0 deletions docs/upgrading/0.4.0-0.5.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Upgrading 0.4.0 to 0.5.0

## Migrate from global icon library to FaIconLibrary

Icon library from `@fortawesome/fontawesome-svg-core` (referred as *global icon library*) is deprecated in favour of `FaIconLibrary` provided by `@fortawesome/angular-fontawesome` and managed by Angular (referred as *icon library*).

The library will emit a warning when icon definition from a global icon library is used, so to migrate you'll need to replace all usages of the global icon library.

```diff
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
-import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
-import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faCoffee, fas } from '@fortawesome/free-solid-svg-icons';
import { AppComponent } from './app.component';

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, FontAwesomeModule],
bootstrap: [AppComponent]
})
export class AppModule {
- constructor() {
+ constructor(library: FaIconLibrary) {
- library.add(fas, faCoffee);
+ library.addIconPacks(fas);
+ library.addIcons(faCoffee);
}
}
```

You can also use `FaConfig.globalLibrary` property to change the warning into the hard error or suppress warnings altogether (not recommended).
3 changes: 0 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,3 @@ Take your icons to the next level with these advanced features.
* [Layers with text](./usage/features.md#layers-with-text)
* [Layers with counter](./usage/features.md#layers-with-counter)
* [Programmatic API](./usage/features.md#programmatic-api)

## Configurations
* [Changing the Default Prefix](./usage/default-prefix.md)
13 changes: 0 additions & 13 deletions docs/usage/default-prefix.md
Original file line number Diff line number Diff line change
@@ -1,13 +0,0 @@
# Changing the default prefix

The default prefix, `fas`, can be adjusted by injecting the `FaConfig` and modifying the `defaultPrefix` property.

```typescript
import { FaConfig } from '@fortawesome/angular-fontawesome';

export class AppComponent {
constructor(private faConfig: FaConfig) {
this.faConfig.defaultPrefix = 'far';
}
}
```
65 changes: 36 additions & 29 deletions docs/usage/icon-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,68 @@

The icon library provides convenient usage in your templates but you have to manage the icons separate from your components. This has long-term maintenance implications, specifically, this means that if someone accidentally removes the icon from your icon library the component that uses it will break.

Icons can be registered once in `app.module` with `library.add()`. Icons added to the library will be available to any other component whose parent module also imports `FontAwesomeModule`. This eliminates the need to redefine or explicitly import icons into individual components across multiple modules, lazy-loaded or not.
Icons can be registered once in `app.module` with `FaIconLibrary.addIcons()` or `FaIconLibrary.addIconPacks()`. Icons added to the library will be available to any other components whose parent module also imports `FontAwesomeModule`. This eliminates the need to redefine or explicitly import icons into individual components across multiple modules, lazy-loaded or not.

`src/app/app.component.html`

```html
<div style="text-align:center">
<!-- simple name only that assumes the 'fas' prefix -->
<fa-icon icon="coffee"></fa-icon>
<!-- ['fas', 'coffee'] is an array that indicates the [prefix, iconName] -->
<fa-icon [icon]="['fas', 'coffee']"></fa-icon>
</div>
<!-- simple name only that assumes the default prefix -->
<fa-icon icon="coffee"></fa-icon>
<!-- ['fas', 'coffee'] is an array that indicates the [prefix, iconName] -->
<fa-icon [icon]="['fas', 'coffee']"></fa-icon>
```

`src/app/app.module.ts`

1. Import `{ FontAwesomeModule } from '@fortawesome/angular-fontawesome'`
1. Import `{ FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'`
1. Add `FontAwesomeModule` to `imports`
1. Import `{ library } from '@fortawesome/fontawesome-svg-core'`
1. Inject `FaIconLibrary` into constructor of the module.
1. Import an icon like `{ faCoffee } from '@fortawesome/free-solid-svg-icons'`
1. Add to the library with `library.add(faCoffee)`
1. Add icon to the library with `library.addIcons(faCoffee)`

```typescript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { BrowserModule } from '@angular/platform-browser';
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faCoffee } from '@fortawesome/free-solid-svg-icons';
import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FontAwesomeModule
],
providers: [],
declarations: [AppComponent],
imports: [BrowserModule, FontAwesomeModule],
bootstrap: [AppComponent]
})
export class AppModule {
constructor() {
constructor(library: FaIconLibrary) {
// Add an icon to the library for convenient access in other components
library.add(faCoffee);
library.addIcons(faCoffee);
}
}
```

You can also import entire icon styles. But be careful! This way of importing icons does not support tree-shaking, so all icons from the imported package will end up in your bundle.
You can also import entire icon styles. But be careful! This way of importing icons does not support tree-shaking, so all icons from the imported package will end up in the bundle.

```javascript
import { library } from '@fortawesome/fontawesome-svg-core';
```typescript
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';

library.add(fas, far);
export class AppModule {
constructor(library: FaIconLibrary) {
library.addIconPacks(fas, far);
}
}
```

## Changing the default prefix

The default prefix, `fas`, can be adjusted by injecting the `FaConfig` and modifying the `defaultPrefix` property.

```typescript
import { FaConfig } from '@fortawesome/angular-fontawesome';

export class AppComponent {
constructor(faConfig: FaConfig) {
faConfig.defaultPrefix = 'far';
}
}
```
9 changes: 4 additions & 5 deletions projects/demo/src/app/alternate-prefix.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Component } from '@angular/core';
import { FaConfig } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { FaConfig, FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { faBellSlash, faHandPaper, faUser } from '@fortawesome/free-regular-svg-icons';

@Component({
Expand All @@ -11,10 +10,10 @@ import { faBellSlash, faHandPaper, faUser } from '@fortawesome/free-regular-svg-
]
})
export class AlternatePrefixComponent {
constructor(private faConfig: FaConfig) {
constructor(faConfig: FaConfig, library: FaIconLibrary) {
// Setting the defaultPrefix to far
this.faConfig.defaultPrefix = 'far';
faConfig.defaultPrefix = 'far';
// Adding dynamic icons to library for use
library.add(faUser, faHandPaper, faBellSlash);
library.addIcons(faUser, faHandPaper, faBellSlash);
}
}
7 changes: 4 additions & 3 deletions projects/demo/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { IconDefinition, IconName, library } from '@fortawesome/fontawesome-svg-core';
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
import { IconDefinition, IconName } from '@fortawesome/fontawesome-svg-core';
import { faFlag, faUser as regularUser } from '@fortawesome/free-regular-svg-icons';
import {
faAdjust,
Expand Down Expand Up @@ -58,7 +59,7 @@ export class AppComponent {
isSyncAnimated = true;
magicLevel = 0;

constructor() {
constructor(library: FaIconLibrary) {
// Notice that we're adding two different icon objects to the library.
// Each of them within their respective icon npm packages are exported as faUser,
// but we've renamed the second one in order to disambiguate the two objects within
Expand All @@ -78,6 +79,6 @@ export class AppComponent {
// <fa-icon [icon]="regularUser"></fa-icon>
//
// You don't specify the prefix in that case, because the icon object knows its own prefix.
library.add(faUser, regularUser);
library.addIcons(faUser, regularUser);
}
}
30 changes: 30 additions & 0 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
import { Injectable } from '@angular/core';
import { IconPrefix } from '@fortawesome/fontawesome-common-types';
import { FaIconLibrary } from './icon-library';

@Injectable({providedIn: 'root'})
export class FaConfig {
/**
* Default prefix to use, when one is not provided with the icon name.
*
* @default 'fas'
*/
defaultPrefix: IconPrefix = 'fas';

/**
* Whether components should lookup icon definitions in the global icon
* library (the one available from
* `import { library } from '@fortawesome/fontawesome-svg-core')`.
*
* See https://github.com/FortAwesome/angular-fontawesome/blob/master/docs/usage/icon-library.md
* for detailed description of library modes.
*
* - 'unset' - Components should lookup icon definitions in the global library
* and emit warning if they find a definition there. This option is a default
* to assist existing applications with a migration. Applications are expected
* to switch to using {@link FaIconLibrary}.
* - true - Components should lookup icon definitions in the global library.
* Note that global icon library is deprecated and support for it will be
* removed. This option can be used to temporarily suppress warnings.
* - false - Components should not lookup icon definitions in the global
* library. Library will throw an error if missing icon is found in the global
* library.
*
* @deprecated This option is deprecated since 0.5.0. In 0.6.0 default will
* be changed to false. In 0.7.0 the option will be removed together with the
* support for the global icon library.
*
* @default 'unset'
*/
globalLibrary: boolean | 'unset' = 'unset';
}
42 changes: 42 additions & 0 deletions src/lib/icon-library.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { inject } from '@angular/core/testing';
import { far, faSun as farSun, faUser as farUser } from '@fortawesome/free-regular-svg-icons';
import { fas, faSun, faUser } from '@fortawesome/free-solid-svg-icons';
import { FaIconLibrary } from './icon-library';

describe('FaIconLibrary', () => {
it('should be possible to add an icon', inject([FaIconLibrary], (library: FaIconLibrary) => {
library.addIcons(faUser);
expect(library.getIconDefinition('fas', 'user')).toBe(faUser);
}));

it('should be possible to add multiple icons', inject([FaIconLibrary], (library: FaIconLibrary) => {
library.addIcons(faUser, farUser);
expect(library.getIconDefinition('fas', 'user')).toBe(faUser);
expect(library.getIconDefinition('far', 'user')).toBe(farUser);
}));

it('should be possible to add an icon pack', inject([FaIconLibrary], (library: FaIconLibrary) => {
library.addIconPacks(far);
expect(library.getIconDefinition('far', 'user')).toBe(farUser);
}));

it('should be possible to add multiple icon packs', inject([FaIconLibrary], (library: FaIconLibrary) => {
library.addIconPacks(far, fas);
expect(library.getIconDefinition('fas', 'sun')).toBe(faSun);
expect(library.getIconDefinition('far', 'sun')).toBe(farSun);
}));

it('should be possible to get an icon', inject([FaIconLibrary], (library: FaIconLibrary) => {
library.addIcons(faUser);
expect(library.getIconDefinition('fas', 'user')).toBe(faUser);
}));

it('should return null if icon prefix was not registered', inject([FaIconLibrary], (library: FaIconLibrary) => {
expect(library.getIconDefinition('fas', 'user')).toBeNull();
}));

it('should return null if icon name is not registered', inject([FaIconLibrary], (library: FaIconLibrary) => {
library.addIcons(faUser);
expect(library.getIconDefinition('fas', 'sun')).toBeNull();
}));
});
32 changes: 32 additions & 0 deletions src/lib/icon-library.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Injectable } from '@angular/core';
import { IconDefinition, IconName, IconPack, IconPrefix } from '@fortawesome/fontawesome-svg-core';

@Injectable({providedIn: 'root'})
export class FaIconLibrary {
private definitions: { [prefix: string]: { [name: string]: IconDefinition } } = {};

addIcons(...icons: IconDefinition[]) {
for (let i = 0; i < icons.length; i++) {
const icon = icons[i];
if (!(icon.prefix in this.definitions)) {
this.definitions[icon.prefix] = {};
}
this.definitions[icon.prefix][icon.iconName] = icon;
}
}

addIconPacks(...packs: IconPack[]) {
for (let i = 0; i < packs.length; i++) {
const pack = packs[i];
const icons = Object.keys(pack).map((key) => pack[key]);
this.addIcons(...icons);
}
}

getIconDefinition(prefix: IconPrefix, name: IconName): IconDefinition | null {
if (prefix in this.definitions && name in this.definitions[prefix]) {
return this.definitions[prefix][name];
}
return null;
}
}
6 changes: 3 additions & 3 deletions src/lib/icon/duotone-icon.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, Input } from '@angular/core';
import { IconDefinition, IconProp } from '@fortawesome/fontawesome-svg-core';
import { FaIconComponent } from './icon.component';
import { IconLookup } from '@fortawesome/fontawesome-svg-core';

@Component({
selector: 'fa-duotone-icon',
Expand Down Expand Up @@ -48,8 +48,8 @@ export class FaDuotoneIconComponent extends FaIconComponent {
*/
@Input() secondaryColor?: string;

protected normalizeIcon(): IconLookup {
const lookup = super.normalizeIcon();
protected findIconDefinition(i: IconProp | IconDefinition): IconDefinition | null {
const lookup = super.findIconDefinition(i);

if (lookup != null && lookup.prefix !== 'fad') {
throw new Error(
Expand Down
Loading

0 comments on commit 7e85ba9

Please sign in to comment.