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.
Related
I'm using display-inline in my elements as I want them to lay out horizontally instead of vertically as they do in most of the Angular examples. That said, the CSS behaves very weird. I'm specifically having two issues.
When I click an element to drag it, it grows (shrinks back to regular size after dropped). I'm not sure exactly why this happens but it is definitely not desired. I've tried numerous things to fix this via both css and adding a cdkDragPreview element with matchSize present (this seems to be the method Angular recommends). All of those efforts failed. I came across the following bug report that seems similar to my issue: https://github.com/angular/components/issues/19060. I noted that the bug was closed, so I don't know if that means it has been fixed.
When I start to drag an element from the bottom drop list, the remaining items move around sporadically while that element is still in the drop list (when it goes out of the bottom drop list they behave as I would expect them to). I created a hide style for the cdkDragPlaceholder as this seems to be how Angular provides control of this but it only helped with the top drop lists and seemed to have no effect on the bottom.
Here is a link that illustrates both issues on StackBlitz: https://stackblitz.com/edit/spuzzler. I'm guessing that my issue can be fixed with CSS, but I can't figure out how.
Create a cdkDropList for each word. My idea is to have an outer div and an inner div that is really the element that is dragged. More over, I fixed the size of the outer div. So, when you drag, there's no re-order of the words (simply leaves an empty space instead of the word you drag)
You can see the result in this stackblitz
<div #contenedor class="categories" cdkDropListGroup>
<ng-container *ngFor="let item of items;let i=index">
<div class="categories-item" cdkDropList
cdkDropListOrientation="horizontal"
[cdkDropListData]="{item:item,index:i}" (cdkDropListDropped)="drop($event)" >
<div class="inner" cdkDrag>
<div *cdkDragPlaceholder></div>
<div class="categories-item-drag" *cdkDragPreview matchSize="true" >
<div class="inner">{{item}}</div>
</div>
{{item}}
</div>
</div>
</ng-container>
</div>
I use an observable that returns an array or words. In subscribe I equal to item and, using a setTimeout() add the size to the outter div
export class AppComponent implements OnInit {
#ViewChildren(CdkDropList, { read: ElementRef }) pills: QueryList<ElementRef>;
constructor(private renderer: Renderer2) {}
items: any[];
positions: any[];
ngOnInit() {
this.getParragraf().subscribe(res => {
this.items = res;
setTimeout(() => {
this.pills.forEach(x => {
this.renderer.setStyle(
x.nativeElement,
"width",
x.nativeElement.getBoundingClientRect().width + "px"
);
});
});
});
}
drop(event: CdkDragDrop<any>) {
this.items.splice(event.previousContainer.data.index, 1);
this.items.splice(event.container.data.index,0,event.previousContainer.data.item)
/* if we want to interchange the words, replace the two lines by*/
//this.items[event.previousContainer.data.index]=event.container.data.item
//this.items[event.container.data.index]=event.previousContainer.data.item
//event.currentIndex=0;
}
getParragraf() {
return of(
"Let him who walks in the dark, who has no light, trust in the name of the Lord and rely on his God.".split(
" "
)
);
}
}
Updated Really you needn't make a cdkDropListGroup, you can take advantage of [cdkDropListConnectedTo]. For this, you have two arrays: words and items
if res is an array of strings, you can have
this.items = res.map((x,index)=>({value:x,ok:false,id:'id'+index}));
this.words=res.map(x=>({o:Math.random(),value:x}))
.sort((a,b)=>a.o-b.o)
.map(x=>(
{value:x.value,
connected:this.items.filter(w=>x.value==w.value).map(x=>x.id)
}))
and use item.value,item.id and word.value,word.connected
See a new stackblitz
I'm here today because I'm wondering something about the NG Style with Angular (my version being the 6). How can i update [ngStyle] when I use a function to return a value.
As always, here is a simplified example of my problem:
I generate div from an array of objects.
For each section, there are two div: one on the left and one on the right.
The size of the left div changes depending on the content, so it can do both 50px and 125px.
I want the right div to fit the size of the one on his left, always half that size (2 in getLeftDivHeight).
Obviously, this will be done in each section (Container).
How can I make the ngStyle update when the div's height to the left changes (due to resizing, adding content, or page display time)? )
Here is the code:
HTML
<section class = "Container" *ngFor="let oneContent of allContent">
<div id = "{{oneContent.id}}" style="float: left">
<p> {{oneContent.Content}} </ p>
</div>
<div style="float: right" [ngStyle]="height: getLeftDivHeight(oneContent.id, 2)">
</div>
</div>
Typescript (only the related function)
getLeftDivHeight(id: string, divisionNumber: number): string {
height = document.GetElementById(id).getBoundingClientRect().height /
divisionNumber;
return height + 'px';
}
Note that I am not looking for an HTML solution, but an Angular one, the code above is just an example to explain my problem.
Thank you in advance
You could return the whole style string, for example height: 100px, from the getLeftDivHeight method
getLeftDivHeight(id: string, divisionNumber: number): string {
height = document.GetElementById(id).getBoundingClientRect().height / divisionNumber;
return `height: ${height}px`;
}
or you could use the below syntax in the template
[style.height.px]="getLeftDivHeight(parameters)"
return only numerical height value from the method.
So, I finally manage to do it, using a directive.
I used ElementRef to access the HTML Object of my right div.
import {ElementRef,Renderer} from '#angular/core';
constructor(private el: ElementRef,public renderer: Renderer) {}
Then, I used Dom to access the left div height
this.el.nativeElement.parentElement.childElement[0].clientHeight;
Then I use this.renderer.setElementStyle() to apply style
I also learn that use offscrollheight to do the math is not a good idea !
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>
I am using react-sparklines to draw charts for 5 day weather data for a given city.
This is my React container (not yet complete) that displays the weather data for each city the user searches for -
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Sparklines, SparklinesLine } from 'react-sparklines';
class WeatherList extends Component {
constructor(props) {
super(props);
this.renderWeather = this.renderWeather.bind(this);
}
renderWeather(cityData) {
const name = cityData.city.name;
const temps = cityData.list.map(w => w.main.temp);
console.log(temps);
return (
<tr key={name}>
<td>{name}</td>
<td>
<Sparklines height={40} width={80} data={temps}>
<SparklinesLine color="red" />
</Sparklines>
</td>
</tr>
)
}
render() {
return (
<table className="table table-hover">
<thead>
<tr>
<th>City</th>
<th>Temperature</th>
<th>Pressure</th>
<th>Humidity</th>
</tr>
</thead>
<tbody>
{this.props.weather.map(this.renderWeather)}
</tbody>
</table>
)
}
}
function mapStateToProps(state) {
return {weather: state.weather};
}
export default connect(mapStateToProps, null)(WeatherList);
And, I've given specific measuresments that state the width and height for each sparkline chart -
<Sparklines height={40} width={80} data={temps}>
However, while the measurements seem right when it it is rendered on chrome, the chart is completely out of proportion on Firefox. Here are the screenshots -
The top one is chrome, Firefox is below -
Next, I inspected the sparkline chart with React Dev Tools on both Firefox and Chrome. Here are the results:
Firefox -
Chrome-
The project uses only Bootstrap, and no other CSS. For this particular table, only table and table-hover classes are used. So, why is the chart rendered differently on Firefox than on Chrome, even when the width and height is constant across both? How can I fix it?
This looks like Stephen Grider's course on React, which is extremely excellent and I recommend you do all especially the GraphQL and JWT auth ones.
There is a minor issue in that one where his project shows correct and yours will probably have one that is incorrect sizing. Later in the video series, he introduces some CSS that fixes it.
If I recall correctly, it is some CSS on the SVG graphic that gives it proper styling. I played with Sparklines after, and I noticed it has a tendency to bleed with what I might call some eratic sizing, so I suspect it is a matter of getting the CSS correct.
I'm looking at my project now, try putting this in the CSS:
svg {
height: 150px;
}
I want to build an Edit popup dialog with an input form in Angular2 using the PrimeNG widgets. I run into trouble with dynamic content of that dialog box (see screenshot).
I've naïvely been trying to wrap the CalendarModule in a div that is positioned above the other elements. (see Angular Template HTML below)
<p-dialog [(visible)]="display" [modal]="true" [resizable]="false">
...
<table class="ui-datatable-responsive">
<tbody>
<tr>
...
</tr>
<tr>
<td class="ui-cell-data">Start By:</td>
<td class="ui-cell-data">
<div [style]="generateSafeStyle('position:relative; z-index:1000')">
<p-calendar dateFormat="dd.mm.yy" [(ngModel)]="value"></p-calendar>
</div>
</td>
</tr>
</tbody>
...
</table>
</p-dialog>
However it seems the DialogModule frames all its content. Is there a hack to overflow that frame?
How would you handle that?
Thank you.
P.S: The generateSafeStyle Function just uses an injected DomSanitizer and works fine.
generateSafeStyle(style:string):SafeStyle{
return this.sanitizer.bypassSecurityTrustStyle(style);
}
just use appendTo="body", it will show calendar above all, even if it is in table, popup or scroll panel
<p-calendar [(ngModel)]="invariable.value" dateFormat="mm/dd/yy" required appendTo="body" readonly></p-calendar>
So I would guess things have changed since this was originally asked, but I found that if I added
[contentStyle]="{'overflow': 'visible'}"
to the p-dialog it allowed the calendar popup to overflow the dialog border.
The only thing that worked so far were the following style options:
<p-calendar dateFormat="dd.mm.yy" [(ngModel)]="dueDate" [style]="{'position': 'fixed', 'overflow': 'visible', 'z-index': '999'}">
This however smashed up the table. So I got rid of the table and used flexboxes to align the elements. Looks better anyway like this.
It's related to overflow:auto on .ui-dialog-content
In dialog there is a div with class .ui-dialog-content make overflow:visible in that div and it will fix this problem.
If you check official PrimeNG Calendar documentation, you will find list of attributes for calendar component, among them there's style attribute which you can use to add needed CSS:
<p-calendar dateFormat="dd.mm.yy" [(ngModel)]="value"
[style]="{ 'position': 'relative', 'z-index': '1000' }"></p-calendar>
I found a better solution for this. Just add a method on click listeners and select element ui date picker
(click)="modifyStyle()"
In ts file import elementRef and Renderer2
constructor(private ele: ElementRef, private ren: Renderer2)
{}
modifyStyle()
{
let ui = this.ele.nativeElement.querySelector(".ui-datepicker");
if(ui)
this.ren.setStyle(ui, "top", "unset")
}
That's it.