CSS Styling Angular 2+ (pain in the butt) - css

Ok, i am new to Angular 4. I am having issues with styling the application properly. I went ahead and added a background image to the body, this worked as expected, then added a component that would display content and it looked as expected.
I added a second component different route but for this one I do not want the body to have the background image at this point i am not sure what is the best practice.
I read a few articles and some say that you can have a different style for the body at the component level by adding a body style override in the styleUrls. I did this but everytime i went from say /myroute/page2 to myroute/page1 the background sticked to what i set for myroute/page2 while it should show the body image for /myroute/page1. I am also using ViewEncapsulation set to None (maybe this is the issue)?.
Also this is my setup and may be wrong too
index.html has something like this:
</head>
<body>
<app-root></app-root>
</body>
</html>
then my app.component.html has this:
<app-navigation></app-navigation>
<router-outlet></router-outlet>
<app-footer></app-footer>
and then i have my welcome.component.html that has a section
<section id="hero">
<div>
</div>
</section>
Now the component above shows the background set correctly, but if I navigate using something like
this.router.navigate(['/page2']);
the background stays the same as in my welcome component. If i refresh on /page2 the right background now shows up.
UPDATE:
Ok i gave up on showing the background on the second component, i wanted without background image, but want to keep the one in the first component. So i removed the ViewEncapsulation from all of them but now if I move the body {} to the first component it does not show it for some reason (I made sure it has the right path). Is there a better fix than removing it by using the DOM?. What is the best practice?.

You need to set style class on ngOnInit and remove it on ngOnDestroy.
constructor(private renderer: Renderer2) {
}
ngOnInit() {
this.renderer.addClass(document.body, 'your_class');
}
ngOnDestroy() {
this.renderer.removeClass(document.body, 'your_class');
}

This worked for me, pls try. In your component1,
ngOnInit() {
document.body.style.background="url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSu5rIAFQkkswZLuwAUCXZqc_8bBROGkGgmZP5bmGk57sRKXWJMEg)";
}
ngOnDestroy() {
document.body.style.background="";
}
In your component2,
ngOnInit() {
document.body.style.background="url(http://gkreading.com/wp-content/uploads/2017/03/awesome-kid-in-the-grass.jpg)";
}
ngOnDestroy() {
document.body.style.background="";
}
Hope this helps.

Related

Angular: How to add global CSS (e.g. to the body), but only for one specific page?

