How to set up styleClass inside ts file? - css

I want to create css element inside ts file and pass it as a styleClass to PrimeNg Toast.
var style = document.createElement('style');
style.innerHTML = '::ng-deep .cssClass { background-color: #e62323; border: solid #8A427A; border-width: 0 0 0 6px; color: #2c1e30; }';
this.messageService.add({severity:'custom', summary:'Service Message', detail:'Via MessageService', styleClass: 'cssClass', sticky: true});
The above code is not working for me, style is not applied.
Could someone help me with that?
EDIT: I tried to catch the p-toast and append style but it's not applied still.
setTimeout(() => {
let message = document.getElementsByClassName('p-toast-message-custom')[0];
message.appendChild(style);
}, 100)

In Angular we should not set a style with directly DOM manipulation. The reason is:
Angular cannot detect changes if you do this by hand
Angular render components and create and update the DOM.
So its possible, but not a good way. In your case, set append the element to the DOM. Then it will works, I think.
The Angular Way
Each component has it one contained CSS/SCSS.
What you can do is to use Angulars board means, like ngStyle, ngClass and so on. example:
<div [ngStyle]="{'background-color':'green'}"></<div>
You can do it with property binding, too:
// Code
getColor(country) { (2)
switch (country) {
case 'UK':
return 'green';
case 'USA':
return 'blue';
case 'HK':
return 'red';
}
}
// Html
<div [ngStyle]="{'color':getColor(person.country)}">Test</div>
ngClass does the same but let you set a class flexible to any component.
// Code
val: number = 9;
// Html
<td [ngClass]="val > 10 ? 'red' : 'green'">{{ val }}</td>
Link to ngClass docu, link to ngStyle docu.

To add a class to a message using PrimeNG's MessageService, you can use the add method and provide an optional options object that includes a styleClass property.
Here's an example code snippet that demonstrates how to add a class to a message:
import { Component } from '#angular/core';
import { MessageService } from 'primeng/api';
#Component({
selector: 'app-my-component',
template: `
<button (click)="showSuccess()">Show Success Message</button>
`,
providers: [MessageService]
})
export class MyComponent {
constructor(private messageService: MessageService) {}
showSuccess() {
this.messageService.add({severity:'success', summary:'Success', detail:'Message Content',
options: {styleClass: 'my-custom-class'}});
}
}
.my-custom-class {
background-color: #b3ffb3;
color: #006600;
border: 1px solid #006600;
border-radius: 5px;
padding: 10px;
}

Related

How can I style :host element dynamically based on parameter from #Input?

Usually, I can style the very root of my component by using the :host pseudo style like this.
:host{ border: 1px solid gold; }
But how shold I handle if said style is supposed to be set dynamically, based on the parameters passed to #Input?
The only way I can think of at the moment is to add an auxilliary DIV and style it like so.
<div [ngClass]="styleMeDynamically"> ... </div>
Is there a way to apply a style dynamically directly on the host without the injected DIV?
I've found this suggestion but it requires explicitly stating the classes and connecting them to separate inputs. I'd like to get a config object as passed in parameter and bind the styling using [ngClass] to retail full flexibility.
Probably #HostBinding decorator can help you. It allows to bind any host attribute including class and style. For example:
#Component({ ... })
export class MyComponent {
// you can conditionally add a class to the host element
#Input()
#HostBinding('class.large')
large = false;
// it's possible to bind a style as well
#Input()
#HostBinding('style.border.px')
borderWidth = 1;
#Input()
green = false;
// and you can use a getter
#HostBinding('style.border-color')
get borderColorStyle() {
return this.green ? 'green' : 'black';
}
}
Since angular 9 it should be possible even to bind a CSS variable, see Improved CSS class and style binding section of the 9 version release article.
<div [style.--main-border-color]=" '#CCC' ">
<p style="border: 1px solid var(--main-border-color)">hi</p>
</div>
What you can do is,
Create a custom directive that will accept a style object. and inside that directive, you can get the reference of host element and modify its style.
Here is a Demo
And here is a quick explanation.
Create a directive as which will accept a style object.
import {Directive,TemplateRef,ElementRef,OnChanges,SimpleChanges,OnInit,Renderer2,DoCheck,Input} from "#angular/core";
#Directive({
selector: "[appSetStyle]"
})
export class SetStyleDirective implements OnInit, OnChanges {
#Input() appSetStyle: { [key: string]: any } = {};
constructor(private elementRef: ElementRef<HTMLElement>) {}
ngOnInit(): void {}
ngOnChanges(changes: SimpleChanges): void {
this.applyStyles();
}
applyStyles(): void {
if (this.appSetStyle) {
for (const key in this.appSetStyle) {
this.elementRef.nativeElement.style[key] = this.appSetStyle[key];
}
}
}
}
Use that style object with any html element or any other component in your project.
<app-header [appSetStyle]="dynamicStyles"></app-header>
If you don't want to make a directive then you can inject the ElementRef inside the component itself which you want to style.
ElementRef is the what you need to use to get the reference of host.
I hope this will help.

