MDBootstrap 5 - Class styles not applied to content projected elements - css

I am working in an Angular 14 app using MDBootstrap 5 and I am building some base components. For this example I am trying to build the List Group.
I have a list-group parent component:
#Component({
selector: 'list-group',
standalone: true,
imports: [CommonModule],
template: `
<ul class="list-group list-group-flush">
<ng-content></ng-content>
</ul>
`,
styles: [],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListGroupComponent {
}
Along with a list-group-item child component:
#Component({
selector: 'list-group-item',
standalone: true,
imports: [CommonModule],
template: `
<li class="list-group-item">
<ng-content></ng-content>
</li>
`,
styles: [],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListGroupItemComponent {
}
And I am using them in a card component template:
<list-group>
<list-group-item>
<span class="text-muted">Id: </span>
<span>placeholder</span>
</list-group-item>
<list-group-item>
<span class="text-muted">Name: </span>
<span>placeholder</span>
</list-group-item>
...
</list-group>
The issue is that when using content projection like this, the bootstrap class for list-group-flush does not get applied correctly, or at all. It should remove the top border from the first element and the bottom border from the last element.
If I get rid of the projection and instead place the list items directly inside the list group, then it will apply the css properly.
Additionally, if I try something like adding the following css to the list-group:
::ng-deep li:first-of-type {
border-top-width: 0;
}
::ng-deep li:last-of-type {
border-bottom-width: 0;
}
It will remove the top and bottom border from every element in the list.
I dont understand whats happening and am at a loss here as to why the css will not get applied and am wondering if anyone else has encountered such an issue when using content projection with MDBootstrap or another UI library.
Research Update
This is not the ideal solution, as i want to be able to just apply the list-group-flush class and use its styling instead of having to override and apply my own.
However, by adding the following to the list-group-item component:
:host {
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid rgba(0,0,0,.125);
}
:host:last-of-type {
border-bottom: 0;
}
It has accomplished what I wanted, and it will suffice for now. For anyone else on this path, this article was very insightful.
I would still like any feedback though if there are solutions to get the bootstrap class to apply to my projected content.

Related

How styling works over components in angular?

Question is not clear but I'll break it down. In angular we can write isolated css for styling. It works pretty well for native html elements. But unlike react, angular wrap our html with custom elements like <app-card>...</app-card>. When I write css for those wrapper elements, it doesn't work .
If I have a post list like
<div class="post-list">
<app-card [post]="post" *ngFor="let post of posts"></app-card>
</div>
If I write css to apply some vertical gap between app-card components in PostListComponent. Well nothing happens.
.post-list app-card:not(:last-child) {
margin-bottom: 2rem;
}
How can I make it work? Or with angular logic, how can I apply vertical gap between angular components
Just add display: block; on your app-card component & it will work as expected.
.post-list app-card {
display: block;
}
.post-list app-card:not(:last-child) {
margin-bottom: 2rem;
}
<div class="post-list">
<app-card>Card 1</app-card>
<app-card>Card 2</app-card>
<app-card>Card 3</app-card>
</div>
You can define encapsulation: ViewEncapsulation.None in your Component like this:
#Component({
selector: 'foo',
template: './foo.component.html',
styleUrls: ['./foo.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FooComponent { }
Which will treat your .css as the same if you were putting it in the global scope.
To be more accurate, it won't append .fooComponent to each css rule in foo.component.scss.
You can make the iteration in div tag then add your class
<div class="post-list">
<div class="post" *ngFor="let post of posts">
<app-card [post]="post"></app-card>
</div>
</div>
And in your css
.post-list .post:not(:last-child) {
margin-bottom: 2rem;
}
There is no reason it shouldn't work. Just tried to put in some of your code here. https://stackblitz.com/edit/angular-scss-demo-icqrye
app.component.html
<div class="post-list">
<app-new *ngFor="let item of [1,2,3,4]"></app-new>
</div>
styles.scss
.post-list app-new:not(:last-child) p {
margin-top: 2rem;
color: green;
}
And it works perfectly. Are you looking for something else?
And if you want to add the style (margins) to the component directly, you will first need to set the display of the component to block/flex as per requirement.
.post-list app-new:not(:last-child) {
display: flex;
}

ng-select multiple - Change height

This is related to the question at ng-select - Change height . I want to do the exact same but with a ng-select that enables multiple items to be selected.
Here is my code of the ng-select:
<label class="col-sm-4 text-sm-right col-form-label">Payout Format</label>
<div class="col-sm-8">
<ng-select
[items]="payoutFormats"
[multiple]="true"
[closeOnSelect]="true"
[searchable]="true"
bindValue="payoutBatchFormatID"
bindLabel="name"
placeholder="All"
[(ngModel)]="filter.payoutFormats"
name="payoutFormats">
</ng-select>
</div>
In my component I have added the following styles:
#Component({
templateUrl: './test.component.html',
styles: [`
.ng-select{
font-size:0.85em;
}
.ng-select .ng-select-container {
min-height: 3px;
}
.ng-select.ng-select-multiple .ng-select-container {
height: 3px;
}
`],
})
I was able to get this working with a non multiple select box by using
.ng-select.ng-select-single .ng-select-container {
height: 20px;
}
But changing it from .ng-select.ng-select-single to .ng-select.ng-select-multiple when multiple is enabled has no effect on the height.
Here is how my select looks like after the CSS:
I need it to be smaller.
UPDATE
In dev tools I can change min-height in .ng-select .ng-select-container as follows:
And my select box appears smaller:
However adding the same style to my component styling does not pick this up:
#Component({
templateUrl: './list-exceptions-handling-payments.component.html',
styles: [`
.ng-select{
font-size:0.85em;
}
.ng-select .ng-select-container{
min-height: 3px;
}
.ng-select.ng-select-multiple .ng-select-container {
height: 3px;
}
`],
})
You have overriden .ng-select-container and .ng-select-multiple which is correct but you hadn't overriden it's children elements.
Multiple select checkboxes have additional elements compared to select element (deselect button, view selected choices, etc)
These are in
<div class="ng-value-container">
<div class="ng-value>
....
ng-value divs contain mendoza and franklin elements:
You need to adjust the height/line-height/margin/padding defined in these children elements.
EDIT:
You need to make your child elements smaller also before the parent element will decrease in size. E.g. in GIF:

