Pagination on Angular Material Design - Show page numbers or remove the row count - angular6

Angular 6/7, Material Design.
Since I don't have access to the total number of items the item count is irrelevant (the box in the screen shot).
How do I remove the item count completely? Or alternatively show the page I'm currently on instead of the item count?
<mat-paginator
itemsPerPageLabel="Items per page"
(page)="changePage()"
[length]="resultsLength"
[pageSizeOptions]="[10, 100]">
</mat-paginator>

Remove the range label by inserting in global CSS
.mat-paginator-range-label {
display: none;
}
Insert page number instead (of course based on your API - you might not have the page info!) by inserting in your component
ngAfterViewChecked() {
const list = document.getElementsByClassName('mat-paginator-range-label');
list[0].innerHTML = 'Page: ' + this.page.toString();
}
and of course delete the CSS rule above!
Paginator now looks like this

I just modified Johan Faerch's solution to fit more to your question.
Create method which has two parameters, one for matpaginator and another for list of HTMLCollectionOf
paginatorList: HTMLCollectionOf<Element>;
onPaginateChange(paginator: MatPaginator, list: HTMLCollectionOf<Element>) {
setTimeout((idx) => {
let from = (paginator.pageSize * paginator.pageIndex) + 1;
let to = (paginator.length < paginator.pageSize * (paginator.pageIndex + 1))
? paginator.length
: paginator.pageSize * (paginator.pageIndex + 1);
let toFrom = (paginator.length == 0) ? 0 : `${from} - ${to}`;
let pageNumber = (paginator.length == 0) ? `0 of 0` : `${paginator.pageIndex + 1} of ${paginator.getNumberOfPages()}`;
let rows = `Page ${pageNumber} (${toFrom} of ${paginator.length})`;
if (list.length >= 1)
list[0].innerHTML = rows;
}, 0, paginator.pageIndex);
}
How to call this method? you can initialize this on ngAfterViewInit()
ngAfterViewInit(): void {
this.paginatorList = document.getElementsByClassName('mat-paginator-range-label');
this.onPaginateChange(this.paginator, this.paginatorList);
this.paginator.page.subscribe(() => { // this is page change event
onPaginateChange(this.paginator, this.paginatorList);
});
}
Include this method in your css file(note: do not include in the main styles.css file)
.mat-paginator-range-label {
display: none;
}
You can call onPaginateChange(this.paginator, this.paginatorList) functions wherever you need to change the page number details other than clicking on the navigation buttons on the mat paginator.
Result looks like this

Related

Is there a way to sort a table based on a cell value in Angular?

My current table looks like this:
Status
Draft
Pending
Complete
I want to sort them based on the value of the cells. Is there a way to do that? I've only been able to sort them using this code:
onChange(status: string){
const sortState: Sort = {active: status, direction: 'desc'};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);
}
But I want to sort using the values of the status themselves since I'd want to create a button which when click sorts starting from complete or draft or pending.
I'm a little confused by your question, but I think I understand what you're asking.
You're going to want to convert your values into an array and then use the .sort() function. So, assuming you have an array of your cells, we can call that let array = Cell[], you can then access the status of the cells like this:
sortCells(){
let array = Cell[]; // here we're assuming there is already a cell type and a cell.active parameter, like shown in your example.
let possibleValues = ["Draft","Pending","Complete"]; // easier way to compare two values
array.sort((a,b)=>{
let aIndex = possibleValues.indexOf(a.active); // index of gets the location of the element in an array
let bIndex = possibleValues.indexOf(b.active);
if(a > b){
return -1;
} else if(b > a){
return 1;
}else{
return 0; // they are equal
}
})
}
You can read more about sort here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

ReactStrap pagination element exceeds available width