Angular 6: How to change page background-color dynamically

I work on an Angular 6 app (with Bootstrap 4) and need to change the page background color depending on which page the user enters. Default is white, but for login and registration screen the page color needs to be blue.
What I found so far:
in ngAfterViewInit() using
this.elementRef.nativeElement.ownerDocument: this approach makes the
app more vulnerable to XSS attacks and I want to avoid that.
Set View Encapsulation to None in app.component.ts: this way I can
set the body color in the app.component, that is 1 step forward.
So, now I have in my app.component.css:
body {
background-color: blue;
}
Question:
How can I change that color value (in app.component) using a variable?
With [ngStyle] I can not reach the body background-color.
Maybe using a css variable? but how can I change the value of that css variable dynamically?
I'm new to Sass, but might this offer a solution?
My question is different from the other question on this subject as I need to be able tochange the color value dynamically.
use render2 and set class to body using document object
app.component.ts
constructor(private renderer: Renderer2) {
this.renderer.addClass(document.body, 'body-class');
}
Note: if you are toggling classes, just remove previous class before assigning new class
The way I would do it is based on the routes. When defining the routes, you can add extra data, for example a class name.
When the route changes (i.e. via navigation), the data from the active route can be used to set the class on the body tag.
This is how you can achieve this
Update the styles.css to add different classes for body:
body {
...
}
body.red {
background-color: #ff8181;
}
body.blue {
background-color: #a0c3ee;
}
Update the routes to add extra data, specifying the body class name. Add an extra data property, like bodyClass:
const routes: Routes = [
{ path: '', component: DefaultComponent },
{ path: 'red', component: RedComponent, data: { bodyClass: 'red' } },
{ path: 'blue', component: BlueComponent, data: { bodyClass: 'blue' } }
];
Write the code to read the bodyClass and set the class to the body element when navigation occurs. This can be done in the app.component.ts:
#Component({
selector: 'app-root',
template: `
<div>
<router-outlet></router-outlet>
<app-menu></app-menu>
</div>
`
})
export class AppComponent implements OnInit {
constructor(
#Inject(DOCUMENT) private document,
private renderer: Renderer2,
private router: Router,
private activatedRoute: ActivatedRoute) {
}
ngOnInit() {
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.pipe(map(() => this.activatedRoute))
.pipe(map((route) => {
while (route.firstChild) {
route = route.firstChild;
}
return route;
}))
.pipe(filter((route) => route.outlet === 'primary'))
.pipe(mergeMap((route) => route.data))
.subscribe((event) => this.updateBodyClass(event.bodyClass));
}
private updateBodyClass(customBodyClass?: string) {
this.renderer.setAttribute(this.document?.body, 'class', '');
if (customBodyClass) {
this.renderer.addClass(this.document?.body, customBodyClass);
}
}
}
Here is a demo on StackBlitz: https://stackblitz.com/edit/angular-ivy-rs1tai
Why not just define a separate class based on different background-color? For instance:
.blue {
background: blue
}
.green {
background: green
}
.grey {
background: grey
}
And then set these classes on the body using ng-class or ngClass whatever convention you use based on the page. This should be fairly easy to implement.
My favourite approach for doing stuff like this is to add a class to html tag depending on the route. For example, we have some code in our basic layout component (you could put it in your root component) that does this inside ngOnInit:
let wrapper = ''
const path = this.activatedRoute.snapshot.routeConfig.path
wrapper += this.tidyPath(path)
if (wrapper !== '') wrapper += '-'
const childPath = this.activatedRoute.snapshot.firstChild.routeConfig.path
wrapper += this.tidyPath(childPath)
this.routeWrapperCssClass = wrapper
$('html').addClass(this.routeWrapperCssClass)
This will add a class to your html tag to make it look like this (although you may have to tweak this code to suit your app):
<html class="registration">
....
</html>
The class will be instantly updated whenever you change route.
Now you can do this in your main stylesheet:
body {
background-color: pink;
}
html.registration body {
background-color: yellow;
}
You could also do things like hide elements based on the class added to the html tag like so:
.navbar {
display: block;
}
html.registration .navbar {
display: none;
}
Because you know what route you are on at all times you have total control via CSS.
PS you may want to use render2 instead of jQuery to do the DOM manipulation - see this article ... https://alligator.io/angular/using-renderer2 ... never used it myself before but almost identical to jQuery syntax - thanks to Pratap A.K answer for this
Personally i replace :
<body>
<app-root></app-root>
</body>
to
<app-root></app-root>
and then i add all the time the body on components or if i have multi
router-outlets i add it on app.component.css

