Load different style in a component based on routing - css

I am trying to load css styles in a component based on the URL parameters. Basically the user will load the page like SOME_URL/{screenSize}/{type}. This shall always load the same component, but with different CSS styling. I am already using a router and have the parameters set - how can I dynamically load the CSS files? Is that possible at all?
Here is some code - basically the goal is not to load the static CSS file defined in the example, but to load something like screen.component.21-5.a.css
#Component({
selector: 'app-screen',
templateUrl: './screen.component.html',
styleUrls: ['./screen.component.css']
})
export class ScreenComponent implements OnInit {
public screenType = 'a';
public screenSize = '21-5';
ngOnInit() {}
constructor(private mediaService: MediaService, private route: ActivatedRoute) {
this.parameterSubscription = route.params.subscribe((params) => {
if (params.size) {
this.screenSize = params.size;
}
if (params.type) {
this.screenType = params.type;
}
});
}
...
}

You cannot do that, as Angular requires the style declarations to be statically analyzable. This is done for the AOT compilation. All of your templates and styles are getting compiled to JavaScript when you run ng build --prod, and the styles are imported ahead of time. So, if you could reload styles using some conditionals, that code would no longer be statically analyzable (the screenSize variable can be known only during runtime, so which style should we import when building ahead of time?), thus resulting in the impossibility of AOT compilation.
(no, there is no way of compiling both styles and then importing them in runtime - that would mean we could understand what output will the function produce, which is virtually impossible - see halting problem)
But you can (and should) use ngClass to toggle between css classes depending on the screen size.

You can use ngClass
in your html
<div class="gradient " [ngClass]="{'class-21-5': screenSize ==='21-5', 'class-a': screenType==='a'}">
...
</div>
You can put the different classes in different files and import it on the component if you want.
#Component({
selector: 'app-screen',
templateUrl: './screen.component.html',
styleUrls: ['./screen.component.css', './my-other-style.css']
})

Related

Can I load into an array all the CSS classes into an array listed in the styleUrls property of an Angular Component

In my Angular component I wish to load one of the CSS files used in my app so I can collect the contained CSS classes, put them into an array and loop through them in my component's HTML template. I know from previous experience that in JavaScript/TypeScript I can load all of my CSS stylesheets into JavaScript/TypeScript like so:
Array.prototype.forEach.call(document.styleSheets, (styleSheetList: any) => {
console.log(styleSheetList.cssRules); // do something
});
This is okay but a little inefficient and I shouldn't really call "document" in my Angular component file as I have heard this is considered an anti-pattern. So could I add the specific CSS file to/through my component's styleUrls property then somehow expose it in my component and loop through the classes there?
Something like this:
#Component({
selector: 'css-list',
templateUrl: './css-list.component.html',
styleUrls: ['./css-listcomponent.scss', 'the-file-i-want-to-iterate-through.css']
})
export class CssListComponent implements OnInit {
// so let's load the style sheet
ngOnInit() {
Array.prototype.forEach.call(this.styleUrls[1].cssRules, (cssRules: any) => {
console.log(cssRules); // do something
});
}
}

How to compile component styles dynamically in Angular 6

I'm trying work out a way to use jss in an angular 6 project to allow dynamic styling of components.
The issue I'm running into is that the dynamic styles are always less specific than the predefined styles, because the dynamic styles are missing the attribute selector from the view encapsulation system.
I can easily get the raw CSS output from jss, but I haven't been able to find a way to run this through the angular compiler to have the selectors modified to include the attribute selector.
Ideally I'd like to be able to bind a <style> tag in the template to a cssText property of the component, but this doesn't seem possible.
import {Component, OnInit} from '#angular/core';
import * as color from 'color';
import jss from 'jss';
#Component({
selector: 'app-example',
template: `
<p [ngClass]="cssClasses">TEST TEST</p>
`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent implements OnInit {
cssClasses: { [name: string]: boolean } = {};
constructor() {
}
ngOnInit() {
const {classes} = jss.createStyleSheet({
dynamicClass: {
color: color('blue').hex(),
}
}).attach();
this.cssClasses[classes.dynamicClass] = true;
}
}
example.component.scss
p {
color: 'red'
}
If there a way of invoking the angular CSS compiler on an arbitrary piece of CSS, with the context of a particular component?
Or another way to achieve what I'm describing above?
Note: I'm aware that I can bind and apply inline styles to elements, but this doesn't meet my requirements - in particular you cannot target pseudo selectors, or do media queries etc using this mechanism.
I could probably work around this by not using the scss file at all and defining all default styles through the jss mechanism however I would prefer to retain the ability to use the normal style system so that the jss is only used where needed. Also I think I would still run into selectivity issues when styling 3rd party components using jss.

Angular 6 not applying scss styles

