I have been working with angular for the last few weeks, and now I have a requirement to dynamically style a public site. The site admin set various color codes as well as a log
First of all, in ASP .NET, it might be not bad to have a db hold your styles and other front end related assets. This is because it is a Server Side Rendering framework. On the other hand, in Angular, it is client side (with the exception of Angular Universal, but you'll still have to expect working with things the same way on it). Even with translations (i18n or custom), in Angular world, it is most likely stored on the front end and not from the back (db or so). So you'll have to go and have your "theme"'s stored in a certain manner you prefer and make your way to switching between them dynamically with Angular. You can of course store the keys/variables for the styles/themes but their actual CSS values (classes and such) should be stored on the front end.
Try to see this simple example from CSS vars in use while dynamically setting app theme (Angular). This is only just one way and there are lots of ways to do this and you might have to look for your personal preference.
Since sass is a pre compiled css. we cannot dynamically change the theme without generating a seperate theme.css. This is where JSS comes to play. JSS is a javascript based style inject mechanism, where css are directly injected into the files you are using it.
react-angular-material uses it extensively, where we can pass color variables dynamically to change theme of the application.
for instance this guy has made it with angular.
Docs: jss-angular, jss
links: jss-with-angular
As other answers explained, it is not possible to set SASS variables and process that on the client, as SASS is converted to plain CSS at build time and when app is running or in APP_INITIALIZER browser can process only CSS.
I see two options to achieve what you want.
Generally, you would have some base css for the app, and then you need to load the additional css based on admin settings. What needs to be considered from css point of view is that all css specificity in additional css should be greater than base css, because otherwise it won't override the base. That requires basic css knowledge so I won't go into details.
Generate your additional css on server request. Load it when app is started from server URL. Reload it by js when admin change any settings.
/additional.css
(or it could be similar to /api/theme/custom-css
) which will generate css out of database. For example you have background=red
in db, then the endpoint should returnbody {background-color: red;}
<link id="additionalCss" rel="stylesheet" type="text/css" href="additional.css" />
in <head>
of index.html
. And that will be enough to make it work.document.getElementById('additionalCss').href = document.getElementById('additionalCss').href;
This will make new request to the server, server will execute DB -> css and return the updated css, which will be applied to the browser.
And if you want to be cool (or need to support big and complex themes) scss can be used. Backend should generate scss variable definitions out of database, then should use some server-side app to compile scss -> css, and then serve compiled css back to the client. But this will be overkill if additional css is simple enough.
One important consideration of this method is browser caching, because content behind additional.css is dynamic, but browser may cache it, not call the backend and serve outdated version.
If you don't want or can't mess with the backend. Load settings from DB by some API endpoint in json, then generate css code on the client and apply it.
{
"background": "red"
}
then you convert this to string as
cssCode = 'body {background-color: red}';
let additionalCssStyle = document.getElementById('additionalCss');
if (! additionalCssStyle) {
additionalCssStyle = document.createElement("style");
additionalCssStyle.id = 'additionalCss';
document.head.appendChild(additionalCssStyle);
}
additionalCssStyle.innerText = cssCode;
It is not possible in that way but rather than using the sass variable, you use the value of the sass variable. It may be any value.
Why? because sass is compiled during packaging and in the end, it would still generate plane CSS.
An example of a framework making use of this optional style processor is angular.
In your case I would recommend looking into dynamic themeing within angular as what you require definitely needs JavaScript. Look into the guide on medium given by one of the contributors.
https://stackoverflow.com/a/54559350/3070499
While @Cold Cerberus has suggested a good approach and is right about maintaining style related things at front-end, i am suggesting some ways for this.
As you said you want various colour combination,you can use Conditional CSS of SASS.
body[theme="theme1"] {
// theme 1 css
}
body[them="theme2"] {
// theme 2 css
}
You can use sass theme map along with conditional css.
Just update your attribute and theme will be applied automatically.
themeChange() {
const dom = document.querySelector('body');
dom.theme = theme1; // change theme here
}
If you are very particular about some element style which should be updated from back-end (like colour code) you can use ng-style along with theme approach.
<some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
You have to use smart combination of above in order to fulfill your requirement.
I don't think that what you want will be possible to do... Angular processes the SASS files during application build and writes all the common results into a plain old css
file. The component-specific stuff will get generated as javascript that, in turn, will apply your styling at run time.
Hence all the SASS variables you need to set up have to be present at compile time.
What you can do, though, is to pre-define your setup in Angular components and then toggle it based on an input (from your DB or wherever else), like so:
// your.component.ts
@Component({
// ... component stuff
styles: ['h1.option1 {color: red;}', 'h1.option2 {color: blue;}'],
template: `
<h1 *ngIf="optionSelection$ | async as option; else noOption"
[class.option1]="option == 1"
[class.option2]="option == 2">
Hey there, I'm styled!
</h1>
<ng-template #noOption>
<h1>No option received</h1>
</ng-template>
`
})
export class YourComponent {
optionSelection$: Observable<number>;
constructor(yourService: YourService){
this.optionSelection$ = yourService.getYourOption().pipe(startWith(null));
}
}
Hope this helps a little :-)