Angular2 CSS style/class strings as inputs to component

Background
I have a component that wraps two span elements. I would like parent components to be able to style the inner two spans by specifying CSS classes and styles as inputs (preferably as simple strings). Here's a simplified example of what I would like this to look like:
Code
app component (parent)
#Component({
selector: 'my-app',
template: `
<div>
<app-two-spans [classArg1]="'class1'" [classArg2]="'class2'"
[styleArg1]="'width: 100px'" [styleArg2]="'width:200px'"></app-two-spans>
</div>
`, style: `
::ng-deep .class1 {
border: 1px solid black;
}
::ng-deep .class2 {
border: 1px solid blue;
}
`
})
export class App {
}
two spans component (child)
import {Component, Input, Output, EventEmitter} from '#angular/core';
#Component({
selector: 'app-two-spans',
template: `
<span *ngIf="flag" [ngClass]="classArg1" [ngStyle]="styleArg1"
(click)="flag = !flag">click me</span>
<span *ngIf="!flag" [ngClass]="classArg2" [ngStyle]="styleArg2"
(blur)="flag = !flag" contenteditable="true">
click me to change my value</span>
`
})
export class TwoSpansComponent {
flag: boolean = true;
#Input() styleArg1: string;
#Input() styleArg2: string;
#Input() classArg1: string;
#Input() classArg2: string;
constructor() {}
}
Problem
The class styling seems to work in my local environment (though it doesn't seem to work on Plunker for some reason). However, the styles are not showing up. I've seen other posts about styles as inputs, but from what I've seen this is usually done by passing style-value pairs (see accepted answer here). However, I would really like to pass these styles as a simple string to make working with the component easier.
I notice the following error in the console: ERROR Error: Cannot find a differ supporting object 'width: 100px'. I'm not sure what this means at all.
Plunker here
Is there a way to do this? How can I give parent components the ability to stylize children?
ngStyle accepts an Object instead of plain string. You can pass your styles as:
[styleArg1]="{ width: '100px' }"
[styleArg2]="{ width: '200px' }"

Angular 4 In Code override Bootstrap colors globally

I am looking to create a dynamic coloring system with Angular. I created a service that has 4 strings,success, info, warning, danger with defaults of their color codes. I want to at the root of my application which I assume is at app.component because I am using routing to inject this service and override globally
.btn-warning {
color: #fff; <-- Inject my own color like so (color: AppSettings.ColorSettings.Warning)
background-color: #f0ad4e; <-- Inject my own color
border-color: #f0ad4e; <-- Inject my own color
}
I want this to be dynamic so that if the user went into a settings panel could change these colors and in real time see the effect in every place that uses those classes.
I see [ngStyle] but that applies specific things like colors to just one element.
I also see [ngClass] but I don't know how to create class that is more like a way to apply a class to an element.
Could I do something like so?
<html>
<!-- Junk ^ with bootstrap up here -->
<style>{{GetColorSettings()}}</style>
<!-- More stuff -->
</html>
This may not be the most elegant solution however I really like it for how easy it will be to setup. Inside of my app.component I have this setup. Within the GenerateColorSettings method you will need to define all the different colors, which will require me expanding my classes inside of ColorSettings further. Some of the effects are default, :hover, :disabled
constructor(public infoService: InformationService, private configSettings: GlobalConfigSettings)
{
var style = document.createElement('style');
style.innerHTML = this.GenerateColorSettings();
style.id = "DynamicColors"
document.body.appendChild(style);
setTimeout(() =>{
configSettings.ColorSettings.warning = "#fff";
configSettings.ColorSettings.ColorsChanged.emit();
}, 25000);
configSettings.ColorSettings.ColorsChanged.subscribe(() =>
{
var style = document.getElementById('DynamicColors');
style.innerHTML = this.GenerateColorSettings();
});
}
private GenerateColorSettings(): string
{
console.log("Getting color");
return `.btn-warning{
color: #fff;
background-color: ${this.configSettings.ColorSettings.warning};
border-color: ${this.configSettings.ColorSettings.warning};
}
.btn-warning:hover{
color: #fff;
background-color: ${this.configSettings.ColorSettings.warning};
border-color: ${this.configSettings.ColorSettings.warning};
}
`;
}

CSS Modules, React and Overriding CSS Classes

I am using a React component library (React Toolbox), which is outputting this class in their Tab component using CSS Modules and Webpack: label___1nGy active___1Jur tab___Le7N The tab is a className prop I am passing down. The label and active classes are coming from the library. I want to apply a different set of styles on active, something like .tab.active where tab is referring to the styles I have created and active matches the generated selector the library has created but cannot figure out how to do this with css-modules. I need to override this dynamically selector: .label___1nGy.active___1Jur.
[]]2
[]3
Old post but still relevant, so adding an answer to help those with similar issue
While not inherently possible in CSS modules alone, the author of the react-toolbox library has actually solved this particular problem very nicely
Read their github docs which are more in depth on the subject at https://github.com/react-toolbox/react-toolbox#customizing-components
A list of the themeable classes for a particular component is given on the component demo page on their site too
In your case as well as passing a className for tab, you would also create a theme with classes that overrides that desired parts of the default theme and pass that as the theme prop. For example something alog the lines of...
MyComponentWithTabs.css
.tab {
color: white;
}
MyTabTheme.css
.active {
color: hotpink;
}
MyComponentWithTabs.js
import styles from './MyComponentWithTabs.css'
import tabTheme from './MyTabTheme.css'
// blah blah...
return <Tab key={index} className={styles.tab} theme={tabTheme} />
Under the surface this uses a decorator they have abstracted into separate library react-css-themr, I recommend giving that a read too as it explains how the defaults are composed with your overrides in much greater depth, including the different merging strategies they use
I had a similar case, and I solved it like so:
import classNames from 'classnames';
...
const activeClassName = {};
activeClassName[`${styles.active}`] = this.props.isActive;
const elementClassNames = classNames(styles.element, activeClassName);
return <div className={elementClassNames} />
I'm using the classnames package to dynamically add the active class based off of the isActive prop. Instead of an isActive prop you can provide any boolean value.
A more terse way of doing this may be:
const elementClassNames = classNames(styles.element, {[styles.active]: this.props.isActive});
CSS modules won't allow you to safely override the active className (largely by design). Really it should be exposed via an API, e.g. 'activeClassName'.
If the maintainers disagree or you need this quick then you can quite easily add your own active className because your implementing component is managing the index state:
import {Tab, Tabs} from 'react-toolbox';
import styles from './MyTabs.css';
class MyTabs extends React.Component {
state = {
index: 1
};
handleTabChange(index) {
this.setState({ index });
}
render() {
const { index } = this.state;
return (
<Tabs index={index} onChange={this.handleTabChange}>
<Tab label="Tab0" className={index === 0 ? styles.active : null}>
Tab 0 content
</Tab>
<Tab label="Tab1" className={index === 1 ? styles.active : null}>
Tab 1 content
</Tab>
</Tabs>
);
}
}
Disclaimer: Code is untested.
Group style loader
You can use the group-style-lader to override the style of the components. This loader gives you an easy and intuitive way of override the style of the components.
Configure the loader
Install the loader
npm install --save-dev group-style-loader
Configure the loader in your webpack settings
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
'group-style-loader',
'style-loader',
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
]
}
};
You only need to put it, before the style-loader or the mini-css-extract-plugin loader.
Override the style of the components
The next example show as you can to override the style of the Card component from the App component.
You can define the style of your component as you are used to.
card.css
.container {
width: 300px;
height: 400px;
border-radius: 8px;
}
.title {
font-weight: bold;
font-size: 24px;
}
.summary {
margin-top: 24px;
font-size: 20px;
}
The unique difference is that in the Card component, you going to use the mergeStyle function to merge the custom style with the default style of the component.
card.jsx
import React from 'react';
import { mergeStyle } from './card.css';
export function Card({ customStyle }) {
const style = mergeStyle(customStyle);
return (
<div className={style.container}>
<div className={style.title}>Title</div>
<div className={style.summary}>
Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor.
</div>
</div>
)
}
Then, in the App component, to override the style of the Card component you need to define the custom style of the Card component using the separator _. All the classes using this separator going to be grouped in the card property of the style object.
app.jsx
.title {
font-size: 32px;
font-weight: bold;
margin: 44px;
}
.list {
display: flex;
}
.card_title {
color: blue;
}
.card_summary {
color: green;
}
Finally, you only need to pass the custom style of the Card component through the customStyle property of it.
app.jsx
import React from 'react';
import { Card } from './card';
import { style } from './app.css';
export function App() {
return (
<div>
<h1 className={style.title}>Group style</h1>
<div className={style.list}>
<Card/>
<Card customStyle={style.card}/>
</div>
</div>
);
}
In the previous example, you have two Cards components, the first uses its default style, and the second uses the customs tyle that we have defined.
You can see a full explanation of how to use the group-style-loader in its repository.
Using :global(.classname) you can override the external classnames.
Even 3rd party library css can be override.
:global(.active) {
color: hotpink;
}
trick in fileName.css
add className-active after declaring className
.className{
background: white;
}
.className-active{
background: black;
}
<div className={'className className-active'} />
<div className={'className-active className'} />
divs always will be black

Resources