Angular Material 2: Sidenav has no backdrop - html

I am currently developing an Angular 2 site and having some trouble with the SideNav component.
The SideNav can have 3 modes, none of which seem to change what happens when I open the Sidenav. I am trying to get the backdrop to display after opening.
The sideNav does open, however the backdrop doesn' t show up.
The root of the app is the md-sidenav container. The SideBar component will also be passed to the NavBar component as the open button is defined in there.
Note that I have also tried to bring the SideBar component outside the <md-sidenav-container>
<md-sidenav-container>
<sidebar #sideBarComponent></sidebar>
<navbar [sideBar]=sideBarComponent></navbar>
<router-outlet></router-outlet>
</md-sidenav-container>
The SidNnav currently contains 2 links and is set to mode "over" (which should create a SideNav and backdrop over the main content)
<md-sidenav #sideBar mode="over">
<md-nav-list>
<a md-list-item routerLink="/home" (click)="sideBar.close()" routerLinkActive="active-link">
<md-icon md-list-icon>home</md-icon>
<p md-line>Home</p>
</a>
<a md-list-item routerLink="/users" (click)="sideBar.close()" routerLinkActive="active-link">
<md-icon md-list-icon>account_circle</md-icon>
<p md-line>Users</p>
</a>
</md-nav-list>
</md-sidenav>
Inspecting the page using chrome's developer tools reveals that a backdrop is created (<div class="md-sidenav-backdrop"></div>) and covering the whole page, however it is set to hidden and has no effect due to not having a functional CSS applied to it. (only set fixed width, height and position, nothing visible that alters the page)
The prebuild-css has a background color tag for it, however it needs a .md-sidenav-shown:
.md-sidenav-backdrop.md-sidenav-shown {
background-color: rgba(0, 0, 0, .6)
}
Is there anything that would cause this behavior to happen?
None of the tutorials and examples I found did something complicated so I think it is due to my set-up.
Relevant Libraries and versions (All webjars):
Angular 2.4.1
common
core
compiler
forms
http
platform-browser
platform-browser-dynamic
router
Angular-Material 2.0.0-Beta.1
SystemJs 0.19.41
Core-js 2.4.1
Reflect-Metadata 0.1.8
Typescript 2.1.4
RxJs 5.0.1
HammerJS 2.0.6
Zone.js 0.7.4

It seems that you cannot put md-sidenav inside component because there is no link between md-sidenav-container and child md-sidenav's. Md-sidenav-container uses #ContentChildren to access md-sidenav instances.
So there must be direct parent-child relation between md-sidenav-container and md-sidenav.

To keep the scope of CSS defined in your.component.css limited to only that component, Angular processes the CSS/SCSS/SASS files and adds "attribute" selectors to each CSS rule defined in that file.
In your case, it converts your .md-sidenav-backdrop.md-sidenav-shown {...} CSS rule into something like:
.md-sidenav-backdrop.md-sidenav-shown[_ngcontent-xyh-65] {
background-color: rgba(0, 0, 0, .6)
}
Angular is strict about restricting the scope. The trouble is that the backdrop <div class="md-sidenav-backdrop"></div> is added by the material component (at browser render time, I presume). Hence, the selector suffix [_ngcontent-xyh-65] is not added to that element at compile time.
The only way around it, that I could find till now, is to move your CSS to <root>/src/styles.css.
UPDATE: As Rose suggests, it would be safest to have your component name selector prefixed to the CSS you add in styles.css. In your case:
md-sidenav-container > .md-sidenav-backdrop.md-sidenav-shown {
background-color: rgba(0, 0, 0, .6)
}

Related

Can you set body id dynamically in React?

