Angular/CSS Style parts of a component dynamically - css

I have a component, that has different parts. However, I want to be able to style the individual components with different colors.
For instance:
<div class="OuterBox">
<div class="InnerBox1"></div>
<div class="Seperator"></div>
<div class="SecondBox">
<div class="TextInfo"></div>
</div>
</div>
I add this to a page, via a standard Angular component:
<app-my-component></app-my-component>
I have seen the ngStyle option for Angular which I could use to specify , but my problem is I cannot simply do a <app-my-component [styles]="{backgroundColor: 'blue', 'font-size': '16px'}">. I need to color the different div sections differently, for instance the InnerBox1 has a background of green, and the SecondBox background should be red.
I can do these styles on the individual CSS, but when I want to make this a common component, I want to be able to change the colors on each instance, so they can be different from green and red, and could be blue and orange or something else.

You can simply declare a variable for each color in your component and bind them from outside
In your component :
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-my-component',
template: `<div class="OuterBox" [ngStyle]="{backgroundColor: outerBoxColor}">
<div class="InnerBox1"></div>
<div class="Seperator"></div>
<div class="SecondBox">
<div class="TextInfo"></div>
</div>
</div>`
})
export class MyComponent {
#Input() outerBoxColor;
}
and then pass the color from outside like this:
<app-my-component [outerBoxColor]="'blue'"></app-my-component>
<app-my-component [outerBoxColor]="'red'"></app-my-component>

Or if you want style more than just one css selector you can use DomSantizer and pass all css style to your Child component
In parent:
<child-component
div1Style='width: 400px;background-color:red;'
div2Style='width: 400px;background-color:red;'>
</child-component>
child component input variable:
#Input() div1Style: string;
#Input() div2Style: string;
in child in html div
<div [style]='GetStyle(div1Style)' >
<div [style]='GetStyle(div2Style)' >
and in code of child component
constructor(private sanitizer: DomSanitizer) { } //to inject instance of this DomSantizer
GetStyle(c) {
if (isNullOrUndefined(c)) { return c; }
return this.sanitizer.bypassSecurityTrustStyle(c);
}
and you can declare as many these variables as you need - one per each div for example

Related

Is there some way with which i can apply css dynamically to my child component?