I have a Pagination element rendered inside a Card element:
This is the code:
<Col col="6" sm="4" md="2" xl className="mb-3 mb-xl-0">
<TablePagination
pagesCount={this.props.pagesCount}
currentPage={this.state.currentPage}
handlePageClick={this.handlePageClick}
handlePreviousClick={this.handlePreviousClick}
handleNextClick={this.handleNextClick}
/>
</Col>
</Card>
This is the TablePagination React functional component:
import React, { Component } from "react";
import { Pagination, PaginationItem, PaginationLink } from "reactstrap";
import PropTypes from "prop-types";
/** I used object destructuring of the props object, to pass down the properties as variables. This improves readability by getting rid of props. */
const TablePagination = ({
pagesCount,
currentPage,
handlePageClick,
handlePreviousClick,
handleNextClick,
}) => {
return (
/**The reactstrap Pagination component encapsulates the reactstrap PaginationItem which in turn encapsulates reactstrap PaginationLink. */
/**The first PaginationItem inside the Pagination is the previous button. This is disabled when the current page is zero or less
* than zero “disabled={currentPage <= 0}”. */
<div>
<Pagination size="sm">
<PaginationItem disabled={currentPage <= 0}>
<PaginationLink onClick={handlePreviousClick} previous href="#" />
</PaginationItem>
{/* The next PaginationItem after the previous PaginationItem button is the dynamic PaginationItem. This is the one that generates the page number buttons. */}
{/* “Array(pagesCount)”: creates and initializes a new array object of length equal to pagesCount. */}
{/* “[…Array(pagesCount)].map( fn)”: using the spread operator I expand the array. After expanding, the map() method then creates a new array of PaginationItems. */}
{[...Array(pagesCount)].map((page, i) => (
<PaginationItem active={i === currentPage} key={i}>
<PaginationLink onClick={(e) => handlePageClick(e, i)} href="#">
{i + 1}
</PaginationLink>
</PaginationItem>
))}
<PaginationItem disabled={currentPage >= pagesCount - 1}>
<PaginationLink onClick={handleNextClick} next href="#" />
</PaginationItem>
</Pagination>
</div>
);
};
TablePagination.propTypes = {
//pageCount: the total number of records in our dataset.
pagesCount: PropTypes.number.isRequired,
//currentPage: the current page navigated to
currentPage: PropTypes.number.isRequired,
/**handlePageClick: a function that handles the click event when a page number is clicked.
* This function will pass the current page number which will be saved in state. */
handlePageClick: PropTypes.func.isRequired,
/**handlePreviousClick: a function that handles the click event when the previous button is clicked. This enables navigating to the previous(<<) page. */
handlePreviousClick: PropTypes.func.isRequired,
/**handleNextClick: a function that handles the click event when the next (>>) button is clicked. This enables navigating to the next page. */
handleNextClick: PropTypes.func.isRequired,
};
export default TablePagination;
I've searched in the documentation, I couldn't find a way to make the Pagination numbers be rendered in the next line in case they exceed the width limit.
You can add a style rule for the ".pagination" element
flex-wrap: wrap;
But it looks kinda crappy with several lines.
I think it's better to use dots for exceeded pages, e.g. MaterialUI pagination
I had the same problem and I wanted make this more user friendly.
So I wanted display 10 pagination links and change the starting and ending pages dynamically, like in google page results pagination.
let pageLimit = 10; // number of page links in pagination
let start = 0; // starting page
let end = pageLimit; // ending page
if (pagesCount <= pageLimit) {
pageLimit = pagesCount;
}
// increment start page when current page is greater than 5
if (currentPage - 5 >= 0) {
start = currentPage - 4;
}
// if reaching end of pagination stop increment
if (start + pageLimit >= pagesCount) {
start = pagesCount - pageLimit;
}
// increment end page when current + 5 exceeds page limit
if (currentPage + 5 >= pageLimit) {
end = currentPage + 6;
pageLimit = end;
if (pagesCount <= pageLimit) {
pageLimit = pagesCount;
}
}
These numbers may different from your data set.
Then render PaginationItem between start and end points
{[...Array(pageLimit)].map((page, i) => {
if (i >= start && i < end) {
return (
<PaginationItem active={i === currentPage} key={i}>
<PaginationLink onClick={e => handleClick(e, i)} href="#">
{i + 1}
</PaginationLink>
</PaginationItem>
);
}
})}

