Author: Yoko Ishioka

  • The Biggest Limitation of CSS Custom Properties (and Why I Still Use Them)

    The Biggest Limitation of CSS Custom Properties (and Why I Still Use Them)

    Over the past year, I’ve shifted from relying heavily on Sass variables to using CSS custom properties (aka CSS variables) almost exclusively. Why? Because unlike Sass variables, CSS variables can be overridden dynamically at runtime.

    This difference changes how you structure your styles—and in many cases, it makes your CSS more flexible and maintainable.

    The limitations of Sass variables

    Sass variables are powerful, but they come with one key limitation: they’re compiled away into static values.

    That means once your Sass is compiled into CSS, those variables don’t exist anymore—they’ve been replaced with hard-coded values. If you need different values under different conditions (like responsive breakpoints, dark mode, or user preferences), you can’t “update” the variable. Instead, you end up creating multiple variables for each scenario.

    For example, let’s say we want paragraph text to be 14px on mobile and 16px on desktop:

    $p-sm: 14px;
    $p-md: 16px;
    
    p {
      font-size: $p-sm;
    
      @media (min-width: $breakpoint-md) { 
        font-size: $p-md;
      }
    }

    This works fine, but notice we had to define two separate variables. If you scale this pattern across font sizes, colors, spacing, and themes, you quickly end up with a lot of extra Sass variables.

    How CSS property overrides can solve the problem

    CSS custom properties, on the other hand, are inherited and live in the browser at runtime. That means their values can be updated, overridden, and scoped dynamically—without recompilation.

    Here’s the same font-size example written with CSS custom properties:

    :root {
      --font-size-p: 14px;
      @media (min-width: $breakpoint-md) {
        --font-size-p: 16px;
      }
    }
    
    p {
      font-size: var(--font-size-p);
    }

    Now we only need one variable, and we simply override it at the media query. The p element always references --font-size-p, and the browser handles the cascade.

    This pattern scales beautifully. Whether you’re building responsive typography, theming with light/dark mode, or even reacting to user interactions, CSS variables stay flexible.

    There is still one thing CSS variables can’t do

    Sass variables vanish at compile time, while CSS custom properties live on at runtime and adapt as styles cascade. That flexibility is powerful, but it comes with a catch—media queries are evaluated at parse time, before custom properties exist.

    In practice, this means you can’t write:

    @media (min-width: var(--breakpoint-md)) {
      /* this will fail */
    }

    The parser will ignore it entirely. Instead, the right pattern is to use Sass variables.

    @media (min-width: $breakpoint-md) {
      /* this will work */
    }

    * Edited for improved clarity with a little help from AI

  • How to fix an Angular component that won’t stick

    How to fix an Angular component that won’t stick

    If you’re working in a component-driven framework like Angular, you might notice one day that your components start ignoring your router’s scroll events and are no longer sticky. All of a sudden, you find the only way to keep your components from scrolling off the page is by assigning them a fixed position with CSS. The only reason this works is because the closest positioned ancestor your components can find is the last possible resort, Document, which represents the whole DOM.

    What do you mean by ‘sticky’?

    Just to be clear, I’m referring to the CSS property ‘position’ that has the following possible values:

    1. static – no special positioning
    2. relative – normal positioning
    3. fixed – always in the same place, regardless of scroll
    4. absolute – always in the same place relative to its closest ancestor that has positioning
    5. sticky – always in the same place after it reaches a top, right, bottom, or left scroll position

    Using sticky is like fixed + relative.

    Fixed is cool if you only care about positioning in respect to the viewport. But using it means your components no longer take space in the DOM, and therefore no longer have any relation to the positioning of everything else in the DOM. This runs the risks for things like overlapping, cropped and/or empty content.

    What kinds of issues are we talking about?

    1. Applying sticky has no effect on component. It scrolls out of viewport with everything else.
    2. Applying sticky has partial effect on component. It stays in the right spot until you scroll past the entire length of the screen.

    Steps to troubleshoot

    Make sure you’ve specified at least a top, left, right or bottom position on the component you’re trying to make sticky.

    You can used fixed or relative units, but you must specify at least a top, left, right or bottom position or else it won’t know where to stick.

    Make sure your component’s container has a defined height or width.

    Your component needs to know the dimensions of its container in order to calculate where the sticking point is in relation to itself. It will continue checking its ancestors until it finds one with positioning.

    Make sure your component’s container does NOT have overflow set to hidden.

    Once you’ve scrolled your component out of the viewport, it is considered overflow. Therefore, hiding the overflow on a sticky element that needs overflow to stick won’t work.

    But what if the height is dynamic?

    Let’s say you want the footer to always start at the bottom of the page, even if there’s not enough content to push it down. If you set the component’s container height to 100% and it doesn’t stretch to the end of the page, you might try 100vh. While that works and your component might be sticky at first, you may notice it stops sticking after you’ve scrolled past the full length of the page.

    To solve this problem, set the following styles on your component’s container:

    .container {
       ...
       min-height: 100vh;
       height: 100%;
     }
    
  • Cloud Engineering Studio has a new URL for Medium

    Cloud Engineering Studio has a new URL for Medium

    Cloud Engineering Studio has a new URL for Medium! Be sure to update your bookmarks with the new link: https://medium.com/@cloud_engineering_studio

  • How to fix an RPC failure in Git

    How to fix an RPC failure in Git

    If your pushes to GitHub keep failing only a particular commit, you may be encountering this error:

    RPC failed; HTTP 400 curl 22 The requested URL returned error: 400 Bad Request

    This happens when you are trying to push a stream to your remote branch using Git that’s larger than the allowed budget. For example, I ran into this error when trying to commit a bunch of screenshots and a video at once.

    Luckily, I found this stackoverflow that had the solution! The fix is to up the buffer limit for that particular commit. (524288000 refers to the size limit):

    git config http.postBuffer 524288000
    git pull
    git push
  • How to typecast an IterableIterator into an Array using JavaScript

    Use case

    If you’re using JavaScript Map and want to access its values as an array, you might first think to get its values like Object.values:

      const map: Map<string, BlogArticle> = new Map<string, BlogArticle>();
      ...
      // DOES NOT WORK
      articles: BlogArticle[] = map.values;
    

    There’s a slight problem

    In order to typecast a custom object into an array, the object must implement the iterable protocol and have the Symbol.iterator method. When called without arguments, this method must return an iterator that has the next() method defined.

    Until it does, the two objects are totally incompatible. You will see the following error:

     Type '() => IterableIterator<BlogArticle>' is not assignable to type 'BlogArticle[]'.
    

    Solution

    1. Call Map.prototype.values() so that it returns an Iterator object with values
    2. Use JavaScript’s spread syntax to make a shallow copy
    // DOES WORK
    articles: BlogArticle[] = [...map.values()];
  • Migrate your build scripts for sexy Angular SSR

    Migrate your build scripts for sexy Angular SSR

    If you’re migrating build scripts from an older project into one that uses Angular 17+, you might run into the error:

    Project target does not exist.
    

    Why it happens

    Angular 17 no longer has a separate build command for SSR! Also, it will complain if you try to use the –production flag.

    The fix

    In package.json, remove the script with ‘build:ssr’ as the key. It will be located under ‘scripts’:

    "scripts": {
      "build:ssr": ".." -- you need to delete this key-value pair
    }
    

    Prerendering

    As a bonus, Angular will also run the prerender command automatically when it builds. (It’s a setting in angular.json if you want to change it.)

  • Stop bulldozing your npm cache clean

    Stop bulldozing your npm cache clean

    As of version 5, npm (already one of the best packaging managers) became ‘virtually self-healing’. This means you should no longer have to face those terrifying moments of desperation when you say what the fuck to the CLI that is emphatically assuring you there is no reason to force a cache clean because everything is in perfect order and the bug must undoubtedly be something on your end.

    But if the versions are all lined up, the dependencies have been dealt with, vulnerabilities squashed, and you’re still running into errors, there might be only one thing left…

      npm cache clean --force
    

    What is wrong with forcing a cache clean?

    BUT WAIT! There’s a reason why the CLI says this is a very futile and foolish thing to even try. It’s the same as running the uninstall and starting all over again. If there’s any problem in your configuration, you won’t have any backups to get you through.

    Verify the cache instead

    Now, there’s a safer alternative to try first:

    npm cache verify
    

    This command tells npm to figure out what it needs by verifying the integrity of your install packages to determine what is trash or not.

    Read this excellent article that explains the differences in the npm cache commands for more information.

  • Why Angular components, pipes, and directives should stand alone and what that means for modules

    Why Angular components, pipes, and directives should stand alone and what that means for modules

    Don’t get me wrong – I really love Angular modules. They are like that office manager who keeps track of everything in their head and runs everything like a nearly infallible machine.

    But if you are developing a UI component library, it gets very tedious to have to create a module just so something can use your one component. Modules are meant for groupings, and they are great at that. But for generic components, they can be overkill.

    What difference does a module make?

    Before standalone, the only way to use a component, directive, or pipe was to declare them in a module that had to be imported by whoever wanted to use them. If you declared multiple items in the same module, it became hard to track what was being imported for what and what was no longer being used. This ran the risk of importing things unnecessarily, causing bloated bundle sizes and subsequently slower app times.

    As of Angular 14, the brilliant Angular team added standalone functionality. In other words, components, directives, and pipes can import what they need directly without modules just like any other import.

    You can import components, directives or pipes directly to use them!

    Lazy load with less files

    Before standalone, the only way to lazy load was to create a module with routing to serve as a locator to get to the correct component without any knowledge of what that component is. Now we can point to the component with imports instead of routes.

    Should I switch everything to standalone?

    I can’t think of a good reason to declare components, directives, and pipes with modules ever again. Please let me know if you can think of reasons why you should.

    What good are modules then?

    Modules are still super useful, just less needed…like a lot less needed. Actually, if you use them just for grouping and are no longer concerned with the dependencies of what you’re declaring, how to best use modules will become a lot clearer.

    For example, if you’re importing the same group of components, directives, and/or pipes across multiple places or want to alter what’s being imported in one spot, you should bundle them in a module. Don’t use the module to declare anything. Only use it for importing/exporting what you need.

    Another example would be if you are using things that rely on each other, like a component that uses a directive to interact with its children. But even then, just use the module to import/export.

    Which Angular decorators can be standalone?

    • Components
    • Directives
    • Pipes

    How do you become standalone?

    By Angular CLI

    ng g c componentName --standalone
    

    By component metadata

    @Component({
      standalone: true,
      selector: 'your-selector',
      imports: [
        CommonModule,
        ExampleModule
      ],
      ...
    })
    

    If you’re using an existing decorator, you need to delete it from the module that’s declaring it. You can be declared by a module or have standalone turned on but not both.

  • The right way to componentize SVGs for your Angular app

    The right way to componentize SVGs for your Angular app

    Last year, I wrote an article about how to create an SVG library in Angular by making components that link to SVG files instead of HTML templates. Shortly afterwards, my boss tasked me with finding a solution to the excessive DOM size problem, which we discovered was being caused by all of the elements that make up our SVGs.

    How to create references instead of actual clones to your SVGs

    After doing extensive research and trying different methods, I came across the ‘symbol’ and ‘use’ HTML elements. This article gives great in-depth explanations into why this is the correct approach.

    If you have ever worked in Flash…I mean Animate, you will remember how awesome symbols were. They enabled you to create one symbol and then reference that symbol so any changes you did to the original were cascaded to all of its references. By using ‘symbol’ and ‘use’, you are essentially doing the same thing.

    To make this solution even more enticing, the ‘use’ element acts as a container and stores its children in the shadow DOM. In other words, your SVG elements no longer count towards your DOM size.

    The drawbacks of the shadow DOM and how to overcome them

    There are some drawbacks though with using a closed shadow DOM. For example, you can’t access or manipulate the SVG’s viewBox through JavaScript. Therefore, you won’t be able to do things like pull out the SVG’s exact dimensions without inspecting the element through a browser or updating the paths of the SVG without an image editor.

    This can be problematic because if you don’t set the SVG’s width and height, it will automatically have a width of 300 pixels and a height of 200 pixels. If your SVG is considerably smaller, you will see a lot of blank space around the SVG. Because we’re going to create one component for all of your SVGs, you can’t set one universal viewBox to handle this problem.

    The viewBox isn’t the only thing that’s inaccessible. All of the SVG’s styles are as well. This is actually a pretty good benefit because a change to the original would be applied to all of its references.

    Don’t fret; we can easily overcome these hurdles by making our SVG generic and adaptable. We can use properties like ‘inherit’ on the SVG’s width and height and ‘currentColor’ for its fill and outline so they can be set through a parent or ancestor.

    How to create the SVG component

    Step 1: Create a graphic and export it as an SVG

    Note: In order to individually style your SVG and have it scale gracefully to any size, you must use actual paths instead of embedded images for your SVGs.

    For example, I made my logo with vector art using Illustrator. This is what it looks like when I export just the cloud as an SVG:

    <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 229 149.04">
      <path d="M189.37,142a12.35,12.35,0,0,1-3.2-3c-1.26-1.65-2.31-3.5-3.71-5a12.12,12.12,0,0,0,2.48,4.77A12.55,12.55,0,0,0,189.37,142Z" transform="translate(0 0)" style="fill:#ccc"/>
      <path d="M192.89,142.94a12.66,12.66,0,0,1-3.52-1A6.19,6.19,0,0,0,192.89,142.94Z" transform="translate(0 0)" style="fill:#ccc"/>
      <path d="M182.46,133.91a11.83,11.83,0,0,1-.4-3.65,5,5,0,0,0-1.2,2.21,1.51,1.51,0,0,1,.26.18A10.64,10.64,0,0,1,182.46,133.91Z" transform="translate(0 0)" style="fill:#ccc"/>
      <path d="M229,76.35a28.33,28.33,0,0,0-19-26.61,14.29,14.29,0,0,0,1.32-6,14.8,14.8,0,0,0-14.93-14.67h-.09a15.07,15.07,0,0,0-10.47,4.2,27.92,27.92,0,0,0,.45-5C186.28,12.67,173.36,0,157.43,0a28.94,28.94,0,0,0-25.81,15.64,24.52,24.52,0,0,0-2.7-.2A29.22,29.22,0,0,0,113.33,11C97.4,11,84.48,23.62,84.48,39.25a28.41,28.41,0,0,0,.15,2.92c-2.79-.57-5.76-1.72-8.51-2.21-4.42-.54-8.85,2.71-12.19,5.47a36.36,36.36,0,0,0-3.47,3.66A31.66,31.66,0,0,0,52.9,67.61c-.56,13,10,21.14,18.8,29.27,1.1,1.63,0,4.88,1.1,5.42,3.32,3.26,6.64-2.71,6.08-6-1.1-4.88-6.08-6-8.84-9.22-3-3.25-2.91-7.58-1-11.31a14.81,14.81,0,0,1,2.49-3.44c3.45-4.22,9-3.68,13.41-2.59,7.19,2.17,14.93,6,16.6,13.59-.06,1.62-.15,3.29-.28,5-1.06,13.49-4.32,26.82-15.67,35.58-8.37,6.46-19.16,9.14-29.75,10.3s-21.32.93-31.87,2.4c14.49-4.51,29.91-5.73,44.28-10.58,7.48-2.53,14.82-6.18,19.89-12.12a35.54,35.54,0,0,0,7.68-17.8c1.05-6.79.27-14.5-4.81-19.23-3.5-3.26-9.84-4.22-12.87-.53s-1.07,9,1.5,12.82A19.63,19.63,0,0,1,78,83.06a6.67,6.67,0,0,1,2.37-5.66c2.14-1.58,5.3-1.22,7.48.29a13.06,13.06,0,0,1,4.46,6.37,28.19,28.19,0,0,1-3,25.38C82.12,120,68.58,124.6,55.72,126.23,46.5,127.4,37.17,127.4,28,128.57S-12.06,144.48,5.1,148c16,3.23,88.05-1.41,106.45-9.34s34.35-20.07,54.15-24.38c5-1.09,9.4,0,13.82,2.71,7.19,4.34,6.08,11.92,6.08,18.43,0,2.17,2.21,4.88,4.43,4.88,7.74,0,13.82-7.59,14.37-14.64-.56,7.19-5.48,20.7-15.52,16.76-2.32-.91-4.12-2.73-5.85-4.5a6.9,6.9,0,0,0,2.39,3.76,13.57,13.57,0,0,1-2.71-2.53,9,9,0,0,1-2-4.36,6,6,0,0,1,.14-2.27,1.53,1.53,0,0,0-.51-.21,1.2,1.2,0,0,0-1.17.87c-1.87,5.72,7.7,10.77,12.16,11.31,9.91,1.22,17.12-7.21,19.47-15.79a43.86,43.86,0,0,0,1.13-5.79,23.09,23.09,0,0,0,0-7.42c-.18-1.22-.43-2.44-.72-3.66a21.87,21.87,0,0,0-1.13-4.29c-.48-1.38-1-2.76-1.39-4.12A28.37,28.37,0,0,0,229,76.35Zm-39.19,67.34a21.89,21.89,0,0,0,5.3.41,13.85,13.85,0,0,1-5.32-.41Z" transform="translate(0 0)" style="fill:#ccc"/>
     </svg>
    

    Step 2: Prepare your SVG

    The next step is to add ‘symbol’ tags to the elements inside of your SVG and to move the viewBox to the newly created ‘symbol’ element. You must also add an id attribute to the symbol so that you can call it externally.

    If you want to change the colors differently for each reference, you have to set the ‘fill’ and ‘outline’ attributes to ‘currentColor’ or ‘inherit’. Use ‘currentColor’ to set the color through the ‘color’ property. Use ‘inherit’ to set the color through the ‘path’ property.

    I also learned a trick from using Font Awesome icons. You can automatically make the SVG be two-colored by setting the opacity of the secondary color.

    Now, your SVG will look something like this:

    <svg xmlns="http://www.w3.org/2000/svg">
      <symbol id="ces-logo-cloud"  viewBox="0 0 228.98 148.98">
        <path opacity="0.4" d="M189.37,142a12.22,12.22,0,0,1-3.2-3c-1.26-1.65-2.31-3.5-3.71-5a12.12,12.12,0,0,0,2.48,4.77A12.6,12.6,0,0,0,189.37,142Z"/><path opacity="0.4" d="M192.89,142.94a12.74,12.74,0,0,1-3.52-1A6.2,6.2,0,0,0,192.89,142.94Z"/><path opacity="0.4" d="M182.46,133.91a11.83,11.83,0,0,1-.4-3.65,5,5,0,0,0-1.2,2.21,2.4,2.4,0,0,1,.26.18A10.68,10.68,0,0,1,182.46,133.91Z"/><path fill="currentColor" d="M229,76.35a28.34,28.34,0,0,0-19-26.61,14.29,14.29,0,0,0,1.32-6A14.8,14.8,0,0,0,196.3,29.07a15.06,15.06,0,0,0-10.47,4.2,28.12,28.12,0,0,0,.45-5C186.28,12.67,173.36,0,157.43,0a28.92,28.92,0,0,0-25.81,15.64,24.52,24.52,0,0,0-2.7-.2A29.18,29.18,0,0,0,113.33,11C97.4,11,84.48,23.62,84.48,39.25a28.26,28.26,0,0,0,.15,2.92c-2.79-.57-5.76-1.72-8.51-2.21-4.42-.54-8.85,2.71-12.19,5.47a37.46,37.46,0,0,0-3.47,3.66A31.68,31.68,0,0,0,52.9,67.61c-.56,13,10,21.14,18.8,29.27,1.1,1.63,0,4.88,1.1,5.42,3.32,3.26,6.64-2.71,6.08-6-1.1-4.88-6.08-6-8.84-9.22-3-3.25-2.91-7.58-1-11.31a14.86,14.86,0,0,1,2.49-3.44c3.45-4.22,9-3.68,13.41-2.59,7.19,2.17,14.93,6,16.6,13.59-.06,1.62-.15,3.29-.28,5-1.06,13.49-4.32,26.82-15.67,35.58-8.37,6.46-19.16,9.14-29.75,10.3s-21.32.93-31.87,2.4c14.49-4.51,29.91-5.73,44.28-10.58,7.48-2.53,14.82-6.18,19.89-12.12a35.52,35.52,0,0,0,7.68-17.8c1.05-6.79.27-14.5-4.81-19.23-3.5-3.26-9.84-4.22-12.87-.53s-1.07,9,1.5,12.82A19.62,19.62,0,0,1,78,83.06a6.66,6.66,0,0,1,2.37-5.66c2.14-1.58,5.3-1.22,7.48.29a13.1,13.1,0,0,1,4.46,6.37,28.18,28.18,0,0,1-3,25.38C82.12,120,68.58,124.6,55.72,126.23,46.5,127.4,37.17,127.4,28,128.57S-12.06,144.48,5.1,148c16,3.23,88.05-1.41,106.45-9.34s34.35-20.07,54.15-24.38c5-1.09,9.4,0,13.82,2.71,7.19,4.34,6.08,11.92,6.08,18.43,0,2.17,2.21,4.88,4.43,4.88,7.74,0,13.82-7.59,14.37-14.64-.56,7.19-5.48,20.7-15.52,16.76-2.32-.91-4.12-2.73-5.85-4.5a6.88,6.88,0,0,0,2.39,3.76,13.55,13.55,0,0,1-2.71-2.53,8.92,8.92,0,0,1-2-4.36,6,6,0,0,1,.14-2.27,1.42,1.42,0,0,0-.51-.21,1.19,1.19,0,0,0-1.17.87c-1.87,5.72,7.7,10.77,12.16,11.31,9.91,1.22,17.12-7.21,19.47-15.79a44.22,44.22,0,0,0,1.13-5.79,23.09,23.09,0,0,0,0-7.42c-.18-1.22-.43-2.44-.72-3.66a21.43,21.43,0,0,0-1.13-4.29c-.48-1.38-1-2.76-1.39-4.12A28.37,28.37,0,0,0,229,76.35Zm-39.19,67.34a21.82,21.82,0,0,0,5.3.41A13.73,13.73,0,0,1,189.79,143.69Z"/>
      </symbol>
    </svg>
    

    Step 3: Create a component that will fetch your SVGs

    svg.component.ts

    import { Component, Input, OnInit } from '@angular/core';
    import { SvgSize } from './svg';
    
    @Component({
      selector: 'ces-svg',
      templateUrl: './svg.component.html',
      styleUrls: ['./svg.component.scss']
    })
    export class SvgComponent implements OnInit {
    
    link: string = '';
    
    @Input() size: SvgSize | undefined;
    @Input() type: string = '';
    @Input() dropShadow: boolean = true;
    
    constructor() {
    }
    
    ngOnInit(): void {
      this.link = `/assets/images/svgs/${this.type}.svg#${this.type}`;
    }
    

    It’s crucial to remember you can’t just link a file to the SVG. You must add a hash tag and the name of your id to the end of the file name after the file extension. You can save on http requests if you house multiple SVGs in one file, aka sprites. But you must use unique ids for each SVG in the file.

    Also, if you want to add classes to apply styles that fit exactly to the shape of the SVG like drop shadows or borders, you have to add them directly to the SVG element or else they will be applied to the component host. However, you can define those classes in the component or as a global class.

    svg.component.html

    <svg [ngClass]="{ 'drop-shadow' : dropShadow }" [class]="size ? size : ''" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
      <use [attr.xlink:href]="link" [attr.href]="link" />
    </svg>
    

    I’m specifying the two href attributes because ‘xlink:href’ is being deprecated and is just there for backwards compatibility.

    svg.component.scss

    @import 'variables';
    
    svg {
      width: inherit;
      height: inherit;
    }
    
    .xs {
      width: $icon-xs-sm;
      height: $icon-xs-sm;
    }
    
    .sm {
      width: $icon-sm-sm;
      height: $icon-sm-sm;
    }
    
    .md {
      width: $icon-md-sm;
      height: $icon-md-sm;
    }
    
    .lg {
      width: $icon-lg-sm;
      height: $icon-lg-sm;
    }
    
    @media (min-width: $breakpoint-md) {
      .xs {
        width: $icon-xs-md;
        height: $icon-xs-md;
      } 
    
      .sm {
        width: $icon-sm-md;
        height: $icon-sm-md;
      }
    
      .md {
        width: $icon-md-md;
        height: $icon-md-md;
      }
    
      .lg {
        width: $icon-lg-md;
        height: $icon-lg-md;
      }
    }
    

    Don’t forget to set the width and height to ‘inherit’ using the ‘SVG’ selector or else you won’t be able to set them outside of the component.

    In order to get around the problem of not being able to view or set the viewBox per reference, I use art boards with square dimensions when exporting SVGs. That way, I don’t have to worry about getting the exact width/height ratio correct and can easily avoid distorted graphics.

    In order to enforce standardized sizes, I also created different classes for each size. I defined them using variables so different apps that use this component can have their own standardized sizes.

    Example Usage

    Alas, here is an example of how to use the SVG component we just created in a button:

    <button class="inherit" [ngClass]="{ 'active' :  active }" (click)="toggleEdit()">
      <ces-svg size="xs" name="pencil"></ces-svg>
    </button>
    

    The SVG’s styles that we set earlier will automatically inherit those of the button. But they can also be set by adding a class to the SVG component.

    Source Files

    Here is a link to the source files. Please send me any thoughts or ideas for improvement. Thanks!

  • Set your parents free with Angular’s dependency injection

    Set your parents free with Angular’s dependency injection

    If you’re using a modular, component-driven architecture, one of the greatest challenges you will face is how to enable communication between parent and child components/directives. On the one hand, you want each component/directive to be independently focused on their purpose. On the other hand, you need them to work together as though they were built as a cohesive unit.

    Luckily for us, Angular provides everything you need for any type of situation!

    If your parent view contains the children, a common approach is to use the @Input() and @Output() decorators. To communicate with the parent, you can emit custom events through your children. To communicate with the children, you can pass properties that are bound to your parent.

    You can also use @ViewChild/@ViewChildren and @ContentChild/@ContentChildren to break down the parent/child walls. (I will discuss these powerful decorators in future articles.)

    If your parent view doesn’t contain the children directly or perhaps there is no relationship at all, you can use observables in a service to communicate data. That way, it doesn’t matter what the actual relationship is because anyone who injects the service can update and subscribe to the observables.

    Why use dependency injection to find a parent

    I created a tasking system that displays each task in a table row component. I also created button components to take actions on the table row. However, I didn’t want to include the button components in the table row component so they could be used regardless if they were in a table or not.

    The problem I faced was how to assign the button components a specific ID without having to pass the ID as an input over and over. For example, here is the HTML for the table row component that iterates over an array of tasks.

        <ces-table-row *ngFor="let task of tasks; let index = index;" [id]="task.id" [index]="index">
        <ces-table-column class="justify-center">
            <ces-view-pin [active]="task.pinned"></ces-view-pin>
            <ces-view-delete></ces-view-delete>
        </ces-table-column>
        <ces-table-column class="justify-center">{{ task.id }}</ces-table-column>
        <ces-table-column class="justify-center">
            <ces-task-menu-statuses [default]="task.status"></ces-task-menu-statuses>
        </ces-table-column>
        <ces-table-column>{{ task.dateUpdated | date: 'short' }}</ces-table-column>
        <ces-table-column>{{ task.title }}</ces-table-column>
        <ces-table-column>{{ task.notes }}</ces-table-column>
    </ces-table-row>
    

    The button components I mentioned use the selectors ‘ces-view-pin’ and ‘ces-view-delete’. I wanted these components to only be responsible for the state of the button interaction and to send a message to a service when they are pressed. That way, I could avoid having to repeat redundant code each time they are used and they would behave the same way no matter who was using them.

    Since the table row component is getting the task ID, I needed a way to navigate up the hierarchy to access it. Thankfully, Angular provides an easy way to search the hierarchy.

    How to search for a parent interface

    Step 1: Create an abstract class

    I started by creating an abstract class that contained the properties I wanted the button components to be able to access.

    export abstract class View {
      abstract id: number;
      abstract index: number;
    }
    

    Step 2: Implement the abstract class and add the provider

    The next step is to implement the abstract class and to provide a reference for anyone who tries to inject it.

    import { Component, forwardRef, Input } from '@angular/core';
    import { View } from 'projects/view/src/public-api';
    
    @Component({
      selector: 'ces-table-row',
      templateUrl: './table-row.component.html',
      styleUrls: ['./table-row.component.scss'],
      providers: [{
        provide: View, useExisting: forwardRef(() => TableRowComponent)
      }]
    })
    export class TableRowComponent implements View {
      @Input() id!: number;
      @Input() index: number = -1;
    
      constructor() { }
    }
    

    Step 3: Inject the interface into the child component/directive

    The last step is to inject the View Class using the @Optional() decorator to any child who needs to access the parent’s properties/methods that was defined in the abstract class.

    export class ViewDeleteComponent {
    
      constructor(
        protected _viewService: ViewService,
        @Optional() public view: View
      ) { }
    
      delete() {
        const message: ApiChange = {
          id: this.view.id,
          index: this.view.index,
          action: 'delete',
        }
    
        this._viewService.setChange(message);
      }
    }
    

    View this github gist if you want to see the code for all three components.