I am trying to implement dark mode in my app.
The idea is to add this to the root element:
<div id="dark">
And then this in CSS:
#dark {
background-color: #1A1A2E;
}
And then in Css, customize each dom element by using classes. For example, here I will work on cards:
#dark .card-body {
background-color: #16213E !important;;
}
#dark .card-header {
background-color: #0F3460 !important;
}
Now, this works perfectly fine.
But, with Modals, it does not work. I think it's because Modals are not rendered initially so for some reason the dark style does not apply to them.
What worked though is adding id="dark" to each modal:
#dark .modal-header {
background-color: #0F3460 !important;
}
#dark .modal-body {
background-color: #16213E !important;;
}
#dark .modal-footer {
background-color: #16213E !important;;
}
<Modal
// dark doesn't get applied automatically for modals because apparently modals are not rendered in the beginning
id="dark"
isOpen={this.state.isModalOpen}
toggle={this.toggleModal}
>
<div className="modal-header">
But, it'll be a pain to apply this to every single modal.
One solution mentioned here:
Modal should be the descendant of a tag which has id="dark". It is
loaded by the script right below the script tag and you are trying to
put 'dark' id on some div tag and the modal doesn't lie inside it,
thus the CSS selector not targeting it.
So, you need to put id="dark" on the body tag.
This solves the modals issue.
But, the problem is in my original implementation of dark mode, I am controlling that id in the root component like this:
// Root component
<div id={this.state.should_enable_dark_mode ? "dark" : "default"}>
And should_enable_dark_mode is managed like this:
manageDarkMode() {
window.addEventListener("storage", () => {
console.log("change to local storage!");
let should_enable_dark_mode = localStorage.darkMode == "true";
this.setState({
should_enable_dark_mode,
});
});
}
So the problem with the solution mentioned above is that I couldn't find a way to control the body tag from the react app. And I am not sure if it's a good thing to do.
What do you think I should do?
I see in the comments to your original question that you decided to just modify the body element in the browser DOM, which will probably work fine since the body element is not controlled by React and will likely not be changed by any other code.
I would however like to suggest a few improvements that makes it at bit less dirty:
use a data attribute like data-display-mode="dark" as a target for your CSS selectors instead of the ID. IDs are supposed to be stable and other tools and libraries (e.g. UI test tools) might rely on this.
use the Modal.container property to attach your Modals to the App element (the React-controlled global parent div defined in your React code, which you can control, not the app-root-div in index.html). Then set your data-display-mode attribute here by React-means. This way you will not bypass the virtual DOM.
use CSS custom properties for defining your colors, and then define all dark mode modifications in one place. Don't spread your dark-mode-styling code across multiple class selectors - you will have a hard time maintaining this.

Material UI Drawer component adding unwanted shadow when not focused

I'm having some issues using the Material UI Drawer component. When I try to display it in my webpage it tries to force the focus to the inner div and adds a shadow or border to the component if you're focused anywhere else.
Does anyone know what's causing this shadow to appear and how to disable it? Example screenshot below - you'll see a blue edge at the bottom (this is the same all the way round if I resize the element)
As soon as you click on content inside the Drawer e.g. a List element the shadow goes away. I assume it must be something to do with the component being modal?
<Drawer PaperProps={{ className: classes.floatingMenu }} anchor='top' open onClose={() => {}}>
<div className={classes.dummy}>
</div>
</Drawer>
Note: The floatingMenu class only adds a margin at the top of 55px (i.e. the height of the AppBar - nothing else).
This is somewhat tricky, but possible with just some props and styling. The element that is shading the rest of the UI is the Backdrop component of the Modal component. Drawer uses Modal when it's in temporary mode. The hideBackdrop prop of the Modal controls whether the shade is seen or not, and you can pass this prop to the Drawer directly as well.
However, the Modal component itself would still cover the whole viewport, preventing you from clicking other areas of the UI, before the Drawer is closed. I'm not sure if there's a simpler way, but at least you can do it by just using CSS styling to make the Modal element "through-clickable" by setting its pointer-events to none. To restore the "clickability" of the drawer itself, you should then set its pointer-events back to all.
So, for instance, just using the style prop to make simple inline styling:
<Drawer hideBackdrop style={{ pointerEvents: 'none' }}>
<div style={{ pointerEvents: 'all' }}>
I'm a sidebar!
</div>
</Drawer>
A working example
If you twiddle with Material UI's styling solution, you could also pass the pointer-events style rule to the Drawer's own Paper through the classes prop.
I was able to fix the issue in a little more simplistic manner after reading the doc's a bit more. On the Modal page it states:
Notice that you can disable the outline (often blue or gold) with the
outline: 0 CSS property.
Based on this I didn't touch the component or inner components from my question but instead I simply added one extra CSS class outline: 0 to floatingMenu (which is already passed to the PaperProps):
floatingMenu: {
marginTop: '55px',
outline: 0
}
This resolved the problem and I no longer see the blue shadow border.

Angular 2 Material Customize md-menu