How to create a query selector out of a selected element?

In google chrome, especially now with custom elements, it became very cumbersome to select and element by hand nowadays, even though the browser knows the whole path to it already. Or is there a way that leads to a query selected for an element that I'm inspecting?
Situation:
What chrome can tell me:
What chrome is unable to create for me AFAIK:
While building an chrome extension I have found a need to uniquely locate an element when returning to a page. To do this I needed to create a query string for a selected element (custom context menu click)
While searching for a solution I found this unanswered question.
As I could not find an off the shelf solution or API to do the task I wrote the following function. It is untested in the wild, is very rough and ready (using poor node traversing techniques). I posted it in this state lest I forget and this question remains unanswered.
Create Query String For Element
A function to build a query string that will uniquely locate an element from a reference of the element.
const querytStr = createQueryStringForElement(myElement); // return string or undefined
if (querytStr) {
const element = document.querySelector(queryStr);
console.log(element === myElement); // expected result true
}
If the function fails to create a query that uniquely locates an element it returns undefined. else it returns the query string.
Example results
"#editor > div.ace_scroller > div.ace_content > div.ace_layer.ace_text-layer > div.ace_line:nth-child(45) > span.ace_punctuation.ace_operator"
"#buttons" // A UI container
"#buttons > div.buttons" // A sub UI container
"#buttons > div.buttons:nth-child(2)" // A button element by position
"#buttons > div.buttons:nth-child(3)" // A button element by position
How it works
The code assumes that the page is well formed (ids must be unique).
The query string will try to start with an id eg "#elementId" but if an element has no id the query will use the tag and class names. eg "div.my-class".
The tag and class name may not uniquely identify the element. To check if the query is unique, the query string is used to query the DOM from the elements parent.
If needed the query string will use the elements position to refine the query "div.my-class:nth-child(2)". Unfortunately this makes the resultant query string insensitive to changes in element order.
The query string is built up along each parent until it finds an element with an id or there are no more parents.
The final step uses the query to see if the query finds the correct element returning the query if successful.
The code
function createQueryStringForElement(element) {
const getElementSel = element => {
const tName = element.tagName.toLowerCase();
var i = 0, str = element.id ? "#" + element.id : sel = tName;
if (str.includes("#")) { return str}
str += element.classList.length ? "." + [...element.classList.values()].join(".") : "";
if (element.parentElement) {
const res = element.parentElement.querySelector(str);
if (res !== element) {
while (i < element.parentElement.children.length) {
if (element.parentElement.children[i] === element) {
i > 0 && (str += ":nth-child(" + (i + 1) + ")" );
break;
}
i++;
}
}
}
return str;
}
const queryPath = [];
const original = element;
do {
const subQuery = getElementSel(element);
queryPath.push(subQuery);
if (subQuery[0] === "#") { break }
element = element.parentElement;
} while (element);
const query = queryPath.reverse().join(" > ");
try {
const els = document.querySelector(query);
if (els === original) { return query }
} catch(e) { }
}

Absolute positioning divs in Angular