How can I add separate CSS for one page in Angular?
This is the CSS I need, as per How to remove the URL from the printing page?:
#media print{
#page{
margin:0;
}
body{
margin:30px;
}
}
But putting CSS into the component with ::ng-deep or ViewEncapsulation.None won't help here, because when navigating away from a page, the CSS of the page isn't deleted.
I've added a Stackblitz, which explains the problem clearly.
I've come up with a potential solution, but it doesn't work:
encapsulation: ViewEncapsulation.None
...
constructor(private renderer: Renderer2) {
this.renderer.addClass(document.body, 'special-print');
}
ngOnDestroy() {
this.renderer.removeClass(document.body, 'special-print');
}
....
....
....
#media print{
#page{
margin:0;
}
body.special-print{
margin:30px;
}
}
Why it doesn't work:
While it would help with <body> CSS, it won't help with #page CSS. Perhaps the question would be better summarized as "How to add global CSS, but remove it when we leave the page?".
Solved!
We print the <style> block directly into the component's HTML, and therefore when the component gets removed, our <style> block gets removed too. (Normally this wouldn't work, but thanks to DomSanitizer.bypassSecurityTrustHtml, Angular won't break our code when running optimizations.)
Here's a StackBlitz.
First, create a new component to handle the work:
component.ts: (This is all we need. We don't need an HTML or style.css file.)
//Inside your local component, place this HTML
//<app-local-css [style]="'body{background:green !important;}'"></app-local-css>
// OR
//<app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>
#Component({
selector: "app-local-css",
template: '<span style="display:none" [innerHTML]="this.safeString"></span>'
})
export class LocalCSSComponent implements OnInit {
constructor(protected sanitizer: DomSanitizer) {}
#Input() scriptURL?: string;
#Input() style?: string;
safeString: SafeHtml;
ngOnInit() {
if (this.scriptURL) {
let string = '<link rel="stylesheet" type="text/css" href="' + this.scriptURL + '">';
this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
} else if (this.style) {
let string = '<style type="text/css">' + this.style + "</style>";
this.safeString = this.sanitizer.bypassSecurityTrustHtml(string);
}
}
}
And then use it like this:
mySample.component.html:
<app-local-css [style]="'body{background:green !important;}'"></app-local-css>
// OR
<app-local-css [scriptURL]="'/path/to/file.css'"></app-local-css>
Angular is doing client-side rendering, which is bad news, because you do not have separate pages. You have several possible solutions though:
1. Separate page
You can create another page with or without Angular, which includes the CSS you need and load that page. In the most simplistic approach to achieve this, the other page would have a different URL. If having a different URL is not to your liking, then you could hide your page's content and show the other page inside an iframe. It would admittedly be a hacky solution, but it is a solution.
2. Client-side CSS rendering
Instead of just loading the CSS, you could have a component which would control global CSS rules, matched by your view's name. You would have a template value rendered to a property, like:
#media print{
#page{
margin:0;
}
body{
margin:30px;
}
}
And when you visit the page where this needs to be activated, you would simply initialize a property with a style HTML element that was generated based on the template and added to head. Once you leave the given view, your component would detect that event and would remove() that element. If you choose this solution, then it would be wise to make sure that you are supporting this on more general terms, so that if some new views will have their custom global CSS, then they would be easy to integrate into your project in the future.
3. body classes
You could add/remove some custom-print or whatever class to/from body whenever the style is to be changed. This way you could add the CSS exactly once to your HTML and change the rules accordingly, like:
body.custom-print {
margin: 30px;
}
This would be a neat solution, but the problem in your case is that you have a #page rule as well and I'm not sure how you could make that dependant on body classes or some other HTML attributes. I would conduct quite a few experiments about this if I were you.
4. Iframe staging
You could avoid having that CSS in your main page, but would have a hidden iframe where you would have the CSS and would just copy the content into the CSS and once that's loaded, print that.
Don't change the whole body from apple. Instead, there are a few changes to make.
In the app component, hold a boolean for whether or not you are on apple, and use ngClass for class defined in scss.
Track which route you are on in appComponent, and set isApple accordingly
Add a div around all your html, for container to take full size
Add global html, body setting height to 100% so you see color everywhere
Remove body overriding in apple
so,
appComponent.ts:
isApple: Boolean;
constructor(router: Router) {
router.events.subscribe(v => {
if (v instanceof NavigationEnd) {
this.isApple = v.url === "/apple";
}
});
}
appComponent.html:
<div [ngClass]="{'red':isApple}" class="container">
<p>
There are two components: Apple and Banana. Switching between them will show
the problem.
</p>
<router-outlet></router-outlet>
</div>
appComponent.scss
.red {
background-color: red;
}
.container {
height: 100%;
}
apple.component.scss (remove body)
/*Sample "global" CSS, that affects something outside the current component.*/
::ng-deep {
#media print {
#page {
margin: 0;
}
}
}
styles.scss (global)
html, body {
height: 100%;
}
You can see this altogether at this Stackblitz link
You can add different css files in the component (for instance, app-task.component.ts):
#Component({
selector: 'app-task',
templateUrl: './app-task.component.html',
styleUrls: ['./app-task.component.scss', './styles2.scss', './styles3.scss']
})
In this example, the style files are in the same folder that the component, but this is not the best option: you have to put the files in assets, for example. Also, be careful with the thread of the styles, since the first one you put will be put before the second (obviously).

Styling from separate CSS files not applied with new components

I have been trying to build a small project with React for the past few days and all went great until today. For some reason, no CSS is applied to new components! All the CSS that worked before is still up and running but if I'd add something like a div in between an already existing div, the new div will not pick up any CSS!
Example:
<div className="DivStyle"> // Styling applied!
<div className="DivStyle"> </div> // Styling completely ignored!
<div>
It is probably worth mentioning that I am still able to style the components inline.
Also, looking at the sources in Chrome, the styles are uploaded!
Here is my concrete example:
import '../styles/drawers.css';
class BottomFilterDrawer extends React.Component<IBottomFilterDrawerProps, IBottomFilterDrawerState> {
...
public render() {
return(
<Drawer
open={this.state.isOpen}
anchor="bottom"
// tslint:disable-next-line jsx-no-lambda
onClose={() => this.toggleDrawer(false)}>
<div className="BottomDrawerContainer" style={{margin: "10px"}}> // Styling for "BottomDrawerContainer" class not applied!
...
</div>
</Drawer>
);
}
}
The CSS file:
#BottomDrawerContainer {
margin: 10px;
}
I am certain that the import path is correct, Typescript wouldn't even let me run it if it weren't.
You are applying css for class not for id. So your css must be like below.
.BottomDrawerContainer {
margin: 10px;
}
if you want to apply for inner div.
.DivStyle .DivStyle {
//style for inner div
}
do not use again same id, React create error and if show any error first you solve error otherwise browser not show anything

How to set background image for Angular component?

