how to use angular material form field and flex-layout - angular-flex-layout

I want to have 2 form input fields in one row:
1. the first has a fixed with,
1. the second should grow and shrink, but this does not shrink below 180px.
Here is a full stack-blitz example
When you start the app, we see this
There maybe another issue:
I think the 2nd input field should already show the hint text and the horizontal line - but it will only show it when it get's the focus.
Is this the expected behaviour or am I missing something?
Anyway. The main issue is that the 2nd field does not shrink as expected. It will not shrink below 180px:
In the chrome dev-tool I can see that the input element is wrapped with a div class="mat-form-field-infix"> and the class mat-form-field-infix has a fixed width of 180px!
The only workaround that I came up with is to override this width with using ::ng-deep.
You can activate this in the co-input-field.component.scss file of the Stackblitz example
:host ::ng-deep .mat-form-field-infix {
// width: auto !important;
width: unset !important;
}
With this workaround the 2nd input shrinks as expected:
But ::ng-deep is deprecated and will be removed.
So what is the right way to make the input shrink as expected?

since .mat-form-field-infix has a fixed width of 180px there is no way of making form field shrink beyond 180px. inevitably .mat-form-field-infix must be overridden.
you can achive the same result with ::ng-deep in a couple of ways;
1.disable view encapsulation for that particular component. However, this approach has a huge drawback that all the styles in your component becomes global so they need to be managed carefully.
#Component({
selector: 'app-co-input-field',
templateUrl: './co-input-field.component.html',
styleUrls: ['./co-input-field.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class CoInputFieldComponent {}
and then in co-input-field.component.scss you do the following
app-co-input-field {
.mat-form-field-infix {
width: auto !important;
}
// all other component styles goes in here
// in order to keep them isolated from global scope
}
2.don't disable view encapsulation. use the element selector of parent component in global styles.
put the following in styles.scss
app-co-input-field {
.mat-form-field-infix {
width: auto !important;
}
// co-input-field.component.scss still can be used for encapsulated styles
}
3.don't disable view encapsulation. define a global rule for this particular situation.
put the following in styles.scss
.shrinking-mat-form-field {
.mat-form-field-infix {
width: auto !important;
}
}
and apply the .shrinking-mat-form-field class to corresponding element
<mat-form-field style="width: 100%" class="shrinking-mat-form-field">
<input matInput placeholder="placeholder" />
<mat-hint align="end">hint text</mat-hint>
</mat-form-field>
Even though second and third approaches are fundamentally same I personally prefer the third approach in order to make it readable, keep it consistent over the project, have minimal side effects and manage them from a single point.

:host ::ng-deep.mat-form-field-appearance-legacy .mat-form-field-infix {
padding: 0.4375em 0;
display: flex;
}

Related

Angular change child component style from parent component but not globally

I have created a shared component(<nextgen-table></nextgen-table>) based on Mat-table (Angular Material). While using this component inside a project, discover that I need to change the behavior(width) of the table columns.
I have exported nextgen-table in my other components (let's say X, Y ) where nextgen-table is, of course, a child component.
To change the width of specific columns of the mat-table I have to use something like this:
mat-cell:nth-child(1),
mat-header-cell:nth-child(1) {
flex: 0 0 40%;
text-align: left;
}
mat-cell:nth-child(2),
mat-header-cell:nth-child(2) {
flex: 0 0 20%;
}
The above CSS code I was implementing in the X.component.css and it was not working because of encapsulation I guess.
After a little bit of search, I have found the solution which worked correctly just by adding the encapsulation: ViewEncapsulation.None in the Component decorator of x.component.ts. After this solution, I was navigating from the component X to the Component Y in which I didn't implement the above CSS code. But the component Y had the first two columns as I wanted only for component X but somehow component Y had also which I didn't want for the component Y.
So my question is how can I update the style of nextgen-table from the parent component which only applies for the parent component and not in the other components.
I have also tried to use
:host(mat-cell:nth-child(1)){
flex: 0 0 40%;
text-align: left;
}
:host(mat-header-cell:nth-child(1)) {
flex: 0 0 40%;
text-align: left;
}
but nothing happened/changed.
Thanks in advance for the help
You can use the ::ng-deep pseudo class, to specifically target child elements without changing the view encapsultation for the whole component (which would mean that all its rules would leak).
Note: ::ng-deep has been marked as deprecated for since a few major versions now, but they will not remove suppoprt until they have a workaround.
parentX.html
<div class="compContainer">
<nextgen-table></nextgen-table>
</div>
parentX.scss
::ng-deep .compContainer nextgen-table
{
mat-cell:nth-child(1),
mat-header-cell:nth-child(1) {
flex: 0 0 40%;
text-align: left;
}
mat-cell:nth-child(2),
mat-header-cell:nth-child(2) {
flex: 0 0 20%;
}
}
You could also add your css rules to the global style.scss file.
//Rules for parent X
app-parent-componentX .compContainer nextgen-table
{
mat-cell...
}
//Rules for a parent Y
app-parent-componentY .compContainer nextgen-table
{
mat-cell...
}
All you need to do is use both :host and ::ng-deep pseudo-class selectors in your X or Y component.
Here is the working demo.
And here is the quick explanation.
styles which are written for the <nextgen-table> inside let say nextgen-table.component.css are get encapsulated by angular by adding a specific attributes for each style. i.e if you have written something like,
.mat-header-cell{
background-color: #ff0000;
}
then it becomes something like,
.mat-header-cell[_ngcontent-c29]
background-color: #ff0000;
}
So all we need to do is to override this style inside our component X or component Y.
We have ::ng-deep pseudo-selector which will prevent angular from encapsulating out component's css.
But using ::ng-deep will leak our css on to parent components as well. So to prevent that we need to encapsulate out ::ng-deep style. to do that we can use :host pseudo-selector.
so if we write following css inside component X,
:host ::ng-deep .x-table .mat-header-cell{
background-color: lightblue;
}
then it will become something like,
[_nghost-c82] .x-table .mat-header-cell {
background-color: lightblue;
}
now this above css selection has higher precedence than the style written in the table component .mat-header-cell[_ngcontent-c29].
That's how we can override child component's style inside any parent component.
I hope this will help.
Update:
As you can see in Angular's official docs that ::ng-deep is deprecated.
The shadow-piercing descendant combinator is deprecated and support is
being removed from major browsers and tools. As such we plan to drop
support in Angular (for all 3 of /deep/, >>> and ::ng-deep). Until
then ::ng-deep should be preferred for a broader compatibility with
the tools.
So if you don't want to be dependent on ::ng-deep than,
You can use ViewEncapsulation.None in your <nextgen-table> table component which you have already tried. Demo here
And to prevent the style from bleeding into other components you can scope the table's style by adding the selector in front of all the styles like this.
nextgen-table .mat-header-cell{
background-color: #ff0000;
}
and then you do the same for your X component.
Disable view encapsulation using ViewEncapsulation.None
Then override styles on table component by writing styles that have higher specificity than the table's actual style.
disable encapsulation in side your X component,
#Component({
selector: "app-x",
styleUrls: ["x.component.css"],
templateUrl: "x.component.html",
encapsulation: ViewEncapsulation.None
})
export class XComponent {
}
then override the table's component style in x.compoent.css
app-x nextgen-table .mat-header-cell{
background-color: lightblue;
}
If you don't want to disable the view encapsulation then you can write styles directly into global stylesheet styles.css.
Just remember that It's all about overriding and scoping your styles.
The only reliable way i've ever found is to use ::ng-deep
Anything else seems to be "hit or miss" intermittently