I'm developing a calendar component for Angular and I need to position some div element (representing events) in a grid.
this.days.forEach((day: SchedulerViewDay, dayIndex: number) => {
day.events = this.getEventsInPeriod({...}).map((event: CalendarSchedulerEvent) => {
const segmentDuration: number = 60.0 / this.hourSegments;
const dayStartDate: Date =
setSeconds(setMinutes(setHours(
setDate(setMonth(setYear(new Date(), day.date.getFullYear()), day.date.getMonth()), day.date.getDate()), this.dayStartHour), this.dayStartMinute), 0);
const segmentsNumber: number = (differenceInMinutes(event.start, dayStartDate) / segmentDuration);
return <CalendarSchedulerEvent>{
...
height: this.hourSegmentHeight * (differenceInMinutes(event.end, event.start) / segmentDuration),
top: (this.hourSegmentHeight * segmentsNumber)
};
});
With that code, this is the result:
As you can see the event div is not correctly positioned. I think that this is due to the grid cell borders so i've applied that fix:
top: (this.hourSegmentHeight * segmentsNumber) + (segmentsNumber / 2) // 1px for each separator line
Each border is 1px width and I consider only solid borders and not dashed ones (this is the motivation of / 2.
This almost resolve the problem. There's still a misalignment.
Moreover this is not an elegant solution but I can't figure out to make it more clean.
How can I correctly position these divs?
Thank you very much!
EDIT: implementation of inorganik's solution (first answer)
I've created the following mixins in my scss file:
#mixin day-x($attr, $attr-count: 7, $attr-steps: 1, $unit: '%') {
$attr-list: null;
#for $i from 1 through $attr-count {
$attr-value: $attr-steps * $i;
&.day#{$i} {
#{$attr}: #{$attr-value}#{$unit};
}
$attr-list: append($attr-list, unquote(".#{$attr}-#{$attr-value}"), comma);
}
#{$attr-list} {
//append style to all classes
}
}
#mixin time-x($attr, $start-hour: 0, $end-hour: 23, $attr-steps: 2, $minutes-steps: 15, $unit: '%') {
$attr-list: null;
$hours: $start-hour;
$minutes: 0;
$attr-count: ((($end-hour + 1) - $start-hour) * 60) / $minutes-steps;
#for $i from 0 through $attr-count {
$attr-value: $attr-steps * $i;
#if($i > 0) {
$minutes: $minutes + $minutes-steps;
#if($minutes == 60) {
$minutes: 0;
$hours: $hours + 1;
}
}
$hoursString: '#{$hours}';
#if($hours < 10) {
$hoursString: '0#{$hours}';
}
$minutesString: '#{$minutes}';
#if($minutes < 10) {
$minutesString: '0#{$minutes}';
}
&.time#{$hoursString}#{$minutesString} {
#{$attr}: #{$attr-value}#{$unit};
}
$attr-list: append($attr-list, unquote(".#{$attr}-#{$attr-value}"), comma);
}
#{$attr-list} {
//append style to all classes
}
}
#mixin length-x($attr, $attr-steps: 2, $minutes-steps: 15, $unit: '%') {
$attr-list: null;
$attr-count: 24 * 60 / $minutes-steps;
#for $i from 0 through $attr-count {
$attr-name: $minutes-steps * $i;
$attr-value: $attr-steps * $i;
&.length#{$attr-name} {
#{$attr}: #{$attr-value}#{$unit};
}
$attr-list: append($attr-list, unquote(".#{$attr}-#{$attr-value}"), comma);
}
#{$attr-list} {
//append style to all classes
}
}
#include day-x('left', 7, 5);
#include time-x('top', 6, 22, 1.47);
#include length-x('height', 1.47);
Then I apply these styles with [ngClass] attribute, calling the following method (I don't need getDay, for now):
getPositioningClasses(event: CalendarSchedulerEvent): string {
const classes: string[] = [
this.getDayClass(event.start),
this.getTimeClass(event.start),
this.getLengthClass(differenceInMinutes(event.end, event.start))
];
return classes.join(' ');
}
private getDayClass(date: Date): string {
return '';
}
private getTimeClass(date: Date): string {
let hours: string = date.getHours() < 10 ? `0${date.getHours()}` : `${date.getHours()}`;
let minutes: string = date.getMinutes() < 10 ? `0${date.getMinutes()}` : `${date.getMinutes()}`;
return `time${hours}${minutes}`;
}
private getLengthClass(durationInMinutes: number): string {
return `length${durationInMinutes}`;
}
This is the result:
Graphically it works like a charm but I need to keep segments height (58px) aligned to the percentage increment used to generate positioning classes (now it's 1.47%).
Is there a way to make those variables (segments height and percentage height and top position increments) from Angular code, making the segment height configurable by the user and let these increments to adapt itself?
Thank you very much!
EDIT 2: another problem
Hi again! I've encountered another problem.
The component hour range it is not fixed but it is configurable from outside.
In the example in the question is 6AM - 22PM. For that range the percentage of 1.47 is fine but if I change the range (eg. 0AM - 22AM) the calendar height is higher and the percentage is not ok anymore.
I think I need to calculate those positioning values from Typescript. But I can't figure out how to do that.
To try something I'm trying this:
ngAfterViewInit(): void {
this.calendarContainerHeight = this.calendarContainer.nativeElement.clientHeight;
const segmentPercentage: number = 100.0 * this.hourSegmentHeight / this.calendarContainerHeight;
console.log("calendarContainerHeight: ", this.calendarContainerHeight);
console.log("segmentPercentage: ", segmentPercentage);
setTimeout(() => {
this.view.days.forEach(d => {
d.events.forEach((event: CalendarSchedulerEvent) => {
let hours: number = event.start.getHours();
if (this.dayStartHour > 0) { hours = hours - this.dayStartHour; }
const numberOfSegments: number = hours * this.hourSegments;
event.top = numberOfSegments * segmentPercentage;
});
});
});
}
Then I've added [style.top.%]="event.top" to the event div. This is the result (ignore height for now, I've not managed them yet):
As you can see the percentage is not accurate and those events which are in the middle of the day (or towards the end of the day) are not positioned correctly.
How could I solve this problem?
Thank you very much, again!
It seems like a brittle solution to calculate pixel distances in code. I would use CSS classes. You can have an SCSS mixin generate all the possible classes for you for days, times and segment lengths. Make 3 methods that return css class strings, one for day, one for time, and one for segment length which you can apply with [ngClass]. Then a method which calls each one, something like below. This assumes you have an array of segments which store date/time/length info on them:
getPositioningClasses(segment: any): string {
const classes = [this.getDayClass(segment.dayStartDate), this.getTimeClass(segment.dayStartDate), this.getLengthClass(segment.timeLength)];
return classes.join(' ');
}
So your markup might end up looking like this:
<segment [ngClass]="getPositioningClasses(segment)"></segment>
Compiled:
<segment class="day0 time1430 length130"></segment>
day0 - you could have 0-X for however many days you display. This class is just responsible for distance from the left. Use percentages if you can so that the layout can be flexible.
time1430 in this case 2:30pm - This class is just responsible for distance from the top
length130 in this case an hour and a half. Ideally set the smallest increment of time (15m?) as a percentage height, then let the mixin multiply the increment for each possible length.
Also to make sure you don't have to factor in borders, put the box-sizing: border-box css rule on pretty much everything.

random default thumbnail for blogger

i'm currently learning html and css. I'm working on a blogger blog template.
My template has a front page of post thumbnails.
I have set a class for my default thumbnail for when my posts do not have an image and I have set the url in the css.
.altthumbnails {
background: url(myimagelocation.jpg);
}
is there any way I can make more than one default thumbnail? I would like to chose maybe three images to show at random when there is no post image.
Thanks for any replies
You can not do it only with pure css. But you can do it either by the server-side code you use or js which the one I suggest. Basicly one of the easiest solution for this is you can make different classes for each different image you wanna use, then you can produce a random number in js and use this number to apply different classes to your elements. Here's an example:
DEMO
In the example there are 5 different css class that supply 5 different background-image and all their names end with numbers 1 >> 5.
.thumb_1 { .. }
.thumb_2 { .. }
.thumb_3 { .. }
.thumb_4 { .. }
.thumb_5 { .. }
In javascript we're gonna loop through the objects, generate a random number 1 >> 5 and use this number to add a new class like this.
var div = document.getElementsByClassName("thumb");
//var rand = Math.floor(Math.random() * 5 ); // for just one image
for (var i = 0; i < div.length; i++){
var rand = Math.floor(Math.random() * 5 ); // for multiple images
div[i].className += " thumb_" + (rand + 1) ;
};