Make Angular Material mat-select automatically take width of its selected option - angular-material

How do I make automatically take width according to its selected option's width? I've looked through the API of mat-select, and was not able to find an option/input for this.

Just sharing a solution to the problem. Here's how (turn ViewEncapsulation off or prepend these selectors with ::ng-deep):
.mat-select-value {
max-width: max-content !important; // <= SOLUTION HERE...
}
.mat-form-field-infix {
width: fit-content !important; // <= and HERE...
}
Playground with this fix

My Solution is something mixed with this answer.
I took his code and modified it a bit, then in my Angular App I put the class into the styles.scss (This will be the outer most scss/css file you can find). And it works without ::ng-deep
This is my solution:
.mat-form-field-infix {
width: fit-content !important;
min-width: 200px !important;
}
This solution is not very flexible though, thats why I would suggest you calculate the width in your script and set the elements width to that. it would look something like this:
HTML:
<mat-form-field [style.width.px]="setSizeOfDropDown(16)">
Typescript:
setSizeOfDropDown(fontSize:number):number{
let width: number = minWidth; //This is so that we dont have any codesmells
return fontSize * myOptionsArr.reduce((a,b)=>{
a.length>b.length?a:b; //I used the shorthand if because it looks very clean
}).length
I multiplied the fontSize with the length of the longest string in the Array. and that is my Select width.

Related

Mat-select panel min-width

I'm trying to customize mat-select with multiple checkboxes.
for some reason the panel get wrong min-width as below:
and I don't know where its calculating this min-width.
also I tried to add panelClass and override the min-width from this class,
for example:
<mat-select #multipleSelect (selectionChange)="selectItem($event.value)" panelClass="multiple-panel" multiple>
&.multiple-panel {
min-width: 200px !important;
}
but when opening the dropdown its open with the original width (like in the pic) and after few millisecond"jump" to the custom min-width defined on the panel class.
I find the mat-select very hard to style. anybody knows how to solve this problem?
You can style your mat-select dialog box by giving a panel class (as you mentioned).
Please follow this demo : https://stackblitz.com/edit/angular-matselect-style?file=src/styles.css
to see the styled mat-select components.
Reason :
Reason for the delay is that angular for dialog-boxes, create a cdk-overlay-pane inside the cdk-overlay-container container, So in case of mat-select it provides a min-width of 180px, which is overridden by our panel class in the slight delay.
Yes, there is a slight delay in opening of dialog box and customizing its width to the specified width provided in the panel class. But the delay is acceptable in the project that i was working on. So, you can find the demo for styling the mat-select component, as i have provided 2 components and you can modify any css properties.
Try to use styles using ::ng-deep or :host >>>, if not finding any luck, please paste the styles in style.css.
Update 1 :
Tried css animations, and opacity for making smooth opening of the mat-select options.
.panel-class-applied-on-mat-select {
animation-name: opacityDelay !important;
animation-duration: 0.3s !important;
}
#keyframes opacityDelay {
0% {opacity: 0;}
25% {opacity: 0;}
50% {opacity: 0;}
75% {opacity: 0;}
100% {opacity: 1;}
}
Updated StackBlitz Demo
I used another approach.
Just added this piece of code to global style.
.mat-select-panel {
// some your code
&.ng-animating {
visibility: hidden;
}
}
You can try this solution on
DEMO StackBlitz.
Hack with opacity did not fix jumping width when select is closing.
You'll need to change viewEncapsulation to none at your component decorator.and then add following css to remove the transition effect.Have a look at viewencapsulation in angular docs https://angular.io/guide/component-styles#view-encapsulation.
#Component({
selector: 'app-selector',
templateUrl: './template.html',
styleUrls: ['./template.css'],
encapsulation: ViewEncapsulation.None
})
//CSS
.cdk-overlay-connected-position-bounding-box .cdk-overlay-pane .mat-select-panel.ng-animating {
display: none;
}
Try this way : define a panel class for your mat-select in the code and then in the global/app styling file just add:
.panel-class-name .mat-select-panel {
// add your styling here
}
It worked for me to add some component specific styling for material components.
Please go easy on me S.O. This is my first time contributing. :)
After debugging the console, and running into this issue. Solutions were not clear online. So I'm posting mine here in case someone else runs into this.
I found that there is a width permanently set for the infix class. If you unset it, and optionally add some padding to the right of the value, you'll find that will resolve the issue. Add :host for encapsulation when using ::ng-deep.
Important to Note: ::ng-deep is being permanently deprecated after Angular v14.
There is a property in the #Component() annotation called encapsulation which can be used to turn off the view encapsulation for the component instead of using ::ng-deep.
Solution for the deprecation of ::ng-deep:
#Component({
selector: 'app-selector-name',
template: `<div>Hello World!</div>`,
encapsulation: ViewEncapsulation.None,
styles: [
`
:host mat-form-field .mat-form-field-infix {
width: unset;
}
:host mat-form-field .mat-select-value {
padding-right: 0.5rem; /* 8px */
/* Alternatively, for TailwindCSS: #apply pr-2 */
}
:host .random-class {
/* some encapsulated styling... */
}
.another-random-class {
/* some non-encapsulated styling... */
}
`
]
})
Solution if you do not care about the deprecation of ::ng-deep:
:host ::ng-deep mat-form-field .mat-form-field-infix {
width: unset;
}
:host ::ng-deep mat-form-field .mat-select-value {
padding-right: 0.5rem; /* 8px */
}