Expand all PrimeNG Accordion panels automatically for Printing

I am currently using the PrimeNG library's accordion component in my angular project. See info here.
The template includes some special css styling for printing the page--something like the following:
#media print {
.profile-progress-bar, .top-template-header-content, .header.profile-header{
display: none !important;
}
html, body {
height: auto;
font-size: 10px !important;
}
p-accordionTab > div {
display: block !important;
selected: true !important;
}
}
What I am trying to do, is automatically expand all accordionTab elements when the #media print rendering is processed for the page to be printed.
From the documentation I see that each accordionTab element has a [selected] property which can be bound to and set to "true" in order to expand the tab.
Selected Visibility of the content is specified with the selected
property that supports one or two-way binding.
However, can this be somehow automatically triggered when the #media print rendering occurs?
Thanks!
media query is the way to go, you can take a css only approach to achieve this; no change in TS or HTML files
relevant css:
#media print {
::ng-deep .ui-accordion-content-wrapper-overflown {
overflow: visible;
height: auto !important;
}
}
complete demo on stackblitz here
This is an interesting one. To keep it inside the realm of Angular, you could use the #angular/cdk/layout library and inject MediaMatcher. You could also, of course, do almost this exact same thing using JavaScript (see here... the cdk/layout method I'll show you really just wraps this).
The MediaMatcher service has a method called matchMedia, and from there you just add a listener:
import { MediaMatcher } from '#angular/cdk/layout';
constructor(private readonly mediaMatcher: MediaMatcher ) { }
ngOnInit() {
mediaMatcher.matchMedia('print').addListener(e => e.matches ?
console.log('printing!') : null);
}
So where I've put the console.log, just perform your logic to get the accordians to expand.

Component is briefly rendering without styles on first render