I am new to Angular 2 Material and I am trying to customize the style of the md-menu component.
<md-icon class="material-icons" [mdMenuTriggerFor]="menu">dehaze</md-icon>
<md-menu #menu="mdMenu" [overlapTrigger]="false">
<button md-menu-item>Item 1</button>
<button md-menu-item>Item 2</button>
</md-menu>
The predefined style settings work fine (e.g. setting the Menu to non-overlapping), but I would like to set the md-menu to 100% width and have a little space between the md-icon button, that expands the menu, which I can not do with the predefined directives from Angular 2 Material.
So far I found a solution with the /deep/ css command, but I read that the command is not supported by the major browsers any more.
What is a good way to customize a Angular 2 Material component? How could I style my md-menu, so that it has 100% width and some space between it´s expanding button?
To illustrate what I am talking about:
Draft of the menu
You can pass custom classes to menus.
<md-menu #menu="mdMenu" [overlapTrigger]="false" class="my-full-width-menu">
Then you can target that class with global styles.
For your needs, unfortunately, you'll need to know some information about where your menu overlay is positioned, and hardcode some repositioning
.mat-menu-panel.my-full-width-menu {
max-width: none;
width: 100vw;
margin-left: -8px;
margin-top: 24px;
}
Plunker Demo
The right way to do this is to create a custom overlay component with material's OverlayModule (current in the material package, but soon to be moved to the cdk).
In Angular, ViewEncapsulation.Emulated is the default option which means, it tries to narrow-down the scope of the affect by adding surrogate keys to the host-element etc. One option could be is to add below css. But mind, this ng-deep will also be deprecated soon. Have to wait to know the alternative! https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep
::ng-deep .mat-menu-panel {
max-width: none!important;
min-width: 400px!important;
}
To style mat-menu without turning off encapsulation for this component you should use 2 classes to increase specificity as exactly as you already did or use !important. However, to make it work you should put them into your global stylesheet so that you will override the default styles.

Prevent AEM edit window from inheriting component style

For various reasons, I have a component par section that uses the following style:
width: 250px;
margin-left: -76px;
This code increases the width of all components in the par and, for my purpose, centers it.
The issue I'm seeing now is that the AEM edit window programatically inherits the style of the par or its components as inline style. That means the width of the edit window stretches to ~3000px, so content editors can't see the left and right edges of the edit window.
Is there any way to either limit the scope of the style to only the components or specifically remove the styles from the edit windows?
AEM version 6.1, class UI
I can only assume those styles are attached to your parsys via a class? Why not restrict the addition of the class based on wcmmode.edit || wcmInit.isTouchAuthoring || wcmmode.design

Ionic - CSS is not applied when refreshing on another view

I'm currently working on my first Ionic App and working with Angular for the first time.
I am using the pie-chart library to display charts on the dashboard of the app. This works nicely if I refresh while I am on the dashboard and looks like this:
https://imgur.com/YUCAO6i,oakGp8c#1
But if I navigate to another tab, lets say the server tab, and refresh there, the width and height is not applied to the charts on the dashboard. Instead they are rendered using the standard width and height (500h x 900w instead of 100h x 100w). (See second picture on imgur). If I refresh on the dashboard again, they will render normally.
I went through the source code of the library and saw that when refreshing on the dashboard, the element[0].parentElement.offsetWidth equals 100, but if I refresh when on another view, it is 0, so the default values are used. It looks like the pie-chart directive can't access the parent when on another view.
Here is the HTML and CSS used:
HTML
<div class="pieChart">
<pie-chart data="server.chartData.cpu" options="chartOptions"></pie-chart>
</div>
CSS
.pieChart {
height: 100px;
width: 100px;
float: left;
}
I tried to find an answer for hours, but I am not even sure what exactly I need to search for. The only solution I came up with is to change the default value in the pie-chart.js, which I would prefer not to do.
EDIT
The app is open source, you can find the full code in my repository: https://github.com/AndreasGassmann/cloudatcostapp
After hours of researching I finally found the cause of the problem.
There are 2 separate behaviours causing the issue:
Ionic caches up to 10 views to increase performance. This means that when you switch from one tab to another, the first one remains in the DOM so it can be loaded quicker. However, it seems that the CSS styles are not applied to the view that is not visible. This means that the actual height and width of the div with the .pieChart class is 0 at this point. I think it's similar to setting the display: none property of an element. (A blogpost explaining the issue with jquery)
Whenever the pie-chart updates, it will set its size to the width and height of the parent element. I guess this is made so the chart will resize after you resize your window and refresh the data.
Those 2 things obviously don't go well together. Since Ionic caches the dashboard view, the <pie-chart></pie-chart> element is still in the DOM, so it will try to re-render immediately. But because the styles are not applied to the the parent div, it will just get width and height 0 and fall back to using the default values.
In normal websites the views usually don't get cached. This means that when you refresh, the <pie-chart></pie-chart> element isn't present in the DOM, so it won't try to render at all. Only after you navigate back to the view and the element is loaded again will it try to render and read the dimensions of its parent. (Which will work, since all styles are applied).
I haven't found a way how you can tell an element to "stay rendered", even if it's not the active view. This means that there are 2 options to solve this (besides changing the way the pie-chart library works):
Hardcode the height and width as the default value inside the pie-chart.js
Disable caching for that view in ionic or clear the view cache every time you refresh. You can do this by calling $ionicHistory.clearCache()
Force the style I assume with :
.pieChart {
height: 100px !important;
width: 100px !important;
float: left !important;
}