I am working out a component for a th that right now is simply a th with an icon in it:
custom-th.component.html
<th #main>{{headerName}}<div style="position: relative"><i class="fa fa-th" aria-hidden="true"></i></div></th>
custom-th.component.scss
.fa-th {
position: absolute;
bottom: 0;
right: 0;
cursor: col-resize;
}
custom-th.component.ts
#Component({
selector: 'app-custom-th',
templateUrl: './custom-th.component.html',
styleUrls: ['./custom-th.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class CustomThComponent{
#Input('headerName') headerName: string;
constructor (private renderer: Renderer2) {}
}
When I use my custom component in place of another th inside of a table like:
<thead class="thead-default">
<tr class="bg-primary">
<app-custom-th headerName="Key" ></app-custom-th>
<th>Value</th>
</tr>
</thead>
My custom th is styled different than the other non-custom th. My understanding of view encapsulation being none is that global styles (maybe from other components or bootstrap in my case) can be applied to my host component and its children. If I take the HTML outside of the host component and place it directly into my HTML it works/looks as expected.
with custom component
custom component's html without using custom component (desired outcome)
According to Firefox's inspector, the custom component is about 25x20, so setting 100% width and height does not change anything. If I set width in pixels I get a result, but the cell overall grows much larger (I am not sure why for this).
firefox inspect
From yurzui: You can't use custom elements within a tr. But we can have custom components inside of th and td, so the selector was changed from:
selector: 'app-custom-th' to selector: [app-custom-th] and the component was then changed to <th app-custom-th headerName="Key" ></th>
Related
Problem:
I'm trying to set the height of a prime p-table in angular. I already searched the net and found so many results but none worked so far (probably due to this problem beeing around for many years and changes to the framework disabled certain solutions).
I already tried two approaches the first is in the code below. The second is setting the height of the parent div of the p-table to innerWindowHeight and setting scrollheight of p-table to flex (doesn't work either).
Tools: Angular 14.2.0 and PrimeNG 14.1.1 in a fresh project
I have the following html code in app.component.html:
<div>
<p-table #table
(window:resize)="onResize()"
[value]="data"
[paginator]="true"
[showCurrentPageReport]="true"
[scrollable]="true"
[scrollHeight]="tableScrollHeight"
[rows]="10"
[rowsPerPageOptions]="rowsPerPage"
styleClass="p-datatable-gridlines p-datatable-striped p-datatable-sm"
currentPageReportTemplate="{first} to {last} of {totalRecords} records">
<ng-template pTemplate="header">
<tr>
<th>Date</th>
<th>ID</th>
<th>Description</th>
<th>Value</th>
<th>Tax</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-entry>
<tr>
<td>{{entry.date}}</td>
<td>{{entry.id}}</td>
<td>{{entry.description}}</td>
<td>{{entry.value}}</td>
<td>{{entry.tax}}</td>
</tr>
</ng-template>
</p-table>
</div>
And the following app.component.ts :
import {Component} from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Testapp';
data: any[];
tableScrollHeight: string = "700px";
rowsPerPage: number[] = [5, 10, 20, 50];
constructor() {
this.data = [];
let entry: any = {};
entry.description = "First entry";
this.data.push(entry);
}
onResize() {
this.tableScrollHeight = window.innerHeight + 'px';
}
}
Behaviour I want to implement:
What I want is, that the Paginator stays at the bottom of the window even if the table is empty or dosen't have enough entries to fill the window and that the table is scrollable (header staying at top) as soon as the table rows are bigger than the screen size.
To clarify what I want to acomplish here a screenshot how it looks:
How it looks right now
And here a screenshot how I want it to look:
What it should look like
Question:
Is it possible to acomplish this in a clean way?
Like suggested in the comment I added a stackblitz : https://angular-ivy-sfk7pw.stackblitz.io
Edit:
I found that if you set scrollHeight of table a div inside the table (generated from primeng) with class p-datatable-wrapper gets the style "max-height: XXXpx" where as XXX is the value from tableScrollHeight. I could probably write a CSS selector to change the style to min-width but that's probably not a good Idea since I would have to access the dom from the typscript file and search for the auto generated div.
Maybe you can try to add this style to your css/scss, but this is not a best practice.
.p-datatable-wrapper {
min-height: calc(100vh - 90px);
}
Note: 100vh is your screen height and 90px it's an example your pagination height.
That's it. I hope it can help.
I have a component which is reusable. This component is called from parent component multiply times and sometimes the background page of the parent component is white and sometimes is black.
My child component generates form tags dynamycally - inputs,selects, textarea.
That means i can't have fixed styles in my css in my component for my content.
So when when the background page is white - i have one style for my inputs - for example black background. When the background page is black i have another style for my inputs - for example white bacgrkound.
To solve this is issue:
i tried
Adding input property in my child component ts file
#Input()
public cssTemplate;
in html
<div [ngClass]="{'form-group-white-bg': cssTemplate == 'white', 'form-group-dark-bg': cssTemplate == 'black'}">
<label for=""></label>
....
In the CHILD component i am sending value for input property depending on where the child component is called
if it is called on page with white background
<app-form-group cssTemplate="black" formControlName="address">
</app-form-group>
if it is called on black bacgrkound
<app-form-group cssTemplate="white" formControlName="address" [data]="{ field: 'address', label: 'Address' }">
</app-form-group>
but the problem here is that sometimes on my parent component this component is called multiply times
on one page can be called 12 times where i need 10 inputs and 2 selects
on other page can be called 15 times etc.
That means that i need to repat my self 15 times
<app-form-group cssTemplate="white" formControlName="address">
</app-form-group>
<app-form-group cssTemplate="white" formControlName="someItherControlName">
</app-form-group>
and everywhere to put cssTemplate="white".
ngFor is not an optin because this child component is called multiply times but not on same place in the HTML structure in the parent.
How can i solve this DRY?
you can add styles in your styles.css (the styles general for all the application). If e.g. you has
.white h1{
color:red;
}
.black h1{
color:green;
}
You can use [ngClass] in the "parent"
<div [ngClass]="toogle?'white':'black'">
<hello name="{{ name }}"></hello>
</div>
<button (click)="toogle=!toogle">toogle</button>
See [stackblitz][1]
NOTE: I used the way [ngClass]="expresion" (where expresion use conditional operator) better that [ngClass]="{'classname1':condition;'classname2':!condition}"
Update about your comment "how can i prevent repeating my self on child call", really I don't understand so much. I don't know if you want to make a directive like, e.g.
#Directive({
selector: 'hello', //<--the selector is the selector of the component
exportAs: 'helloDiv'
})
export class HelloDirective implements OnInit{
constructor(#Self() private component:HelloComponent,private dataService:DataService){
}
ngOnInit(){
console.log(this.component.name)
this.dataService.theme.subscribe(res=>{
this.component.theme=res;
})
}
}
This allow to "extends" the component -in the stackblitz the variable "theme" change-
[1]: https://stackblitz.com/edit/angular-ivy-sjwxyq?file=src%2Fapp%2Fapp.component.html
You can use an input property to create a css class map to pass on to ngClass. This object should be an object of string arrays.
It can be pretty much as complex and contain as many classes and rules as you need it too
#Input() color: 'white' | 'red' | 'hotpink' = 'white';
classMap: any;
ngOnInit() {
this.updateClassMap();
}
updateClassMap() {
this.classMap = {
[this.color]: !!this.color, // Add this class if not null
};
}
Then in the Html simply pass it to ngClass
<div [ngClass]="classMap">
Styling Child Components depending on Parent Component
There are two approaches I commonly take in this scenario
:ng-deep - create a style rule based on a class which is set in your parent component
utilize #ContentChildren() to set a property directly on your child components and call detectChanges() manually after the change.
To adopt the first solution you need to exercise greater care in your css naming rules, as using ng-deep obviously breaks the isolation of those style rules.
To adopt the second approach needs some considering due to it technically circumventing the standard input/output flow in Angular and thus can be a bit of a surprise "undocumented behavior" for any other maintainers of the application.
I'm a bit on the fence whether I prefer one approach over the other. The first approach seems more trivial to me, but it can also cause unintended style rule overwrites, while the second approach involves a lot more scripting and seems a bit of a hack.
Approach 1: ng-deep
Give your parent component an input and update a class on a block-element wrapping your <ng-content>.
create your desired style rules in your child component.
// parent component
#Component(...)
export class FooParent {
#Input() bgStyle: 'light' | 'dark' = 'light';
}
<!-- parent component template -->
<div class="parent" [ngClass]="{light: bgStyle == 'light', dark: bgStyle == 'dark'}">
<ng-content></ng-content>
</div>
// child.css
::ng-deep .light .child-container {
background-color: lightblue;
}
::ng-deep .dark .child-container {
background-color: royalblue;
}
My targeted element in the example is .child-container, you would write a similar style rule for each element you want to affect.
Approach 2: Using ContentChildren to pass along a value
Add a #ContentChildren() decorator to your parent component which selects for your child components.
inject a ChangeDetectorRef
implement ngAfterViewInit to loop through each child and set the value
call detectChanges() once done.
add the ngClass directive as normally in your child component.
Parent
#Component({
selector: 'parent',
templateUrl: 'parent.component.html',
styleUrls: ['parent.component.scss']
})
export class ParentComponent implements AfterViewInit, OnChanges {
#Input() bgStyle: 'light' | 'dark' = 'light';
#ContentChildren(ChildComponent) childComponents!: QueryList<ChildComponent>;
constructor(private change: ChangeDetectorRef) {
}
ngOnChanges(changes: SimpleChanges) {
if ('bgStyle' in changes) {
this.updateChildComponents();
}
}
updateChildComponents() {
this.childComponents.forEach(child => {
child.bgStyle = this.bgStyle;
});
this.change.detectChanges();
}
ngAfterViewInit() {
this.updateChildComponents();
}
}
<!-- parent.component.html -->
<ng-content></ng-content>
Child
#Component({
selector: 'child',
templateUrl: 'child.component.html',
styleUrls: ['child.component.scss']
})
export class ChildComponent implements OnInit {
bgStyle: 'light' | 'dark' = 'light';
constructor() {
}
ngOnInit(): void {
}
}
<!-- child.component.html -->
<div [ngClass]="{light: bgStyle == 'light', dark: bgStyle == 'dark'}" class="child-container"></div>
// child.component.css - you would apply styles as you needed obviously.
.child-container {
width: 40px;
height: 40px;
margin: .5rem;
}
.light.child-container {
background-color: lightblue;
}
.dark.child-container {
background-color: royalblue;
}
Usage
<!-- any other template -->
<parent>
<child></child>
<child></child>
<child></child>
</parent>
Note: If you are creating the ChildComponent directly in the ParentComponent's own template you need to use #ViewChildren instead of #ContentChildren
I look for a simple way to save the color of a row, after clicking on another row
My mat-row
<tr mat-row *matRowDef="let row; columns: ['favicon', 'name', 'quality', 'created_at', 'report', 'edit']"
(click)="selectedRow = selectedRow === row ? null : row" [ngClass]="{ 'selected': row === selectedRow }">
.ts
selectedRow
css
.selected {
background-color: red!important;
}
.mat-row:nth-child(even){
background-color: #e4f0ec;
}
.mat-row:nth-child(odd){
background-color:#ffffff;
}
The problem: After click in some row, the background change to red
(this is OK) but after click on other row, the preview (red) row, lost thei background color.
Expected result:
All clicked rows need to save the red color, or EVEN BETTER - get some other color (to make the difference - row - selected row - visited row).
Ivan, you can not use an unique variable, you need use an array or a new property of your elements. Imagine you call this new variabe "selected"
<tr mat-row *matRowDef="let element; columns: columnsToDisplay;"
[class.selected]="element.selected"
(click)="element.selected= !element.selected">
</tr>
See that your "elements" can has properties that not show in the table
Problem with Angular Material components that they are wrapped in many style classes and it's hard to overwrite it. Although there is some solutions which I discovered how to overwrite mat-* styles. I will give you an examples:
1. Define style class in global style.css file
First you need to discover which classes is used for your action. It could be done with development tools in Google Chrome or other tools.
Then in global styles file (style.css or style.scss...) define that class style.
i.e. (my case):
.mat-focused .mat-form-field-label, .mat-datepicker-toggle {
color: white !important;
}
2. Define style class using ::ng-deep or >>>
(deprecated)
In your component style class define same discovered mat-* style classes using ::ng-deep or >>> prefix.
i.e. (my case):
::ng-deep .mat-focused .mat-form-field-label, .mat-datepicker-toggle {
color: white !important;
}
Although this solution is deprecated and I didn't found replacement for that to overwrite styles in component styles file.
3. Use custom style class without view encapsulation
First you need to set encapsulation parameter/flag in your component. Example:
#Component({
selector: 'app-my-element',
templateUrl: './my-element.component.html',
styleUrls: ['./my-element.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class MyElementComponent implements OnInit {
...
}
Then you can write your own style classes for Angular Material components.
It might be that Material components will not look as before (real Material components)
It's not an exact solution for your problem, but direction for finding your own solution
I am trying to override some properties of bootstrap by defining my own stylesheet and then importing it in my xyz.js file(for some component) like this:
Xyz.js
import React, {Component} from 'react';
import axios from 'axios';
import qs from 'qs';
import '../css/company.css'
export default class Xyz extends Component{
constructor(props){
.....
}
render(){
return(
<div className="container" style={{float: "left"}}>
<table className = "table">
<tbody>
<tr>
<td>{this.props.index + 1}</td>
<td>{this.props.company.name}</td>
<td>{this.props.company.type}</td>
</tr>
</tbody>
</table>
</div>
)
}
}
// I am trying to set border-top of <td> to 0px which is set to 1px
//by default by bootstrap.css but I am not able to do so
xyz.css
td {
border-top-width: 0px;
height: 2px;
}
I am trying to set border-top of 'td' tag to 0px which is set to 1px
by default by bootstrap.css but I am not able to do so.
CSS chooses which rule to use when there are multiple rules available based on a few different criteria which you should definitely Google and read more about. But in this case two things can help:
Specifity: again I hope you research this more on yourself. But basically adding a class to your td and styling td.classname will increase the specifity of your rule and make it override the less specific type selector provided by Bootstrap
!important: you can put !important after your rule to make sure it overrides everything. However there are also a lot of really cool criteria based on which multiple !important rules get ordered and applied.
You can use important for your styles.
Another option is to use styled-components. In which you can make your own styles for different tables as below.
import { styled } from 'styled-components';
const UserTD = styled.td`
border-top-width: 0px;
height: 2px;
`
<UserTD>{this.props.index + 1}</UserTD>
I am attempting to apply some css changes to mat-tooltip from angular material 2 and found in the documentation a matTooltipClass that can then be selected in the css file to make changes. However, I am not able to get it working.
component.html :
<mat-cell
*matCellDef="let productInfo"
matTooltip="{{productInfo.description}}"
matTooltipClass="tooltip">
{{ productInfo.description}}
</mat-cell>
component.scss:
.tooltip {
background-color: red;
color: blue;
font-size: 20px;
}
You have to use ::ng-deep to override default CSS for material elements:
::ng-deep .tooltip {
background-color: red;
color: blue;
font-size: 20px;
}
In addition to what was stated above,
Here are two methods that worked for me:
-in the Component.scss:
::ng-deep mat-tooltip-component{
& .mat-tooltip{
color: green; // your custom properties here.
}
}
-Globally:
.mat-tooltip{ // making the font size on the mat-tooltip 1.5rem globally
font-size: 1.5rem;
&.exaggerated-tooltip{ // to modify the tooltip create a class like this
font-size: 2.5rem; // and use it like this: *matTooltipClass="exaggerated-tooltip"* in the
color: red; // component in which you are putting the tooltip
}
}
A blog post by Siderite (Styling Angular Material tooltips) provided an answer that worked for me. I am paraphrasing from the most-relevant portion of his post and I am using the matTooltipClass="tooltip" scenario described in the Question above:
[The .tooltip class definition] should either be in the global CSS
file (so it applies to everything) or your component should declare
encapsulation ViewEncapsulation.None. [If the .tooltip class
definition is in the global CSS file], then ensure the declaration of
the class is specific enough: try this:
mat-tooltip-component .mat-tooltip.tooltip {...}.
In my case, I had defined the .tooltip class in the global styles.scss file, and it wasn't working until I followed Siderite's suggestion and defined it like this:
mat-tooltip-component .mat-tooltip.tooltip {
color: blue !important;
}
This approach is avoids using ::ng-deep as suggested in the accepted Answer. Angular documentation states that approach is deprecated. I did find I needed to use !important, which some believe is bad style.
Angular material doc says matTooltipClass supports the same syntax as ngClass.
thus you might try [matTooltipclass]="'tooltip'"
<mat-cell
*matCellDef="let productInfo"
matTooltip="{{productInfo.description}}"
[matTooltipclass]="'tooltip'">
{{ productInfo.description}}
</mat-cell>
From the example provided in the website (https://material.angular.io/components/tooltip/examples):
import {Component, ViewEncapsulation} from '#angular/core';
/**
* #title Tooltip that can have a custom class applied.
*/
#Component({
selector: 'tooltip-custom-class-example',
templateUrl: 'tooltip-custom-class-example.html',
styleUrls: ['tooltip-custom-class-example.css'],
// Need to remove view encapsulation so that the custom tooltip style defined in
// `tooltip-custom-class-example.css` will not be scoped to this component's view.
encapsulation: ViewEncapsulation.None,
})
export class TooltipCustomClassExample {}
If you stick that encapsulation: ViewEncapsulation.None line there for your component where you want to use your custom tooltip class, it ought to work. It's how I fixed the issue on my end.
Seeing that ::ng-deep is depricated now, best seems to be to add encapsulation: ViewEncapsulation.None, in your component decorator under your styleUrls line
Just be careful that all your css classes will be global then, so make sure your classes in that component have unique names if you do not want to overwrite the classes in other components accidentally
I found out there is a div element down the body with class="cdk-overlay-container". I considered its z-index. it was 1000 below the z-index of my modal. I made it the same as my modal's z-index, i.e. 1050. And it is working. I also had to put ::ng-deep before my class in CSS file.
::ng-deep .cdk-overlay-container {
z-index: 1050;
}
In case your project prohibits to use: ::ng-deep, you can use
encapsulation: ViewEncapsulation.None
in the TS file.