Category: Angular

  • Componentize SVGs in Angular and style them on the fly

    Componentize SVGs in Angular and style them on the fly

    As you probably know, SVGs, or scalable vector graphics, are essential if you’re using simple artwork like icons. But did you know that you can make them into Angular components? In fact, the two libraries I use the most are the ones I created for SVGs and buttons.

    Why should you use SVGs?

    I started using SVGs because of their ability to scale without any loss in resolution and because I could color them through CSS. (This was useful for a part of the gamified interactive lesson I started where you get to pick a character and make it grow or shrink.)

    Note: Only SVGs made with paths and outlines can scale indefinitely. SVGs made with images will get fuzzy.

    SVGs are also super lightweight because they are represented in XML, and you can avoid http requests to fetch them!

    Liberate yourself from SVG libraries

    What I didn’t realize was that my beloved Font Awesome library was costing me almost 20 Lighthouse points and that I didn’t need their large Javascript files to make them into tidy components (instead of unmanageable chunks of text) by turning them into Angular components.

    Note: I still subscribe to Font Awesome because of their unparalleled selection. In fact, all of the icons on the sites/apps I make are thanks to their brilliant illustrators.

    If you end up using their SVGs or make your own using Illustrator, be sure to delete any styles/comments that are part of the SVG, so that you can properly override them.

    Angular components don’t have to use HTML templates

    If you want your own Angular SVG library, you can easily turn them into components by linking the templateUrl parameter of your component to the SVG in your asset library. (Font Awesome offers a library to access their SVGs, but you can avoid unnecessary dependencies and weight to your app by going this route. As an added bonus, this approach lets you declare your SVGs once, instead of having to declare them individually in every app that uses them.)

    svg-astronaut.component.ts

    @Component({
      selector: 'ces-svg-astronaut',
      templateUrl: '../../../../../../src/assets/images/svgs/astronaut.svg',
      styleUrls: ['../../svg.component.scss']
    })
    

    Note: Initially, I was storing the assets directly in my Angular library but then moved the SVGs to my apps’ shared assets folder so that I don’t have to modify my angular.json file to use the assets and so that I could access the SVGs outside of the library.

    The downside to this decision is that the library won’t work outside of the workspace unless you include the assets folder. Please let me know if you have a better solution.

    Be careful on how you link to your SVGs

    You can’t link the SVGs using the usual “src/assets” or “/assets” shortcuts, as they require a direct relative path to your app’s source folder. That’s why I had to use a ridiculously long relative path. (Again, I’m sure there’s a better way to achieve this.)

    Use the same CSS file for all of your SVGs

    I also linked the SVGs to the same CSS file since they all require the same styles, which are required to be able to style them on the fly through the parent that’s using them. By doing so, I just need to update one file to affect all of the components in the future.

    svg.component.css

    svg {
      width: inherit;
      height: inherit;
      fill: inherit;
      background: inherit;
      background-color: inherit;
    }
    
    img {
      width: inherit;
      height: inherit;
    }
    

    Note: Don’t forget to include the components in your module’s exports section so that your app can access them. And import the SVG library into the apps that use them.

    Clean up unused files

    You can delete the html and CSS files that came with your component, as your component won’t be using them.

    Use your new component

    Now, if I want to use an SVG, I just use the component’s selector:

    <ces-svg-astronaut></ces-svg-astronaut>
    

    Because I follow the same naming convention, e.g., prefix-svg-description, I usually don’t have to look up what I called it.

    What if you want to dynamically select the SVG?

    With some use cases, you won’t know ahead of time which SVG you want to select. Because you can’t make part of the selector dynamic, this feature requires setting up a separate general component.

    svg.component.ts

    import { Component, Input } from '@angular/core';
    
    @Component({
      selector: 'ces-svg',
      template: `
          <img src="/assets/images/svgs/{{ type }}.svg" [alt]="type + ' icon'" [title]="title">`,
      styleUrls: [
      './svg.component.scss'
       ]
    })
    export class SvgComponent  {
      @Input() type: string;
      @Input() title:string;
    }
    

    Note: This is why I included styles for “img” in the above CSS. To change the color of the SVG image, you need to use these styles on an element:

    mask: url('/assets/svgs/astronaut');
    background-color: orangered;
    

    Automatically add “alt” tags and dynamically add “title” tags

    I included the “alt” attribute to automatically take the icon’s type with the word “icon” so that I didn’t have to specify them individually. However, I made the “title” attribute a variable in case I wanted to add help text that shows upon hover, which will vary depending on the usage.

    Use the general SVG component

    Now, you can use the same selector and specify which icon you want to use by passing in an icon type. You only have to set up this component once, even if you add/delete SVGs to your library.

    <ces-svg type="astronaut">
  • Trick Angular into thinking all of your apps share the same source folder

    Trick Angular into thinking all of your apps share the same source folder

    If you start a new project using Angular CLI, you will notice that it comes with a default app that has a “src” folder. Angular uses this folder to act as a root location for your app so that you can access the same starting point no matter where you are in your folder structure.

    But what if you want to share the same starting point across multiple apps?

    After realizing how unbelievably awesome Angular is, you may decide that you want to create multiple apps within the same project workspace but for obvious reasons don’t want to duplicate the same assets over and over. It would seem as though you could just create one assets folder that sits one level above your apps, have all of your apps link to it and be fine. However, by clever design, Angular restricts one source folder per app out of the box that won’t recognize anything outside of its source folder, so you will have to do a little more fanagling to override those protective default behaviors.

    Step 1: Create a new project without a default app

    When you create a new Angular project, specify that you don’t want to create a default app with the following flag:

    ng new [workspace name] --create-application=false
    

    Read this great article by Todd Palmer for a further explanation of other uses of this flag.

    Note: Be sure to use a workspace name that is broad enough to encompass all of your apps because you can’t reuse that same name for an app later on. Really, that name won’t really matter but why not have it make sense regardless?

    You will end up with a seemingly barren Angular project that looks like this:

    Create Angular app without a default app

    Step 2: Create your apps

    Create as many apps to your heart’s delight using Angular CLI:

    ng generate app [app name]
    

    You will notice that the apps will be created in your “projects” folder, the same as if you were creating a library.

    Create Angular apps

    Step 3: Create a shared “src” folder at the same level as the “projects” folder

    Note: There is essentially no difference in having your apps have “projects” or “src” as their root folder location. However, if you want to easily differentiate your apps from your libraries, you will have to create a shared “src” folder and then drag your apps into that folder.

    Create src folder for your Angular apps

    Step 4: Modify “angular.json”

    Do a find-and-replace search for “projects/app-name” to be replaced by “src/app-name.”

    Note: This replacement must be made in all instances of your “angular.json” file (e.g., 44 results for 2 apps), or else your apps will break! Don’t rely on eyeballing on where to make the replacements.

    Find and replace source folder for Angular apps

    Step 5: Create an assets folder

    Create an “assets” folder that sits at the same level as your apps.

    Create shared assets folder for your Angular apps

    Step 6: Modify “angular.json”

    You now have to tell Angular the new location of your “assets” folder by replacing the current assets link with the new one. You can do this by locating the “assets” section in each app and replacing the existing code with the following lines of code:

    Before:

        "assets": [
          "src/cloud-engineering-studio/src/favicon.ico",
          "src/cloud-engineering-studio/src/assets"
        ],
        
    

    After:

         "assets": [
              { "glob": "**/*", "input": "src/assets/", "output": "/assets/" },
              "src/cloud-engineering-studio/src/favicon.ico"
            ],
    

    This replacement must be made in both locations for each app because the first location is for the app and the second one is for testing the app.

    Note: Because I want each app to have a different favicon, I kept the different favicon links. If you do so, make sure you reverse the order of the two statements.

    Step 7: Use your new “assets” folder

    Now, you can use your new “assets” folder the same way for each app, and they will all link to the same spot. For example, to link to an image, you can just write:

    "/assets/file-directory/file-name.jpg"
      
    

    To import a stylesheet that’s located in the assets folder, you can form your import statement like so:

    @import "src/assets/file-directory/file-name.scss"
    
  • How to create a social media component in Angular for all of your users

    How to create a social media component in Angular for all of your users

    Using Angular means you never have to repeat your code again! For example, I created a social media component that is account neutral so that it can be reused over and over again, even if the user has completely different accounts. I’ve outlined the steps below:

    Step 1: Create an interface called Social Media

    You should take advantage of Typescript’s strong typing capability to indicate what parameters the user will need to provide. I chose to include “link” for a hyperlink to the user’s account and “user” for the account holder’s name.

    export interface SocialMedia {
      link:string,
      user:string
    }
    

    Step 2: Create a social media component

    Use variables in your component’s HTML so that the hyperlink and icon will automatically be created for the user.

    <div class="social-media" [ngClass]="{'vertical':vertical}">
      <div *ngIf="facebook" class="social-media-icon">
        <a [href]="facebook.link" target="_blank" rel="noopener" title="view {{ facebook.user }} on facebook"><ces-svg-facebook></ces-svg-facebook></a>
      </div>
      <div *ngIf="twitter" class="social-media-icon">
        <a [href]="twitter.link" target="_blank" rel="noopener" title="view {{ twitter.user }} on twitter"><ces-svg-twitter></ces-svg-twitter></a>
      </div>
      <div *ngIf="linkedIn" class="social-media-icon">
        <a [href]="linkedIn.link" target="_blank" rel="noopener" title="view {{ linkedIn.user }} on linkedIn"><ces-svg-linkedin></ces-svg-linkedin></a>
      </div>
      <div *ngIf="medium" class="social-media-icon">
        <a [href]="medium.link" target="_blank" rel="noopener" title="view {{ medium.user }} on medium"><ces-svg-medium></ces-svg-medium></a>
      </div>
    </div>
    

    Note: You can make your component even more dynamic by substituting the social platform’s name with a variable and filling all values within a for loop, but I chose to write them out so that it’s clearer to see what’s available.

    Additional note: All of the icons on Cloud Engineering Studio were created by componentizing SVGs I purchased with my Font Awesome subscription.

    Step 3: Create Angular Inputs to accept the necessary values

    I added an additional boolean called “vertical” so the user can specify if they want the icons to be displayed inline.

    export class SocialMediaComponent  {
      @Input() facebook:SocialMedia;
      @Input() twitter:SocialMedia;
      @Input() linkedIn:SocialMedia;
      @Input() medium:SocialMedia;
      @Input() vertical:boolean = true;
    }
    

    Step 4: Use the component

    I included the social media component in the footer of my website.

    <ces-social-media ngClass="social-media-footer" [facebook]="{link: 'https://www.facebook.com/cloudengineeringstudio/', user: 'Cloud Engineering Studio' }" [twitter]="{link: 'https://twitter.com/CloudEngineeer', user: 'CloudEngineer'}" [linkedIn]="{link: 'https://www.linkedin.com/company/cloudengineeringstudio', user: 'Cloud Engineering Studio'}" [medium]="{link:'https://medium.com/@yoko_65687', user: 'Yoko Ishioka'}"></ces-social-media>
    

    And voila, here is what will get rendered:

    Angular social media component example

    Here is a link to the code in Github Gist.

  • Angular Security Vulnerability: How to fix tree-kill vulnerabilities

    Angular Security Vulnerability: How to fix tree-kill vulnerabilities

    If you’re working in Angular, you may have seen 2 new security vulnerabilities that require you to update the tree-kill package to version 1.2.2 or later. Unfortunately, the usual commands, “npm audit fix” nor “npm update tree-kill” won’t work. (Those commands will update your package.json file but not your package-lock.json file.)

    Thanks to saleem on stackoverflow, below is the solution that will fix your vulnerabilities.

    Solution

    1. Type “npm uninstall tree-kill”
    2. Go to node_modules -> @angular-devkit -> package.json and update the tree-kill version to 1.2.2
    3. Go to node_modules -> @ngtools/webpack -> package.json and update the tree-kill version to 1.2.2
    4. Type “npm install”
  • How to create a self-updating navigation menu in Angular

    How to create a self-updating navigation menu in Angular

    Rather than hardcoding hyperlinks or buttons to create your website/web app’s navigation menu, you can leverage Angular Routes to pass data to a Menu Component that decides whether or not to render that data based on conditionals! What does that mean?

    You never have to manually code navigation menus in HTML again. Instead, you can create an Angular (or plain Javascript) component, aka template, that is smart enough to create the actual code that the browser needs to render your menu.

    Example

    Ultimately, as a cloud engineer, your goal should be to create settings so others can modify your code without touching or modifying the actual code. (That is what user interface/content management system developers, like myself, have been obsessing over the past decade.)

    That’s why I chose to pass the following variables to create the menu:

    1. show: boolean – sets whether or not the navigation link gets displayed in the menu
    2. showChildren: boolean – determines whether or not to match the exact parameter of the router link when navigating the route
    3. navLabel: string – sets what the label should be for the navigation menu button
    4. navIcon: string – decides which SVG should be included for the navigation menu button
    5. title: string – sets the title attribute of the page as well as the title meta tags
    6. description: string – sets the description meta tags appropriate to the page

    Here is an example of one of my routes in JSON format:

    app-routing.module

    export const routes: Routes = [{
      path: '',
      component: PageIndexComponent,
      data: {
        show: true,
        showChildren: false,
        navLabel: 'Home',
        title: 'Cloud Engineering Studio',
        description: 'Adaptive smart learning: cloud engineering based on human behavior',
        icon: 'house'
      }
    }];
    

    Here is the HTML template I used to display the above data:

    menu.component

    <nav>
      <ng-container *ngFor="let route of currentRoutes">
         <ces-button *ngIf='route.data.show && !route.data.showChildren' ngClass='nav-button' size='small' [routerLink]="[route.path]"
    [title]="route.data.title" routerLinkActive="active" [routerLinkActiveOptions]="{exact:
    true}" >
         <ces-svgs [type]="route.data.icon" class="icon"></ces-svgs><br>{{ route.data.navLabel }} 
         </ces-button>
    
         <ces-button *ngIf='route.data.show && route.data.showChildren' ngClass='nav-button' size='small' [routerLink]="[route.path]" [title]="route.data.title" routerLinkActive="active">
         <ces-svgs [type]="route.data.icon" class="icon"></ces-svgs><br>
         {{ route.data.navLabel }} 
         </ces-button>
    
       </ng-container>
     </nav>