I have a component page and corresponding style sheet, however the classes in the component.scss dosen't apply to the page. There are no errors, I am still wondering why?
This is my product-detailpage.component.html
<div>
<h1>Product Detail Page</h1>
</div>
This is the .ts file
import { Component, OnInit } from '#angular/core';
import {ActivatedRoute} from '#angular/router';
import {ProductdetailService} from '../productdetail.service';
#Component({
selector: 'app-product-detailpage',
templateUrl: './product-detailpage.component.html',
styleUrls: ['./product-detailpage.component.scss']
})
export class ProductDetailpageComponent implements OnInit {
constructor(private route: ActivatedRoute, private productData: ProductdetailService) {
this.route.params.subscribe(params => console.log(params));
}
ngOnInit() {
}
}
This is the .scss file
body{color:Red !important}
app-product-detailpage{
h1{color:red !important}
}
However one thing I noticed was if I make changes to the global styles.css it works fine. just to check I changed the body color to green and it works.
My angular app is configured to work with scss. what could be the reason? can some one suggest?
Your SCSS won't work for your HTML file product-detailpage.component.html.
The reason is Angular uses shadow DOM for components. That means the tags <body> and <app-product-detailpage> are nowhere to be found in your component.
As per the documentation, The style specified in the component can only be applied to its template, which excludes the component.
This is the reason why your styles are not working on the component from component's style.scss but are working fine from global style sheet.
One way of doing it is to use :host pseudo selector as per this documentation which allows to add styles on the container in which component is placed.
The documentation says -
The :host selector is the only way to target the host element. You can't reach the host element from inside the component with other selectors because it's not part of the component's own template. The host element is in a parent component's template.
Because default css encapsulation in Angular is Emulated(ViewEncapsulation.Emulated) so Angular will render out like below:
input[_ngcontent-c0] {
border-radius: 5px;
}
So if you want set style to the currently component, you can use Native option.
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
encapsulation : ViewEncapsulation.Native
})
It will render like as:
input {
border-radius: 5px;
}
But finally I suggest you use global scss file to define style of <web component>.

Angular2 dynamic style

What about dynamic styling.
Imagine you're deploying a unique plaftorm for multiple clients. (SAAS platform)
You deploy ONE webapp folder but these webapp must apply a different theme (color) for each client (exemple configuration : loading colors by domain name).
In angular 2, there is scss but the scss is a compiled language -> so you need to compile each time for each client to compile N webapps. It takes time and it's difficult to maintain.
So the only solution i see:
- compile scss at runtime (via remote server call, fe:jsass, or js) when loading app and inject the generated css file in head section.
but i think it's an anti-pattern with the (s)css file for angular2 components.
Moreover the generated file will contains components style that will apply on entire page instead even if component is not initialized.
Have you got any framework or other solution ?
You can achieve this creating a Service that return the colors of some structures that you want to change and inject it into the Components
Example inside the component html:
<div [style.background-color]="themeService.getNavbarColor()"></div>
And do some logic to get the pattern for each user when App starts and insert into the ThemeService.
Full Example:
import { Component } from '#angular/core'
#Component ({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
constructor( private themeService: ThemeService){}
}
#Injectable()
export class ThemeService {
backgroundColor: string
getNavbarBackgroundColor: string() {
return this.backgroundColor;
}
someLogicToGetTheme() {
//do stuff
}
ngOnInit() {
this.someLogicToGetTheme()
}
}

Refactoring <link> tag in template html to styleUrls

I'm trying to refactor a Component, which links stylesheets in it's html template into using the styleUrls instead.
The linked template has an include alike;
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
I've refactored this into;
styleUrls: [
'https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css'
],
encapsulation: ViewEncapsulation.None
I can confirm the stylesheet is loaded, it is however loaded as an inline style instead of via the link tag, as such references as;
src:url('../fonts/fontawesome-webfont.eot?v=4.4.0');
Do not point to;
https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/fonts/fontawesome-webfont.eot?v=4.4.0
But rather to;
http://localhost:9000/fonts/fontawesome-webfont.eot?v=4.4.0
How would I go about refactoring the html template to using styleUrls, while keeping the relative references?
The only way I was able to find around this was mentioned here in subsequent comments.
You have to create a custom UrlResolver. Here is a test on the Angular2 Material repo where they implement a custom url resolver. Relevant sections below.
import {TestUrlResolver} from './test_url_resolver';
import {..., bind} from 'angular2/core';
bind(UrlResolver)
.toValue(new TestUrlResolver());
Here is the TypeScript version of the TestUrlResolver (the other version is in Dart):
import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
export class TestUrlResolver extends UrlResolver {
constructor() {
super();
}
resolve(baseUrl: string, url: string): string {
// The standard UrlResolver looks for "package:" templateUrls in
// node_modules, however in our repo we host material widgets at the root.
if (url.startsWith('package:angular2_material/')) {
return '/base/dist/js/dev/es5/' + url.substring(8);
}
return super.resolve(baseUrl, url);
}
}

Resources