Angular 2 Material Horizontal scroll Data table Border issue

I am using Angular Material Data table in my application. I need to display multiple columns with a horizontal scroll table. I am facing an issue with the table row border. It's not displaying an entire width of the row. Please check the attached image for your reference. Please help me to resolve this issue.
Please check the Link for Code reference
https://stackblitz.com/edit/angular-mdxik6?file=app%2Ftable-basic-example.html
Thanks in Advance.
Both header row and column rows are display flex by default,
changing it to display inline-flex will fix above issue
.mat-header-row, .mat-row {
display: inline-flex;
}
Use display grid in case of ipad and mobile device screens
mat-table {
display: -ms-grid ;
display: grid;
}
This might help someone else as this is an old post. Please refer to https://stackblitz.com/edit/angular-mdxik6-gnpzuh?embed=1&file=app/table-basic-example.css
Just add the following to your your-comp-component.css or your-comp-component.scss
.mat-row, .mat-header-row {
min-width: 2800px;
width: 100%;
}
Best resolve I did overcome this problem.
Hold dynamic data column with css
columns = [
{columnDef: 'select', width: 75},
...........
]
Recalculate css min width if you implement a show/hide function
recalculateCss() {
let cellWidth = 0;
for (let i = 0; i < this.selected.length; i++) {
const cell = this.selected[i];
const column = this.columns.filter((value, index) => value.columnDef === cell);
cellWidth += column[0] ? column[0].width : 0;
}
this.minWidth = cellWidth;
}
Add css in both mat-header-row and mat-row
[style.min-width.px]="minWidth"
You have style properties that are working against each other. You've set min-width: 150px; for each column, and you have 13 columns which totals 1950px. Plus you've set width: 1123px; on each row which is (obviously) less than 1950px. Also, you set max-width: 1123px; - same width as your rows - on the table itself, but the table needs to be wider than the row in order to show the scrollbar. If you fix those problems, the border will display properly. Try setting just your row width and leaving out the table and column widths settings.
Material uses a flex layout so you could add align-self: stretch; to the columns CSS. This should stretch the auto-sized items to fit the table.
Edit
Stackblitz
Removing your header and cell width element helps. You want to set your rows to have a 100% width then set a desired minimum.
.mat-row, .mat-header-row {
min-width: 1123px;
width:100%;
}
Guys this is will help you solving such issues
You have to start using :
width: max-content;
.mat-row,
.mat-header-row {
min-width: 1123px;
width: 100%;
width: max-content;
}
.mat-table {
overflow: scroll;
max-height: 500px;
}
.mat-cell,
.mat-header-cell {
min-width: 90px;
}
/*unnecessary to make table header fixed*/
.mat-header-row {
top: 0;
position: sticky;
z-index: 1;
background-color: inherit;
text-align: center;
}
find browsers support : https://caniuse.com/#search=fit-content
You need to set the expected with of the table in mat-table{min-width} plus the mat-row left and right paddings (24px each).
In your example case:
.mat-table {
min-width: calc((13 * 150px) + 48px);
}
or
.mat-table {
min-width: 1998px;
}
Hope this helps!!!
Try to use below styling. It helps to render data & borders correctly when more columns in mat-table. (horizontal scrolling issue resolved)
.mat-header-cell{
background-color: inherit;
}

How to use md-chips in line

I'm trying to use the md-chips directive of angular material in a single line, but when I add more chips than size of a field, the field add a line to bottom like in this image.
I think you need a CSS workaround to adapt the behaviour of md-chips in an input form.
For example you can use this CSS:
/* reduce input element (not visible till the user begins editing) width */
.md-chips .md-chip-input-container input {
max-width: 20px;
}
/* adapt input to md-chip height */
.myheight {
height: 49px !important;
}
/* adapt datepicker input to md-chip height */
.md-datepicker-input {
height: 49px;
}
Here is a demo: http://codepen.io/beaver71/pen/gPqPBW
P.S.: don't forget that docs declare:
Warning: This component is a WORK IN PROGRESS. If you use it now, it will probably break on you in the future.

Flexbox Orientation Change Width / Height Issues