To avoid border highlighting in mat-selection-list component [duplicate]

This question already has answers here:
Remove blue border from css custom-styled button in Chrome
(24 answers)
Closed 4 years ago.
I am using mat-selection-list component in my project.I am displaying some names as shwon in below image:
I wonder, Why the border is highlighting on clicking scroll-bar as shown in above image, How can i avoid the border in the background:
HTML
<mat-selection-list>
<mat-list-option *ngFor="let player of Players">
<a mat-list-item><span>{{ player }}</span> </a>
</mat-list-option>
</mat-selection-list>
CSS
mat-selection-list{
position:relative;
margin-top:-20.5px;
width:100%;
height:80vh;
overflow-y: scroll;
}
::ng-deep.mat-pseudo-checkbox{
display: none !important;
}
TS
import {Component,ViewChild} from '#angular/core';
#Component({
selector: 'list-selection-example',
styleUrls: ['list-selection-example.scss'],
templateUrl: 'list-selection-example.html',
})
export class ListSelectionExample {
Players: string[] = ['Sachin Tendulkar', ........ 'Anil Kumble'];
}
The DEMO:
It's due to a CSS property called outline. To remove this, just add this line to your CSS class:
mat-selection-list {
outline: none;
}

I have a plunker. When I define my css globally, it works. When I define my css in my component, it fails. What's going on?