In my angular app I would like to set a background image for a specific view.
To this end, I added the following to the css-file of a component:
body {
background-image: url("../../../assets/images/backgroundImage.jpg");
}
However, the background doesn't change.
This the the file path of the file containing the css-code shown above:
angular-app/src/app/users/profile/profile.component.css
... and this is the file path of the background-image:
angular-app/src/assets/images/backgroundImage.jpg
I also tried
body {
background-image: url("assets/images/backgroundImage.jpg");
}
... but this resulted in a warning during compilation and didn't work either.
What am I doing wrong?
I gave the root element class "root" and then put the following into the css-file:
.root {
background-image: url("../../../assets/images/backgroundImage.jpg");
}
... now the background changes but not for the whole vertical length of the screen (the lower part remains white):
According to Angular, The styles specified in #Component metadata apply only within the template of that component.
you can use a hack like this
In your styless.css file add this
.selector {
background-image: url("../../../assets/images/backgroundImage.jpg");
}
now in your component you can do this
ngOnInit() {
document.body.className = "selector";
}
ngOnDestroy(){
document.body.className="";
}
But this is highly not recommended, i dont know what your code looks like, but there must be another way.
Scale your component to fit whole view-port
set the background on your component
I will work on a plunker and link to this file as an edit when done
I will add another answer for this because its totaly different from my previous answer
in your component import ViewEncapsulation from angular/core
import { ..., ViewEncapsulation } from '#angular/core';
In your #component metatag add encapsulation: ViewEncapsulation.None,
#Component({
...
encapsulation: ViewEncapsulation.None,
...
})
This has a side effect though, all styles in your component will be available to all other components once it loads.
You can check more about it on the Angular page
You probably need to either create a service or use ngrx to communicate between the child component and app.component to switch the style of the app.component.html using ngClass.

angularjs delay route change upon click as long new background image does not load

My web app shows body background for a second then it loads the dynamic background when i route from one page to another. I am trying to remove that white flash by adding a splash screen using AngularJS. I looked at some tutorials but were not able to find exact solution.
how do i avoid showing the white body background before my div background loads?
any suggestions?
HTML
<body class="hold-transition skin-blue sidebar-mini sidebar-collapse">
<div class="wrapper" ng-style="{'background': backgroundImg}" >
<div class="content-wrapper">
<section class="content">
<div ng-view></div>
</section>
</div>
</body>
my route
$routeProvider.when('/about',{
templateUrl:'partials/about.php',
controller: 'pageController'
});
My controller
app.controller("pageController",function($scope, $rootScope){
$scope.title = "About Us";
$rootScope.backgroundImg="url('http://abounde.com/uploads/images/abt-min.jpg')"; //abt bg
$scope.$on('$viewContentLoaded', function() {
console.log("about page loaded");
});
});
From what I understand, the pb is that when you route to another page then you launch a new request to download your background with your url(...). Because this request can take time then you have your white screen.
So a solution could be to "preload" all background images when you open your app. So before displaying anything on your app you can display a "loading..." div. In background download all your backgrounds.
Once this is done, when changing route then simply change the css class of your wrapper div to the css class containing the correct background image.
On this particular page, you could have a div that wraps the entire document.
Give this div a class with css ie .document-wrapper-invisible
in your css give .display-none class a display property of none
.dislay-none {
display: none;
}
When your view content loaded function runs, remove this class. You can simply set a variable to be true and use ng-class to conditionally remove the class. ie
<div ng-class="{display-none: !documentLoaded}">
// your page content
</div>
in your controller
$rootScope.$on('$viewContentLoaded', function() {
console.log("about page loaded");
$scope.documentLoaded = true;
});
Something along these lines should work (I've only ever used rootScope for the viewContentLoaded event, if $scope works on it's own then great)

React Routes - different styling on body css tag

I have two routes on my React app: /a and /b.
For /a, I want the body css tag to have a background-color: red;.
For /b, I want the body css tag to have a background-color: blue;.
Both components a and b live in different .JSX files, and both import their own main.scss file which defines their own respective body background-color.
However, since the entire app is compiled into the body tag, there seems to be a conflict, and only one of the body tags is respected for both routes.
<body>
<script src="bundle.js" type="text/javascript"></script>
</body>
The reason I want it on the body tag and not just a container div is that I want the background-color to be visible when I scroll outside the bounds of the page (the bounce effect on Mac and iOS).
What's the proper way to do this?
That's happening because when you import your styles in your component without CSS Modules, the styles are global so your body style is defined two times (you can see all the styles in the <head> tag).
You can fix that by setting the background color in your component componentDidMount() method.
Example
componentDidMount(){
document.body.style.backgroundColor = "red"// Set the style
document.body.className="body-component-a" // Or set the class
}
or, if youre using functional components you can do the same with useEffect...
useEffect(() => {
document.body.className = 'class-name';
}, []);
I agree with what QoP said but, as an add on to that, you should also make sure to use componentWillUnmount to set it back to whatever it normally is outside that component.
for example:
if normally for the whole application text-align is left but for one component you want it to be center, but after the component it needs to return to being left, you will do the following:
componentDidMount() {
document.body.style.textAlign = "center"
}
componentWillUnmount(){
document.body.style.textAlign = "left"
}
Add this code
componentDidMount(){
document.body.style.backgroundColor = "white"
}
Hope to help.

Resources