I am attempting to use flexbox to achieve a series of sections that fill 100% width and height of the viewport. This works perfectly on desktop without any issues when resizing the browser window. On mobile however, whenever I change the orientation, the section sizing does not adjust correctly.
I have made a pen of my issue:
http://codepen.io/beefchimi/full/LlInw/
The flexbox css is:
main {
display: flex;
flex-flow: row wrap;
}
section {
display: flex;
flex: 1 0 100%;
height: 100vh;
}
article {
margin: auto;
}
I believe my implementation is correct... but I'm very surprised to see iOS not behaving as expected. Any suggestions on solving this problem?
Thanks!
Turns out this is an iOS 6 - 7 bug. More information can be found here:
https://github.com/scottjehl/Device-Bugs/issues/36
The github issue thread suggests a js plugin:
https://github.com/rodneyrehm/viewport-units-buggyfill
For my particular case, I simply implemented my own bit of jQuery that will measure the window height on window load, apply that value to all sections, then track the window height during window resize and reapply. An unfortunate work around :(
var $window = $(window),
$sections = $('section'),
windowHeight;
function adjustHeight() {
// get height of browser window on page load and resize events
windowHeight = $window.height();
// apply windowHeight to each <section>
$sections.height(windowHeight);
}
$window.resize(function() {
adjustHeight();
});
$window.load(function() {
adjustHeight();
});

jquery mobile - forcing panel open on wider screens

I've been trying to test my jquery mobile app on multiple devices. I currently have a panel that is opened via swipe or clicking on a 'menu' button.
However, on wide screens, the app just looks funky. WAY too wide. I understand this is meant for mobile, but, why not format it for ipads/surface/androids as well?
To do this, I'd like to shorten the width by requiring the panel to be open at all times when the width exceeds a specific value.
I've dug through the documentation, and the closest thing I found was:
class="ui-responsive-panel" from the following link: http://view.jquerymobile.com/master/docs/widgets/panels/panel-fixed.php
After adding it to my page header, I noticed that I can't 'swipe' the menu away when the window is wide. When I shrink the window (either on a pc browser, or by rotating the device), it can be swiped.
Is anyone familiar with this, and willing to shed some light?
I'm facing the same problem. I want the panel to stay open when the user turns the device in landscape mode (tablets) or if the window is wider than a specific width at the very beginning.
Unfortunately I did not find any solutions and the jQuery Mobilele example for responsive panels in this case.
So I found a way by using some javascript but I'm not happy with this solutions since a pure CSS solution with media queries would be nicer.
However, here is my "workaround" solution.
<script type="text/javascript">
window.onresize = function (event) {
if (window.innerWidth > 800) {
window.setTimeout(openPanel, 1);
}
if (window.innerWidth < 800) {
window.setTimeout(closePanel, 1);
}
};
function closePanel() {
$("#mypanel").panel("close");
}
function openPanel() {
$("#mypanel").panel("open");
}
$( "#mypanel" ).on( "panelcreate", function( event, ui ) {
if (window.innerWidth > 800) {
openPanel();
}
if (window.innerWidth < 800) {
closePanel();
}
});
</script>
So if the window inner width is higher than 800, the panel opens; if it is lower than 800 it closes. Furthermore the window.onresize function is required to provide the same functionality in case the user turns the device from portrait mode to landscape mode.
Hope it helped. But I'm still looking for a better solution ;)
I found a css-only solution for that issue that is much simpler.
In the media query for your responsive panel #media (min-width:55em){...} add/overwrite the following css classes:
.ui-panel-closed { width: 17em; }
.ui-panel-content-wrap.ui-body-c.ui-panel-animate.ui-panel-content-wrap-closed{ margin-left:17em; }
The second class might be different to yours depending on the swatch you are using; in this case it is "C". However, just take the content wrap class that wraps all your header,content, footer area.
In my example I used a panel with data-display="reveal" data-position="left" If you want it appearing on the right hand side just change margin-left:17em to margin-right:17em
If you want the panel to behave like "overlay", just forget about the second class i posted...
Best regards
I am facing the problem right now and I found the solution of mJay really useful. However it would be great to use media queries instead, something like this perhaps:
#media (min-width:35em){
.ui-panel{
width:30em;
}
.ui-panel-close { width:30em; }
}
Below is my "CSS" solution. What you need to know: mnuMenu is the id of the panel that I want to always have visible on the left side of the screen and lnkMenu is the id of the a tag for the button which normally shows the panel on smaller screen widths.
#media all and (min-width: 900px)
{
#mnuMenu
{
visibility: visible;
position: fixed;
left: 0;
float: left;
width: 300px;
height: 100vh;
background: none;
-webkit-transition: none !important;
-moz-transition: none !important;
transition: none !important;
-webkit-transform: none !important;
-moz-transform: none !important;
transform: none !important;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
#lnkMenu
{
display: none;
}
.ui-content
{
margin-left: 325px;
}
}

Resources