javascriptcssangularangular-templateangular2viewencapsulation

Dynamically adding style tags to component and re-encapsulating the styles (angular)


I am working on an AngularJS to Angular upgrade and we have a hybrid app at the moment that is being bundled up with webpack instead of just angular cli. This has led to problems where we have not been able to get css or scss files to correctly bundle up and be accessible from our upgraded components in styleUrls.

So we have been adding styles in the template with tags. The styles that need to be added look like this: (this is just a sample of some of the styles)

const styles = `
    tr.leader td,
    tr.leader td .personPopup {
        color: ${this.config.LeaderTextColor};
    }

    tr.leader td {
        background-color: ${this.config.LeaderBackgroundColor};
    }

    tr.current-user td,
    tr.current-user td .personPopup {
        color: ${this.config.TableRowCurrentUserTextColor};
    }
`

Colors can be set by users in the front end and loaded through our config for a customized experience.

Unfortunately, I haven't found a way where you can include these in a style tag in the template and access angular variables like {{config.TableRowCurrentUserTextColor}}. It doesn't resolve to the proper value.

So, I go this working by building a style tag in the component and appending it into the component on ngOnInit.

const myWidget = document.getElementById(this.widgetId) as HTMLElement;
myWidget.appendChild(styles);

This works and lets me access my component config variable, but when styles are loaded in like this through append, they are not properly encapsulated by the Emulated view encapsulation so the styles can leak out and affect other components.

Is there any way to actually access these component variables from style tags in the template? I have not found a way that works... Or is there a way to get the component to re-encapsulate itself and re-evaluate the styles to encapsulate them after appending them?


Solution

  • To emulate encapsulation all you need to do is just wrap the parent class class-wrapper-123 like this as the prefix for all your styles, and make sure your component has a div wrapping the contents with the same class. This will prevent the styles from leaking up the component tree, for leaking down the component tree.

    You have to use the > selector to target exactly the element you want to affect, this will ensure these styles do not affect any child components. It's a lot of work, hope your apps transform from hybrid to normal soon!

    const styles = `
        .class-wrapper-123 > tr.leader > td,
        tr.leader > td > .personPopup {
            color: ${this.config.LeaderTextColor};
        }
    
        .class-wrapper-123 > tr.leader > td {
            background-color: ${this.config.LeaderBackgroundColor};
        }
    
        .class-wrapper-123 > tr.current-user > td,
        .class-wrapper-123 > tr.current-user  > td > .personPopup {
            color: ${this.config.TableRowCurrentUserTextColor};
        } `