With reference to this plunker:
https://plnkr.co/edit/GWsbdDWVvBYNMqyxzlLY?p=preview
I have the same css specified in the styles.css file, and in the src/app.ts file.
If I comment in the css in styles.css and comment out the css in src/app.ts, it works.
styles.css:
/* If these are commented in, and the ones in src/app.ts are
* commented out, the three items are spaced appropriately. */
/***
md-toolbar-row {
justify-content: space-between;
}
md-toolbar {
justify-content: space-between;
}
***/
If I comment out the css in styles.css and comment in the css in src/app.ts, it fails.
src/app.ts:
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<md-toolbar color="primary">
<span><md-icon>mood</md-icon></span>
<span>Yay, Material in Angular 2!</span>
<button md-icon-button>
<md-icon>more_vert</md-icon>
</button>
</md-toolbar>
</div>
`,
// If these are commented in, and the ones in style.css are
// commented out, the three items are scrunched together.
/***/
styles: [
`md-toolbar-row {
justify-content: space-between;
}`,
`md-toolbar {
justify-content: space-between;
}`
]
/***/
})
export class App {
name:string;
constructor() {
this.name = `Angular! v${VERSION.full}`
}
}
I'm having trouble visualizing the difference between defining the css for the whole application, and for the specific component. Can someone tell me what's going on?
=================================
#bryan60 and #Steveland83 seem to indicate that the problem lies somewhere in the view encapsulation. And upon further investigation, it does in a sense.
If you look at the code below, you will see that the styles for md-toolbar and md-toolbar-row have an attribute attached. But the html for md-toolbar and md-toolbar-row does not match. Only md-toolbar has the attribute attached. md-toolbar-row doesn't. I have marked the relevant four lines with >>>>>.
So that's the problem but:
1. Do I report it to the material design people as an error?
2. Is there some workaround I can use today?
<html>
<head>
:
<script src="config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
<link href="https://rawgit.com/angular/material2-builds/master/prebuilt-themes/indigo-pink.css" rel="stylesheet">
<style>
>>>>> md-toolbar-row[_ngcontent-c0] {
justify-content: space-between;
}
</style>
<style>
>>>>> md-toolbar[_ngcontent-c0] {
justify-content: space-between;
}
</style>
<style>
.mat-toolbar {
display: flex;
: :
.mat-mini-fab,
.mat-raised-button {
outline: solid 1px
}
}
</style>
</head>
<body class="mat-app-background">
<my-app _nghost-c0="" ng-version="4.4.0-RC.0">
<div _ngcontent-c0="">
<h2 _ngcontent-c0="">Hello Angular! v4.4.0-RC.0</h2>
>>>>> <md-toolbar _ngcontent-c0="" class="mat-toolbar mat-primary" color="primary" role="toolbar" ng-reflect-color="primary">
<div class="mat-toolbar-layout">
>>>>> <md-toolbar-row class="mat-toolbar-row">
<span _ngcontent-c0=""><md-icon _ngcontent-c0="" class="mat-icon material-icons" role="img" aria-hidden="true">mood</md-icon></span>
<span _ngcontent-c0="">Yay, Material in Angular 2!</span>
<button _ngcontent-c0="" class="mat-icon-button" md-icon-button=""><span class="mat-button-wrapper">
<md-icon _ngcontent-c0="" class="mat-icon material-icons" role="img" aria-hidden="true">more_vert</md-icon>
</span>
<div class="mat-button-ripple mat-ripple mat-button-ripple-round" md-ripple="" ng-reflect-trigger="[object HTMLButtonElement]" ng-reflect-centered="true" ng-reflect-disabled="false"></div>
<div class="mat-button-focus-overlay"></div>
</button>
</md-toolbar-row>
</div>
</md-toolbar>
</div>
</my-app>
</body>
</html>
One of the Angular features is View Encapsulation which basically means that you can define styles scoped only to a specific component without affecting any other components.
By default styles are scoped only for the component they are referenced in, but you can choose to override that to make them available globally by setting your components encapsulation to None.
E.g.
import { Component, ViewEncapsulation } from '#angular/core';
#Component({
selector: 'component-that-shares-styles',
templateUrl: './component-that-shares-styles.component.html',
styleUrls: ['./component-that-shares-styles.component.scss'],
encapsulation: ViewEncapsulation.None // <-- Set encapsulation here
})
*Note that you will need to import ViewEncapsulation from #angular/core
Okay, with help from #Steveland83 and #bryon60, I came to a definite answer. The Material Design people are aware of this problem. They have made a writeup.
https://github.com/angular/material2/blob/master/guides/customizing-component-styles.md
Here's their summary:
Styling other components
If your component has view encapsulation turned on (default), your component styles will only
affect the top level children in your template. HTML elements belonging to child components cannot
be targeted by your component styles unless you do one of the following:
Add the overriding style to you global stylesheet. Scope the selectors so that it only affects
the specific elements you need it to.
Turn view encapsulation off on your component. If you do this, be sure to scope your styles
appropriately, or else you may end up incidentally targeting other components elswhere in your
application.
Use a deprecated shadow-piercing descendant combinator to force styles to apply to all the child
elements. Read more about this deprecated solution in the
Angular documentation.
I don't want to use global css, or a deprecated solution. I guess I will style with classes, and not elements. If someone has a better idea, tell me!

How to get global selector inside emulated view encapsulation (CSS / Angular2)

I have a Home component with this inside:
<alert type="info">Hello from ng2-bootstrap</alert>
Inside my home.style.scss, I have this:
:host .alert {
background-color: green;
}
which should change the background color to green, but it does not.
The above css code will produce this style:
[_nghost-wjn-3] .alert[_ngcontent-wjn-3] {
background-color: green;
}
and the final HTML looks like this:
<home _nghost-wjn-3="">
<div _ngcontent-wjn-3="" class="card-container">
<alert _ngcontent-wjn-3="" type="info" ng-reflect-type="info">
<div class="alert alert-info" role="alert" ng-reflect-initial-classes="alert" ng-reflect-ng-class="alert-info">
Hello from ng2-bootstrap Sat Sep 17 2016
</div>
</alert>
</div>
</home>
I don't know what the problem is here, but I think the selector is wrong. I'd like the final selector to be:
[_nghost-wjn-3] .alert
instead of:
[_nghost-wjn-3] .alert[_ngcontent-wjn-3]
Or in other words, why is there no _ngcontent-wjn-3 attribute on <div class="alert">...</div>?
Maybe I'm doing the whole thing wrong. What I'm trying to achieve is to customize the CSS of the individual bootstrap components (<alert> in the code above) as provided by the ng2-bootrap library (https://github.com/valor-software/ng2-bootstrap) inside my custom components (<home> in the code above).
I'm using the default view encapsulation (emulated) in the home component.
How can I do that please?
I figured it out myself. This is what I was looking for:
:host /deep/ .alert {
background-color: green;
}
The above code will produce the following:
[_nghost-wjn-3] .alert {
background-color: green;
}
This way I can modify the default styles of a bootstrap class (.alert, in this case) inside of my component <home>.
Source: https://angular.io/docs/ts/latest/guide/component-styles.html
You need:
[_nghost-wjn-3] alert[_ngcontent-wjn-3]
Instead of:
[_nghost-wjn-3] .alert[_ngcontent-wjn-3]
If you go and check your structure, alert tag has the ngcontent... attribute, not his div child with alert class.

Resources