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 logo image from admin in a database. These will be reflected when the Public Site opens.
As I am from an asp.net background, previously what I would do is on master page load, take values from the DB and write them into a .less file, and let java-script library take care of it. It's simple there.
But for my current situation, I am using sass, and I am not able find a way to write variables into a .scss file.
I just learn a new thing APP_INITIALIZER from here ,but ultimately this post not showing how to write in the .scss file.
I am actually thinking this with my asp.net knowledge,but may be I am wrong ,or there are another way of implementation.
I want a simple solution ,what we do in asp.net I want to achieve this in same way.
Take variable value from DB via api,when application loading for first time.
Write values in SASS variable file .
After that SASS will take care of this and we get result as expected .
Please give some suggestion or example ,to start with .
Thanks.
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.
Method 1
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.
Define backend endpoint at address /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 return
body {background-color: red;}
Add <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.
To reload you can use different methods, but I believe this should 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.
Method 2
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.
Use HttpClient to get settings JSON and generate css as string out of it. For example server returns
{
"background": "red"
}
then you convert this to string as
cssCode = 'body {background-color: red}';
Use
let additionalCssStyle = document.getElementById('additionalCss');
if (! additionalCssStyle) {
additionalCssStyle = document.createElement("style");
additionalCssStyle.id = 'additionalCss';
document.head.appendChild(additionalCssStyle);
}
additionalCssStyle.innerText = cssCode;
To reload - save changed to backend, then repeat 1. and 2.
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.
First of all, in ASP .NET, it might be not bad to have a db hold CSS rules and other static assets. This is because it is a Server Side Rendering framework, so it kinda makes sense.
On the other hand, in Angular, it is client side (with the exception of Angular Universal, but you'll still have to expect working in similar approaches). Even with translations (i18n or custom), in Angular world, it is most likely stored on the front end (i18n .json files) 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 your actual CSS code is still stored on .css files.
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.
UPDATE:
There might be erroneous implications of my answer above, but I'll leave it as is and just share one experience I had that is related to this topic.
I have worked on a webapp where the user can customize his theme via settings, likewise, The CSS rules aren't stored on DB, but the color values to be set on sass variables are. There was a special script where CSS scripts will be compiled (was returned on demand which made it a bit slow but a splash screen just saves your day, not AOT compiled) along with the custom values, which I don't have any idea how it was done. The same with translations, I also recently worked on a project where translations are from db, but there's a script to run for every release/deployment that generates and updates the .json files in the assets/i18n folder.
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 :-)
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
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
We're building an AngularJS application. I'm now building a public form that is customized depending on the URL it's loaded with.
For example
http://url.com.au/Registration.cshtml#?org=org1
will look slightly different than
http://url.com.au/Registration.cshtml#?org=org2
When the page loads I would like to load some info with GET http://url.com.au/api/Org/org1
and use the response to drive some UI elements.
At first the differences will be small (URL for logo, values from some checkboxes) but over time the form may be more and more dynamic.
What is the pattern I should follow to support:
simple differences,
further along the journey a more dynamic ui?
For a simple solution, I would hold the url change in a route parameter for that route. Then access it using $routeParams. That way you can change your base GET url easily.
For both simple and dynamic UIs I would try to keep use of ngInclude to a minimum (less files to load = better load times, also it creates a new scope which isn't always ideal).
For simple differences I would structure your html to be as accommodating to both layouts as possible. Think about using ng-show/hide to trigger DOM changes based of variables retrieved from the GET request. For assets like images the GET request could return the path to the image and you can then use ngSrc to include it.
Further along the journey, you may want to consider having nested routing. UI-Router is a very popular solution.
I'm using firebase as a backend and have several posts in the database. My files look like:
index.html
add-post.html
all-posts.html
How can I create routes for post-details such as /posts/id.
Do I need to use a framework such as Angular for this kind of dynamic routing?
That's correct if you want to use Angular JS UI router, I leave you the link here, the info is clear, hope it helps
https://github.com/angular-ui/ui-router
If you go to the bottom of the page you'll see that it's explained step by step.
I'm building a single page app similar to pininterest where I fetch JSON data in increments of 20. My project does not support Angularjs, backbone mustache or any such framework or libraries.
I need help in identifying a solution for my problems listed below:
I want to decouple my html for the tiles (with images and without) as templates.
On receiving the JSON data first 20 items, I want to build the tiles and inject it inside my main index.html
How do I make calls to get JSON data starting from 21-40 and 40-60. The JSON data has numbering property called rank:1, rank:2 etc..
I'm looking for a front-end solution with Javascript or jQuery.
You are describing what I do with my SPA applications. I implement a very similar feature on my blog's home view, http://love2dev.com. I don't do paging for new tiles though.
You are correct, you do not need any bulky framework to execute what you need. As for templating I use either MUSTACHE or something based on this article, http://tech.pro/tutorial/1743/javascript-template-engine-in-just-20-lines. I actually use the 20 line function in my Blog. In my new SPA book I use MUSTACHE, it is up to you.
To call the next 20 records you would need to configure your API to allow you to retrieve a paged dataset. This can be done with a querystring parameter, or a route variable. It is up to you. Normally you have a parameter for page # and total records. If you need help with the actual AJAX layer I would look at rolling your own or using reqwest, https://github.com/ded/reqwest?source=cc.
In the code we use something like this:
$('#wrapper').html('//app/views/content.ejs', {foo:"bar"});
And when we build the app, this still stays the same, although the content.ejs file is built into production.js.
So my question is, what should we do so that when we build the app, these references point to ejs files inside of production.js?
We are using JMVC 3.2.2
We've also tried using this way:
$('#wrapper').html( $.View('//app/views/content.ejs', {foo:"bar"}) );
Your views are not getting added to production.js; you need to steal each one of them:
steal('//app/views/content.ejs');
JMVC 3.1:
steal.views('//app/views/content.ejs');
Got the answer in JMVC forum: https://forum.javascriptmvc.com/topic/#Topic/32525000000958049
Credit to: Curtis Cummings
Answer:
The paths to the views do not need to change.
When the production.js file is created, your views are included and
get preloaded when the script runs. When you reference:
'//app/views/content.ejs', view first checks if the view file you are
requesting has been preloaded and if it has, will use that instead of
making a request for the .ejs file.