I have a component which is reusable. This component is called from parent component multiply times and sometimes the background page of the parent component is white and sometimes is black.
My child component generates form tags dynamycally - inputs,selects, textarea.
That means i can't have fixed styles in my css in my component for my content.
So when when the background page is white - i have one style for my inputs - for example black background. When the background page is black i have another style for my inputs - for example white bacgrkound.
To solve this is issue:
i tried
Adding input property in my child component ts file
#Input()
public cssTemplate;
in html
<div [ngClass]="{'form-group-white-bg': cssTemplate == 'white', 'form-group-dark-bg': cssTemplate == 'black'}">
<label for=""></label>
....
In the CHILD component i am sending value for input property depending on where the child component is called
if it is called on page with white background
<app-form-group cssTemplate="black" formControlName="address">
</app-form-group>
if it is called on black bacgrkound
<app-form-group cssTemplate="white" formControlName="address" [data]="{ field: 'address', label: 'Address' }">
</app-form-group>
but the problem here is that sometimes on my parent component this component is called multiply times
on one page can be called 12 times where i need 10 inputs and 2 selects
on other page can be called 15 times etc.
That means that i need to repat my self 15 times
<app-form-group cssTemplate="white" formControlName="address">
</app-form-group>
<app-form-group cssTemplate="white" formControlName="someItherControlName">
</app-form-group>
and everywhere to put cssTemplate="white".
ngFor is not an optin because this child component is called multiply times but not on same place in the HTML structure in the parent.
How can i solve this DRY?
you can add styles in your styles.css (the styles general for all the application). If e.g. you has
.white h1{
color:red;
}
.black h1{
color:green;
}
You can use [ngClass] in the "parent"
<div [ngClass]="toogle?'white':'black'">
<hello name="{{ name }}"></hello>
</div>
<button (click)="toogle=!toogle">toogle</button>
See [stackblitz][1]
NOTE: I used the way [ngClass]="expresion" (where expresion use conditional operator) better that [ngClass]="{'classname1':condition;'classname2':!condition}"
Update about your comment "how can i prevent repeating my self on child call", really I don't understand so much. I don't know if you want to make a directive like, e.g.
#Directive({
selector: 'hello', //<--the selector is the selector of the component
exportAs: 'helloDiv'
})
export class HelloDirective implements OnInit{
constructor(#Self() private component:HelloComponent,private dataService:DataService){
}
ngOnInit(){
console.log(this.component.name)
this.dataService.theme.subscribe(res=>{
this.component.theme=res;
})
}
}
This allow to "extends" the component -in the stackblitz the variable "theme" change-
[1]: https://stackblitz.com/edit/angular-ivy-sjwxyq?file=src%2Fapp%2Fapp.component.html
You can use an input property to create a css class map to pass on to ngClass. This object should be an object of string arrays.
It can be pretty much as complex and contain as many classes and rules as you need it too
#Input() color: 'white' | 'red' | 'hotpink' = 'white';
classMap: any;
ngOnInit() {
this.updateClassMap();
}
updateClassMap() {
this.classMap = {
[this.color]: !!this.color, // Add this class if not null
};
}
Then in the Html simply pass it to ngClass
<div [ngClass]="classMap">
Styling Child Components depending on Parent Component
There are two approaches I commonly take in this scenario
:ng-deep - create a style rule based on a class which is set in your parent component
utilize #ContentChildren() to set a property directly on your child components and call detectChanges() manually after the change.
To adopt the first solution you need to exercise greater care in your css naming rules, as using ng-deep obviously breaks the isolation of those style rules.
To adopt the second approach needs some considering due to it technically circumventing the standard input/output flow in Angular and thus can be a bit of a surprise "undocumented behavior" for any other maintainers of the application.
I'm a bit on the fence whether I prefer one approach over the other. The first approach seems more trivial to me, but it can also cause unintended style rule overwrites, while the second approach involves a lot more scripting and seems a bit of a hack.
Approach 1: ng-deep
Give your parent component an input and update a class on a block-element wrapping your <ng-content>.
create your desired style rules in your child component.
// parent component
#Component(...)
export class FooParent {
#Input() bgStyle: 'light' | 'dark' = 'light';
}
<!-- parent component template -->
<div class="parent" [ngClass]="{light: bgStyle == 'light', dark: bgStyle == 'dark'}">
<ng-content></ng-content>
</div>
// child.css
::ng-deep .light .child-container {
background-color: lightblue;
}
::ng-deep .dark .child-container {
background-color: royalblue;
}
My targeted element in the example is .child-container, you would write a similar style rule for each element you want to affect.
Approach 2: Using ContentChildren to pass along a value
Add a #ContentChildren() decorator to your parent component which selects for your child components.
inject a ChangeDetectorRef
implement ngAfterViewInit to loop through each child and set the value
call detectChanges() once done.
add the ngClass directive as normally in your child component.
Parent
#Component({
selector: 'parent',
templateUrl: 'parent.component.html',
styleUrls: ['parent.component.scss']
})
export class ParentComponent implements AfterViewInit, OnChanges {
#Input() bgStyle: 'light' | 'dark' = 'light';
#ContentChildren(ChildComponent) childComponents!: QueryList<ChildComponent>;
constructor(private change: ChangeDetectorRef) {
}
ngOnChanges(changes: SimpleChanges) {
if ('bgStyle' in changes) {
this.updateChildComponents();
}
}
updateChildComponents() {
this.childComponents.forEach(child => {
child.bgStyle = this.bgStyle;
});
this.change.detectChanges();
}
ngAfterViewInit() {
this.updateChildComponents();
}
}
<!-- parent.component.html -->
<ng-content></ng-content>
Child
#Component({
selector: 'child',
templateUrl: 'child.component.html',
styleUrls: ['child.component.scss']
})
export class ChildComponent implements OnInit {
bgStyle: 'light' | 'dark' = 'light';
constructor() {
}
ngOnInit(): void {
}
}
<!-- child.component.html -->
<div [ngClass]="{light: bgStyle == 'light', dark: bgStyle == 'dark'}" class="child-container"></div>
// child.component.css - you would apply styles as you needed obviously.
.child-container {
width: 40px;
height: 40px;
margin: .5rem;
}
.light.child-container {
background-color: lightblue;
}
.dark.child-container {
background-color: royalblue;
}
Usage
<!-- any other template -->
<parent>
<child></child>
<child></child>
<child></child>
</parent>
Note: If you are creating the ChildComponent directly in the ParentComponent's own template you need to use #ViewChildren instead of #ContentChildren

Passing CSS Styles to child HTML elements through Attribute Directives

