Applying dynamic styling to injected HTML in Angular 2 - css

For an Angular project I'm working on, I'm injecting HTML into a <div> like so:
<div class="myClass" [innerHTML]="htmlToInsert"></div>
The htmlToInsert contains a variety of things, notably <a> tags. Previously we were styling all these tags like so:
.myClass ::ng-deep a {
color: #f00;
text-decoration: none;
}
And this worked fine. But now I need the color of these links to be dynamically generated during component initialization, based on data coming in from elsewhere. All of the dynamic styling I've seen in Angular requires you to apply things directly to the HTML tag, but we don't have them here to work with.
How can I apply dynamic styling to HTML that is also dynamically generated? Can I directly change the CSS class somehow? Would using a pipe be the correct approach here? Is there another method I don't know about? I could maybe refactor code if there is absolutely no other way of doing this.

So if you can't modify the innerHTML you are passing in, you can achieve this functionality with a custom directive. Essentially you would tag your div that contains your innerHTML with a custom directive. That directive then looks for any anchor tags in it and changes the color based on an input.
// component.html
<div anchorColor [color]="dynamicColor" [innerHTML]="htmlToInsert"></div>
// directive.ts
#Directive({selector: '[anchorColor]'})
export class AnchorColorDirective implements OnAfterViewInit {
#Input() color: string;
constructor(private el: ElementRef){
}
// afterViewInit lifecycle hook runs after DOM is rendered
ngAfterViewInit(){
// get anchor element
let anchorEl = this.el.nativeElement.querySelector('a');
// assign color
if(anchorEl){
anchorEl.style.color = this.color;
}
}
}
Here is a working plunkr https://plnkr.co/edit/QSYWSeJaoUflP94Cy4Hm?p=preview

Related

How to apply a CSS style dynamically using React JS?

I would like to know how to apply CSS dynamically to all the elements in a page that belong to a CSS class in React JS?
Currently I am using the following:
document.querySelectorAll('.my-paragraph-class').forEach(function (x: any) {
x.style.fontSize = `${data.value}%`;
});
It works but I would like to know if there is a more efficient way instead of using document.querySelectorAll?
Also when the page loads new text, document.querySelectorAll has to be called again after the text loads, which is not ideal. I would like to know how to persist the modified CSS changes when new text is loaded dynamically?
Thank you in advance
For applying a static font size to a class you shouldn’t have to use JavaScript at all.
In your css file just do:
.my-paragraph-class: {
font-size: 10rem || whatever;
}
To dynamically add styling you could use a CSS in JS tool like styled components. For styled components you would import styled components and then write a code outside of your react function like this:
const StyledParagraph = styled.p`
font-size: 10rem || ${props => props.fontSize}
`
In your JSX you would do something like:
<StyledParagraph fontSize=“10rem”> Filler Text </StyledParagraph>

Target Angular binding with SCSS / CSS

I am migrating an application from AngularJS to Angular. I have just moved a loading-indicator component to Angular, and that has an optional modal binding to say whether its a modal loader or not.
In reality this doesnt bind to anything right now, it simply matches a CSS rule.
HTML
<loading-indicator [modal]="true"></loading-indicator>
SCSS
loading-indicator {
&[modal="true"] {
background: rgba(255,255,255,0.5);
}
}
The SCSS above targets an attribute of modal not [modal].
How do I target [modal]? I saw some answers suggesting I can escape the brackets using \\ e.g. &\\[[modal\\]] but this didn't seem to work.
You cannot. Because [modal]="true" for Angular it's an input and this will be converted to another HTML attribute.
You can use modal=true in your component and you can select this with
[modal=true] {
/*styles*/
}
But for encapsulation, you need to put this style on the root styles of the app.
It's a better solution to bind the input modal value to a [ngClass]="":
Inside the component ts loading-indicator:
#Input() modal;
On the template use:
<div [ngClass]="modal ? 'modal-background' : ''"></div>
On the styles of component:
.modal-background{
background: rgba(255,255,255,0.5);
}

How to dynamically generate CSS class and/or set its property