When I open my react app, the component below flashes with width:100%, probably because it inherits it from the material-ui card.
In my react app there are a lot of these components being rendered, each with their own width which are based on the parent component's data. I set the width with an inline style based on the props.
As I understand, the component has the inline style as it is created and there should be no delay to apply it. However I see all the SceneThumb components with 100% width for a a fraction of a second, before they apply the given inline style.
If I change the css of scene-thumb-parent to include some width, say 10% for example, then I'll see them all with 10% for a fraction of a second, before the inline style is applied. That makes me think there is a delay in applying inline css, but it really puzzles me..
Is this to be expected of react? Or of html in general? Is there any way to reduce this inline style application delay? Maybe it's something to do with the dev hot reloading setup I get from create-react-app?
SceneThumb.js (code that is irrelevant to the question has been omitted):
import React, { Component } from 'react';
import './scene-thumb.css';
import Card from 'material-ui/Card';
class SceneThumb extends Component {
render() {
return (
<div
className='scene-thumb-parent'
style={{width:this.props.width, left:this.props.left}}
>
<Card
className={this.props.selected?'scene-thumb-selected':'scene-thumb'}
>
<span>
Hello world!
</span>
</Card>
</div>
);
}
}
export default SceneThumb;
scene-thumb.css:
.scene-thumb-parent {
position:relative;
text-overflow:clip;
white-space:nowrap;
overflow:hidden;
min-width: 12px;
}
.scene-thumb-selected {
border: 2px solid red;
border-radius: 5px;
}
.scene-thumb,.scene-thumb-selected {
padding: 2px;
margin:2px;
position:relative;
}
The width prop is initially null or some other value. A moment later, the prop is updated which triggers another render. This is why you're seeing the flash you're talking about.
You can test this by adding the following to your render() function:
console.log(this.props.width)
You'll probably see it logging at least twice with different values.
There are many ways you can fix this. What makes most sense would depend on the rest of the application, and your personal preference. Regardless, here's one way:
render() {
if(!this.props.width) return null; //if it's null, render nothing.
return (
<div className='scene-thumb-parent' style={{width:this.props.width, left:this.props.left}}>
<Card className={this.props.selected?'scene-thumb-selected':'scene-thumb'}>
<span>Hello world!</span>
</Card>
</div>
);
}

className on Drawer component doesnt work

I'm using react and material-ui and I have come across an issue, I want to define some css behavior for the drawer component, and I have read that it is quite simple, that all I have to do is use the className property, but for some reason it doesn't work.
Here is my css:
.drawer {
width: 200px
}
.drawer:hover {
background-color: black
}
Here is my usage of the drawer:
<Drawer open={this.state.isLeftNavOpen}
docked={false}
className='drawer'
onRequestChange={this.changeNavState}>
<MenuItem primaryText='Men'
onTouchTap={() => browserHistory.push({pathname: '/products', query: {category: MEN}})}/>
<MenuItem primaryText='Women'
onTouchTap={() => browserHistory.push({pathname: '/products', query: {category: WOMEN}})}/>
<MenuItem primaryText='Kids'
onTouchTap={() => browserHistory.push({pathname: '/products', query: {category: KIDS}})}/>
</Drawer>
I tried wrapping the Drawer with div but still no success.
Any idea what I'm doing wrong?
The library does seem to be adding the className, but this issue you are seeing seems to be a consequence of material-ui setting styles directly on the element, which take priority over those on the class you've added. There are a couple of options until the library makes some changes/fixes, such as:
1) set the width and styles inline with the style and/or width properties: (fiddle)
<Drawer open={this.state.isLeftNavOpen}
docked={false}
width={200}
style={{'background-color': 'black'}}
className='drawer'>
Unfortunately this approach doesn't allow for :hover styling though, and their current inline styling solution is likely to be changed in the near future (see issue 1951 and those that follow it). That means that your only real solution at the moment to this specific problem is to:
2) mark the styles in the css as !important to override those set on the element by the library: (fiddle)
.drawer {
width: 200px !important;
}
.drawer:hover {
background-color: black !important;
}
You can also use a combination of the two, passing the width as a prop and only having the hover background style be !important.
(Using LeftNav (the older version of Drawer) in the fiddles because it's in the easiest-to-consume package I could find at time of writing for material-ui, found it on this comment).

Adjust Angular UI Boostrap Modal Size/Width

Essentially what the title says - need to make this wider. Tried several solutions, neither work. Note that the "backdropClass" is applied perfectly and works, but the windowClass doesn't, nor the "size" option. I have tried them independently, nothing. CSS is in the same folder as the working backdrop class"
$modal.open({
templateUrl: 'myTemplate.html',
controller: 'controllingControllerCtrl',
backdrop: 'static',
backdropClass : 'blackBackgroundModal ' +
'blackBackgroundModal.fade blackBackgroundModal.fade.in',
windowClass: 'resizeModalWindow' +
'resizeModalDialog',
size: 'sm'
});
}
}
CSS:
.resizeModalWindow .resizeModalDialog {
width: 5000px;
}
What needs to be done for at least the "size" option to register - I don't really need custom widths.
Edit: Forgot checked links!
Checked this Q first
Then this
And of course docs
bootstrap css has media queries defining the width of a modal based on screen size. to globally overwrite them use !important
like this
.modal {
size: 5000px !important;
}
You should have white space between those two classes will recognize by the css rule.
windowClass: 'resizeModalWindow ' +'resizeModalDialog',
^^^added space here
Add this to your CSS:
.resizeModalDialog .modal-dialog{
width: 5000px;
}
Add the property where you instance the modal
windowClass:'resizeModalDialog',
windowClass will not resize another way.

Resources