I am trying to send a list of styles to the child components be passed through the Attribute Directives.
For Example:
<div [mystyles]>
<p>.....</p>
<div>...</div>
</div>
Through attribute directives I am able to modify the parent div 's css I am using.But want the [mystyles] to modify its child elements p and div .
You can use ngStyle directive in child component.
In your parent component you cam pass (via #input directive) the style you want to your child component, then the child component can use it inside your html.
Here a sample implementation.
Parent.ts
myStyles = {
'background-color': 'blue',
}
Parent.html
<child-component-selector [parentStyle] = myStyles>
Child.ts
#Input() parentStyle: any;
Child.html
<p [ngStyle]="parentStyle">
...
</p>
Here a little guide on how to use ngStyle
Edit:
You can compose in parent the myClasses variable that replace myStyles in this way:
myClasses = {
"class-name-1": {
"background-color": "blue"
},
"class-name-2": {
"background-color": "yellow"
},
"class-name-3": {
"background-color": "lime"
}
}
And then use the classes in child element in this way:
<p [ngStyle]="parentStyle.class-name-1">
...
</p>
<div [ngStyle]="parentStyle.class-name-2">
...
</div>
(parentStyle var have the name you specify after #Input() directive, as in the previous example)
As you can see only one input is needed for pass several classes, it only depends to the input variable you pass to the child.

Set the ClassName with state value in modular css in react

I am using modular css in react. I have to addClass to a <h2> how do I do that the problem is I am using modular css I know how to do it in normal css.Please Help.
Here is my component
import React, { Component } from 'react';
import style from '../css/MessageHeader.css';
class MessageHeader extends Component {
constructor(props) {
super(props);
this.state = {
name : "name"
}
}
render(){
return(
<div className={style.container}>
<div className={style.iconWrapper}>
<i className ="fas fa-angle-left"></i>
</div>
<div className={style.profileWrapper}>
<h2 className={this.state.name}>john appleseed </h2> //this is how I would in normal css
</div>
</div>
);
}
}
export default MessageHeader;
This would help you add the className this way. Variable is any variable in state with classname
<h2 className={`${this.state.variable}`}></h2>
While you can use a global class, I'm assuming you also want the h2 classes to be scoped to that component. Just like your other classes, you need to reference the style import - but use a dynamic key based on the state, like so:
<h2 className={style[this.state.name]}>john appleseed </h2>
Then your css module MessageHeader.css should contain a class for all names that you want to custom styling for.
Example:
.container {
// some styles here
}
.johnAppleseed {
color: yellow;
}
// You can also specify a name as a child of the container just like normal css,
// although it's not really necessary
.container .bobBobbert {
color: red;
}
Keep in mind that if your name in the state has spaces, that isn't going to work for a css class name, so you'll have to do some transformation (e.g. this.state.name.replace(...) to remove spaces and/or punctuation.

Animating content children in Angular (2/4)

I have a component that uses the <ng-content> selector to pull in some content children.
<!-- label-expand component-->
<div>
<ng-content select="[appLabel]"></ng-content>
</div>
So when a component creates an instance of this label-expand component like this:
<label-expand>
<span appLabel>Some label</span>
</label-expand>
I would like to set up the label-expand component, when hovered on to play an animation of the content child with the appLabel directive where the text gets bigger.
#Component({
selector: 'label-expand',
//...,
animations: [trigger('expandLabelState', [
//This is the animation I would like to pass to the content child
])]
})
export class LabelExpandComponent {
#ContentChild(AppLabelDirective) appLabel: AppLabelDirective
}
How do I pass an animation defined with the angular animation metadata in the label-expand component to it's ng-content content child? Or is this a problem that should instead be solved with CSS?

Angular 2 #Component Styles

How can I use Angular 2 styles in #Component with multiple classes on the same tag?
#Component({
styles: `.class1 .class2{background-color:red;}`
})
This generates the following css code:
.class1[<RANDOM_ANGULAR_ATTR>] .class2[<RANDOM_ANGULAR_ATTR>]{
background-color: red;
}
This will not select a tag defined like this:
<div class=".class1 .class2" RANDOM_ANGULAR_ATTR></div>
Is there any way to make this approach work?
Your styles should have the classes without a space between them. Multiple classes in the same selector must be written directly after each another – with no white space. For example:
.class1.class2 { background-color:red; }
And in order to select your object you should have the classes added like this:
<div class="class1 class2">Test css</div>
See the code below:
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
styles: [
`
.class1.class2{background-color:red};
` ],
template: `
<h1>My First Angular 2 App</h1>
<div class="class1 class2">Test css</div>
`
})
export class AppComponent { }
For more options see also the following blog posts (about Shadow DOM, Encapsulation Types .. any other cool things):
https://scotch.io/tutorials/all-the-ways-to-add-css-to-angular-2-components
http://blog.thoughtram.io/angular/2015/06/29/shadow-dom-strategies-in-angular2.html
Have you looked at Justin Schwartzenberger's NG-Conf talk on CSS styles and the randomly generated tag? https://www.youtube.com/watch?list=PLOETEcp3DkCq788xapkP_OU-78jhTf68j&v=J5Bvy4KhIs0
potential options are to:
Use a styleUrls template
change encapsulation
use a different class (since its only relevant to this component its ok to name something simple vs using 2)

Resources