The title is not really a question it is more like an idea, I don't know what approach is best for my situation.
So, the problem. I have some 3rd party component that have some complex structure and styling. Some part of it has some predefined CSS class that I can override with CSS in my surrounding component. Something like this:
my component:
<div class="my-cmp-container">
<some-3rd-party-cmp></some-3rd-party-cmp>
</div>
3rd party component:
<div class="3rd-party-css-class">
...
</div>
For example, 3rd-party-css-class has style background-color: #f00, I can override it with .my-cmp-container .3rd-party-css-class { background-color: #fff; } etc. But. What if I need to set color dynamically, it's stored in a DB for example and I can't predefine each case in my class' CSS. I just have the color in hex.
In theory I can generate unique string to set as CSS class for every instance of some-3rd-party-cmp and somehow generate CSS in my component? I'm lost a little, what is the best approach for this?
Edit: Code sample to illustrate the situation https://stackblitz.com/edit/angular-kxdatq
What you are trying to do is the subject of this open issue about stylesheet binding in Angular. Until that feature is available, you can get what you want with a custom directive. Here is a directive that retrieves the checkbox element generated by ng-zorro-antd and applies two color attributes to it. The two colors are #Input properties and the directive implements OnChanges which allows to react to property binding changes.
#Directive({
selector: "[nz-checkbox][nz-chk-style]"
})
export class CheckBoxStyleDirective implements OnInit, OnChanges {
#Input("nz-chk-bkgnd") chkBkgndColor: string;
#Input("nz-chk-border") chkBorderColor: string;
private checkbox: HTMLElement;
constructor(private renderer: Renderer2, private el: ElementRef) { }
ngOnInit() {
this.checkbox = this.el.nativeElement.querySelector(".ant-checkbox-inner");
this.updateBackgroundColor();
this.updateBorderColor();
}
ngOnChanges(changes: SimpleChanges) {
if (changes.chkBkgndColor) {
this.updateBackgroundColor();
}
if (changes.chkBorderColor) {
this.updateBorderColor();
}
}
updateBackgroundColor() {
if (this.checkbox) {
this.renderer.setStyle(this.checkbox, "background-color", this.chkBkgndColor);
}
}
updateBorderColor() {
if (this.checkbox) {
this.renderer.setStyle(this.checkbox, "border-color", this.chkBorderColor);
}
}
}
Once the directive attribute selector nz-chk-style is applied to the 3rd party element, you can set the checkbox background and border colors with property binding as follows:
<span nz-checkbox nz-chk-style [nz-chk-bkgnd]="bkgndColor" [nz-chk-border]="borderColor" >
See this interactive stackblitz for a demo.
Not sure if you are using Angular but you tagged it, so I guess you are.
If you want to change only the color and nothing more, instead of having a .3rd-party-css-class class, you could just have your with an ng-style like so:
<some-3rd-party-cmp ng-style="{ color: your_color_hex_variable }"></some-3rd-party-cmp>
You can also define a whole object if styles and pass it.
You can also use ng-class and pass one or an array of class names what you want to put additionally on your component:
<some-3rd-party-cmp ng-class="[cls1, cls2, cls3]"></some-3rd-party-cmp>
<some-3rd-party-cmp ng-class="[3rd-party-css-class, someCondition ? 'another-class-name' : '']"></some-3rd-party-cmp>
In the classes you can define the css rules you want to apply and thats it.
With this solutions you can avoid having extra wrapper elements for styling purposes which is a nice thing.

Where to apply the parent theme class when using :host-context(.theme_name) in Angular2?

I'm building a themed Angular2 app. I have loads of nested components and I would like to change the theme for the whole app by changing one parent class. Let's say one theme is called "theme1". When I apply this to a very parent html-tag, all the nested child components will then switch style by using :host-context, like this:
:host-context(.theme1) .title-toc {
font-family: "bookmania";
font-weight: 700;
font-size: 20pt;
text-transform: uppercase;
color: #a5a5a5;
}
However, where to put this class="theme1"? When I put it to <body>, it worked so the code itself works, but I need to change the theme dynamically so it has to be inside some Angular component instead where I can use [ngClass]. For example inside AppComponent. But when I put it inside app.component (for example router-outlet or any other wrapping tag that should wrap my whole app), it didn't work anymore.
Do I have to make it deep with ::ng-deep somehow or what could be wrong?
You can bind to your root component element's class attribute like this:
export class AppComponent implements OnInit {
#HostBinding('attr.class') mainClass = 'theme1';
changeTheme(theme) {
this.mainClass = theme;
}
}
:host-context() sees the classes applied on the document body. You can set the class of body with JavaScript after the application has loaded, like this:
var myclass = "my-theme-name";
document.querySelector('body').classList.add(myclass);
This way you can load the theme name even from server and then apply it to the body from any Angular2 component via JS:
:host-context(.my-theme-name) .my-theme-specific-style

Dynamically change element styles via custom properties?

For example, you can change the ink colour in paper-tabs by changing --paper-tab-ink: var(--accent-color);. Is it possible to change the value of the CSS custom properties dynamically similar to how you can toggle a class or change the style in JS?
There are different ways to do this, but a simple answer is to use the Polymer.updateStyles() method after making your class changes.
For example, let's say your styles are:
<style>
.yellow x-example {
--light-primary-color: #fdd85f;
}
.red x-example {
--light-primary-color: red;
}
</style>
and you want to make the component use the styles in the .red class. You simply add it as you normally would in javascript, then be sure to also use this function to actually update it on the page.
<div class="yellow" onclick="this.className='red'; Polymer.updateStyles()">
<x-example></x-example>
</div>
Yes, first get the object of your custom element. Then get the customStyle object. Add a style to that object. And then run element.updateStyles();
t.clickListener= function(e) {
var t = Polymer.dom(e).localTarget; //retarget if needed
t.customStyle['--the-color-etc'] = 'pink';
t.updateStyles(); // mandatory for the CSS variables shim
};
See the docs

Resources