วิธีแบ่งหน้า HTML ขนาด A4 ใน Angular 9
ฉันพยายามสร้างบางสิ่งเช่นใน Xing the CV maker -> https://lebenslauf.com/.
ฉันมี Arrays of Objects ที่แตกต่างกัน แต่ฉันไม่สามารถสร้างหน้า A4 ซึ่งจะแสดงข้อมูลและถ้าอาร์เรย์มีขนาดใหญ่กว่าหนึ่งหน้าให้สร้างหน้า A4 ใหม่และเพิ่มข้อมูลที่นั่น ฟังก์ชันจะต้องเป็นเช่นนั้นหากอาร์เรย์มีขนาดใหญ่ขึ้นสำหรับหนึ่งขนาดให้สร้างหน้าใหม่ a4 และใส่ข้อมูลที่นั่น ใน stackblitz ฉันได้เพิ่มอาร์เรย์และข้อความสุ่มและออกแบบตัวอักษร A4 ฉันอ้างถึงคำถามและคำตอบนี้ แต่ไม่ได้ช่วยอะไรฉันมากนัก CSS เพื่อชุดขนาด
ฉันพยายามปลอมเลขหน้าและสร้างขนาด A4 แต่ไม่ได้ผล
ฉันดูรหัสนี้ด้วย jquery ที่นั่น ใช้งานได้ แต่ฉันไม่สามารถรวบรวมเป็น Angular ได้https://jsfiddle.net/tm637ysp/10/ ใครสามารถช่วยฉันที่นี่?
ฉันได้สร้างสองโปรเจ็กต์ใน stackblitz บางทีพวกเขาอาจจะช่วย
https://stackblitz.com/edit/angular-ivy-fjhpdu.
https://stackblitz.com/edit/angular-ivy-uzmdwg
ฉันต้องการเปิดคำถามนี้อีกครั้งเพราะคำตอบที่ได้รับอนุมัติจาก @HirenParekh มันไม่ได้ผลตามที่ฉันต้องการ ปัญหาคือตอนนี้ถ้าข้อความใน Object หนึ่งมีขนาดใหญ่มากมันจะไม่เพิ่มหน้าใหม่ในแบบเรียลไทม์ แต่ถ้าฉันโหลดหน้าซ้ำ ฉันคิดว่ารหัสจะใช้สำหรับเพิ่มหน้าใหม่หรือหน้าแก้ไขจะแสดงผลในรูปแบบngOnInit. คำสั่งที่ให้มาเพื่อทำงานนั้นผมคิดว่ามันใช้ไม่ได้ยกเว้น นี่คือstackblitzสิ่งที่เขาพยายามช่วยฉันhttps://stackblitz.com/edit/angular-ivy-zjf8rv
นี่คือรหัสที่ฉันพยายามแสดงข้อมูล
<div style="transition: transform 0.25s ease 0s;transform: scale(1.3);transform-origin: 50% 0px 0px;backface-visibility: hidden;perspective: 1000px;display: block;margin: 0px 11.5385%;font-size:10px;width: 76.9231%;-webkit-font-smoothing: antialiased;">
<app-paginated-view [pageSize]="'A4'" *ngIf="model" class="Grid-grid-column">
<div pageContent class="row">
<div class="col col-lg-7">
<h4>{{currentUser?.firstName}} {{currentUser?.lastName}}</h4>
</div>
<div class="col text-right">
<input type="file" accept="image/*" (change)="readUrl($event)"> <img [src]="url" (change)="readUrl($event)" height="128" style="cursor: pointer">
</div>
</div>
<div pageContent class="Unit-unit-unitGroup"
*ngFor="let personalData of model.personalData; let id = index">
<div pageContent [ngClass]="{ 'isCatActive': selectedCategory === category.PersonalData}">
<ng-container *ngIf="selectedCategory === category.PersonalData" clickOutside (clickOutside)="removeClick()">
<ul>
<li class="fa fa-plus addIconTop" (click)="openDialog()"></li>
<li class="fa fa-plus addIconBottom" (click)="openDialog()"></li>
<li class="fa fa-trash deleteIconRight" (click)="deleteCategory(index)"></li>
<li class="fa fa-arrow-down moveIconDown"></li>
<li class="fa fa-arrow-up moveIconTop"></li>
</ul>
</ng-container>
<div pageContent class="col-md-12" (click)="setCategory(category.PersonalData)">
<div class="row height">
<div class="col-md-4 col-sm-6 text-right tLine"></div>
<h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
<div class="Text-text-wrapper">
<div class="Text-Text-text">{{'category.PersonalData' | translate}}</div>
</div>
</h3>
</div>
</div>
<div pageContent class="container-fluid">
<ng-container>
<app-personal-data [personalData]="personalData" [model]="model" [id]="id">
</app-personal-data>
</ng-container>
</div>
</div>
</div>
<!-- Career Component -->
<ng-container *ngFor="let careers of model.careers" class="Unit-unit-unitGroup">
<div pageContent class="col-md-12">
<div class="row height">
<div class="col-md-4 col-sm-6 text-right tLine"></div>
<h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
<div class="Text-text-wrapper">
<div class="Text-Text-text">{{'category.Career' | translate}}</div>
</div>
</h3>
</div>
</div>
<div class="container-fluid" pageContent>
<ng-container *ngFor="let careerObj of careers.subCategories; let i = index">
<app-career [careerObj]="careerObj" [id]="i" [career]="careers" [model]="model"></app-career>
</ng-container>
<ng-container *ngFor="let emptyObj of careers.emptySubContents; let iEmpty = index">
<app-empty-object [emptyObj]="emptyObj" [iEmpty]="iEmpty" [model]="model" [isFromCareer]="true"></app-empty-object>
</ng-container>
</div>
</ng-container>
<!--Education Component-->
<ng-container *ngFor="let education of model.education" class="Unit-unit-unitGroup">
<div pageContent [ngClass]="{ 'isCatActive': selectedCategory === category.Education}">
<ng-container *ngIf="selectedCategory === category.Education" clickOutside (clickOutside)="removeClick()">
<ul>
<li class="fa fa-plus addIconTop" (click)="openDialog()"></li>
<li class="fa fa-plus addIconBottom" (click)="openDialog()"></li>
<li class="fa fa-trash deleteIconRight" (click)="deleteCategory(index)"></li>
<li class="fa fa-arrow-down moveIconDown"></li>
<li class="fa fa-arrow-up moveIconTop"></li>
</ul>
</ng-container>
<div pageContent class="col-md-12" (click)="setCategory(category.Education)">
<div class="row height">
<div class="col-md-4 col-sm-6 text-right tLine"></div>
<h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
<div class="Text-text-wrapper">
<div class="Text-Text-text">{{'category.Education' | translate}}</div>
</div>
</h3>
</div>
</div>
<div pageContent class="container-fluid">
<ng-container *ngFor="let educationObj of education.subCategories; let i = index" class="col-md-12">
<app-education [educationObj]="educationObj" [id]="i" [education]="education" [model]="model"></app-education>
</ng-container>
</div>
</div>
</ng-container>
<!-- Skills Component-->
<ng-container *ngFor="let skills of model.skills" class="Unit-unit-unitGroup">
<div pageContent [ngClass]="{ 'isCatActive': selectedCategory === category.Skills}">
<ng-container clickOutside *ngIf="selectedCategory === category.Skills" (clickOutside)="removeClick()">
<ul>
<li class="fa fa-plus addIconTop" (click)="openDialog()"></li>
<li class="fa fa-plus addIconBottom" (click)="openDialog()"></li>
<li class="fa fa-trash deleteIconRight" (click)="deleteCategory(index)"></li>
<li class="fa fa-arrow-down moveIconDown"></li>
<li class="fa fa-arrow-up moveIconTop"></li>
</ul>
</ng-container>
<div pageContent class="col-md-12" (click)="setCategory(category.Skills)">
<div class="row height">
<div class="col-md-4 col-sm-6 text-right tLine"></div>
<h3 class="first-template-paragraphTitle Paragraph-paragraph-title height">
<div class="Text-text-wrapper">
<div class="Text-Text-text">{{'category.Skills' | translate}}</div>
</div>
</h3>
</div>
</div>
<div pageContent class="container-fluid">
<ng-container *ngFor="let skillObj of skills.subCategories; let i = index" class="col-md-12">
<app-skills [skillObj]="skillObj" [id]="i" [skills]="skills" [model]="model"></app-skills>
</ng-container>
</div>
</div>
</ng-container>
</app-paginated-view>
</div>
นี่คือ CSS
.A4 {
width: 595px;
height: 842px;
padding: 25px 25px;
position: relative;
}
นี้เป็น json
{
"personalData": [
{
"firstName": "Max",
"lastName": "Muster",
"email": "[email protected]",
"birthday": "2020-09-25T00:00:00.000Z",
"telephone": "0123456789",
"job": "Freelancer",
"country": "Germany",
"postalCode": 12345,
"city": "None",
"title": 2,
"gender": 0,
"street": "Musterman 12",
"state": "",
"status": 1,
"showBirthday": true
}
],
"skills": [
{
"subCategories": [
{
"languages": [
{
"name": "languages.de",
"rate": 5
},
{
"name": "languages.al",
"rate": 1
},
{
"name": "languages.en",
"rate": 5
},
{
"name": "languages.fr",
"rate": 4
},
{
"name": "languages.it",
"rate": 4
}
],
"pcKnowledge": [
{
"_id": "5f5ca07e4dba443f786ea7ae",
"name": "Word"
},
{
"_id": "5f5ca07e4dba443f786ea7af",
"name": "Adobe Photoshop"
},
{
"_id": "5f5fd46bb21df2444c39f317",
"name": "Test"
},
{
"_id": "5f5fd46bb21df2444c39f318",
"name": "Excel"
},
{
"_id": "5f5fd46bb21df2444c39f319",
"name": "Ja"
},
{
"_id": "5f72339552009b4244391972",
"name": "Powerpoint"
}
],
"skillsOffer": [
{
"_id": "5f4a4e2d718d33092df2c327",
"name": "Angular"
},
{
"_id": "5f4a4e2d718d33092df2c327",
"name": "Java"
},
{
"_id": "5f4a4e2d718d33092df2c327",
"name": "Typescript"
},
{
"_id": "5f4a4e2d718d33092df2c327",
"name": "html"
},
{
"name": "Javascript"
}
],
"driveLicenses": [
{
"_id": "5f5ca07e4dba443f786ea7ac",
"name": "B"
},
{
"_id": "5f5ca07e4dba443f786ea7ad",
"name": "C"
},
{
"_id": "5f5f204faa5d0205180bd581",
"name": "B"
}
],
"name": "",
"qualifications": ""
}
]
}
]
}
และนี่คือไฟล์ Paginated view class for adding new page and splitting pages
<!-- display: none style will any child that does not have #pageContent local variable defined -->
<div class="content-wrapper" #contentWrapper style="display: block">
</div>
<div class="paginated-view" #paginatedView>
</div>
export class PaginatedViewComponent implements AfterViewInit {
@Input() pageSize: "A3" | "A4" = "A4";
@ViewChild("paginatedView") paginatedView: ElementRef<HTMLDivElement>;
@ViewChild("contentWrapper") contentWrapper: ElementRef<HTMLDivElement>;
@ContentChildren(PageContentDirective, { read: ElementRef })
elements: QueryList<ElementRef>;
constructor(private changeDetector: ChangeDetectorRef ) {}
ngAfterViewInit(): void {
this.updatePages();
// when ever childs updated call the updatePagesfunction
this.elements.changes.subscribe((el) => {
this.updatePages();
});
}
updatePages(): void {
// clear paginated view
this.paginatedView.nativeElement.innerHTML = "";
// get a new page and add it to the paginated view
let page = this.getNewPage();
this.paginatedView.nativeElement.appendChild(page);
let lastEl: HTMLElement;
// add content childrens to the page one by one
this.elements.forEach((elRef) => {
const el = elRef.nativeElement;
// if the content child height is larger than the size of the page
// then do not add it to the page
if (el.clientHeight > page.clientHeight) {
return;
}
// add the child to the page
page.appendChild(el);
// after adding the child if the page scroll hight becomes larger than the page height
// then get a new page and append the child to the new page
if (page.scrollHeight > page.clientHeight) {
page = this.getNewPage();
this.paginatedView.nativeElement.appendChild(page);
page.appendChild(el);
}
lastEl = el;
});
this.changeDetector.detectChanges();
// bring the element in to view port
// lastEl.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
getNewPage(): HTMLDivElement {
const page = document.createElement("div");
page.classList.add("page");
page.classList.add(this.pageSize);
return page;
}
}
@Directive({
// tslint:disable-next-line: directive-selector
selector: "[pageContent]"
})
export class PageContentDirective {
}
คำตอบ
ทั้งหมดนี้เกี่ยวกับการแบ่งเนื้อหาที่กำหนดให้พอดีกับขนาดหน้าให้
เราสามารถสร้างส่วนประกอบที่จะจัดการฟังก์ชันการหารให้เราได้ ที่นี่เป็น StackBlitz สาธิต
และนี่คือคำอธิบายสั้น ๆ
ใช้ContentChildrenมัณฑนากรเพื่อสังเกตการเปลี่ยนแปลงของเนื้อหา ทุกครั้งที่เนื้อหาเปลี่ยนแปลงเราจะเรียกใช้ตรรกะการสร้างเพจ
import {
AfterContentInit,
AfterViewInit,
Component,
ContentChildren,
ElementRef,
Input,
OnInit,
QueryList,
ViewChild
} from "@angular/core";
@Component({
selector: "app-paginated-view",
templateUrl: "paginated-view.component.html",
styleUrls: ["paginated-view.component.scss"]
})
export class PaginatedViewComponent implements AfterViewInit {
@Input() pageSize: "A3" | "A4" = "A4";
@ViewChild("paginatedView") paginatedView: ElementRef<HTMLDivElement>;
@ViewChild("contentWrapper") contentWrapper: ElementRef<HTMLDivElement>;
@ContentChildren("pageContent", { read: ElementRef }) elements: QueryList<
ElementRef
>;
constructor() {}
ngAfterViewInit(): void {
this.updatePages();
// when ever childs updated call the updatePagesfunction
this.elements.changes.subscribe(el => {
this.updatePages();
});
}
updatePages(): void {
// clear paginated view
this.paginatedView.nativeElement.innerHTML = "";
// get a new page and add it to the paginated view
let page = this.getNewPage();
this.paginatedView.nativeElement.appendChild(page);
let lastEl: HTMLElement;
// add content childrens to the page one by one
this.elements.forEach(elRef => {
const el = elRef.nativeElement;
// if the content child height is larger than the size of the page
// then do not add it to the page
if (el.clientHeight > page.clientHeight) {
return;
}
// add the child to the page
page.appendChild(el);
// after adding the child if the page scroll hight becomes larger than the page height
// then get a new page and append the child to the new page
if (page.scrollHeight > page.clientHeight) {
page = this.getNewPage();
this.paginatedView.nativeElement.appendChild(page);
page.appendChild(el);
}
lastEl = el;
});
//bring the element in to view port
lastEl.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
getNewPage(): HTMLDivElement {
const page = document.createElement("div");
page.classList.add("page");
page.classList.add(this.pageSize);
return page;
}
}
เราสามารถใช้ส่วนประกอบนี้ในแอปพลิเคชันเช่นนี้
<app-paginated-view [pageSize]="'A4'">
<h1 #pageContent>Hello World!!</h1>
<p #pageContent>This content will be displayed in an A4 size page</p>
</app-paginated-view>
เราต้องระบุตัวแปรเทมเพลต #pageContent เพื่อให้เราสามารถเลือกโดยใช้ @ContentChildren ใน PaginatedViewComponent ของเรา
โปรดทราบว่าเรากำลังใช้ Native Dom API ที่นี่เพื่อเปลี่ยนโครงสร้าง Dom มันจะย้ายโหนด dom จากที่หนึ่งไปยังอีกที่หนึ่งเท่านั้นดังนั้นหากคุณมีการเพิ่มตัวฟังเหตุการณ์ใด ๆ หรือมีคุณสมบัติใด ๆ ที่เชื่อมโยงกับลูกเนื้อหาพวกเขาจะทำงานตามที่เป็นอยู่
แก้ไข:ฉันได้อัปเดต stackblitz ของคุณแล้วhttps://stackblitz.com/edit/angular-ivy-zjf8rv
<div class="page" *ngFor="let page of pages; index as i"
[style.height]="sizePage.height + 'cm'"
[style.width]="sizePage.width + 'cm'"
(click)="clickPage(i)">
<div class="content"
[style.paddingTop]="paddingPage.top + 'cm'"
[style.paddingRight]="paddingPage.right + 'cm'"
[style.paddingBottom]="paddingPage.bottom + 'cm'"
[style.paddingLeft]="paddingPage.left + 'cm'"
[id]="'content-' + i" contenteditable="true"
(input)="inputContent($event['data'], i)">
</div>
</div>
ในไฟล์ app.component.css:
.page {
background: white;
display: block;
margin: 40px auto;
box-shadow: 0 0 0.5cm rgba(0, 0, 0, 0.5);
box-sizing: border-box;
}
.page .content {
overflow: auto;
outline: 0;
}
ในไฟล์ app.component.ts:
import { AfterViewChecked, Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewChecked {
sizePage = {
width: 21, //cm
height: 29.7 //cm
}
paddingPage = {
top: 2, //cm
right: 2, //cm
bottom: 2, //cm
left: 2 //cm
}
pages = [
{
htmlContent: null,
full: false
},
]
currentPage = 0;
currentChar = null;
runAfterViewChecked = false;
clickPage(i) {
this.currentPage = i;
}
inputContent(char, i) {
var element = document.getElementById('content-' + i)
var heightContent = element.offsetHeight * 2.54 / 96; // Convert pixels to cm
this.pages[i].htmlContent = element.innerHTML;
console.log(this.pages);
if (Number(heightContent.toFixed(1)) > this.sizePage.height) {
this.currentChar = char;
this.pages[i].full = true;
if (!this.pages[i + 1]) {
this.pages.push({
htmlContent: null,
full: false
})
}
this.currentPage = i + 1;
this.runAfterViewChecked = true;
}
}
ngAfterViewChecked() {
document.getElementById('content-' + this.currentPage).focus();
if (this.runAfterViewChecked) {
if (this.currentChar) {
var str = this.pages[this.currentPage-1].htmlContent;
var indexLastCloseDiv = str.lastIndexOf("</div>");
var indexLastBr = str.lastIndexOf("<br>");
var lastChar = str[indexLastCloseDiv-1];
if (indexLastBr != -1 && (indexLastBr + 4) == indexLastCloseDiv)
lastChar = ' ';
if (indexLastCloseDiv != -1)
str = str.slice(0, indexLastCloseDiv-1) + str.slice(indexLastCloseDiv);
else
str = str.slice(0, str.length - 1);
this.pages[this.currentPage-1].htmlContent = str;
if (this.pages[this.currentPage].htmlContent)
this.pages[this.currentPage].htmlContent = lastChar + this.pages[this.currentPage].htmlContent;
else
this.pages[this.currentPage].htmlContent = lastChar;
}
var element = null;
for (let i = 0; i < this.pages.length; i++) {
element = document.getElementById('content-' + i);
element.innerHTML = this.pages[i].htmlContent;
}
this.runAfterViewChecked = false;
}
}
}
ลิงก์ไปยัง Stackblitz
นี่คือตัวอย่างง่ายๆ มีข้อผิดพลาดเล็กน้อยโปรดให้คำแนะนำเพื่อพัฒนาต่อไป
ฟังก์ชันบางอย่างเช่น Backspace, Delete, Scale Page, ... ยังไม่ได้รับการประมวลผล
หากคุณต้องการสร้าง html A4 เช่น office word A4 คุณต้องใช้ขนาดเหล่านี้:
body{
width: 21cm ;
height: 29.7cm;
margin:30mm 45mm 30mm 45mm;}
การบังคับให้เว็บเบราว์เซอร์แสดงหน้าที่มีขนาดพิกเซลเท่ากับ A4 เป็นเรื่องง่าย อย่างไรก็ตามอาจมีนิสัยแปลก ๆ เล็กน้อยเมื่อมีการแสดงผล
สมมติว่าจอภาพของคุณแสดง 72 dpi คุณสามารถเพิ่มสิ่งนี้:
<!DOCTYPE html>
<html>
<head>
<style>
body {
height: 842px;
width: 595px;
/* to centre page on screen*/
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body>
</body>
</html>
นี่คือตัวอย่างในรหัสของคุณที่มีขนาด A4 ที่พิมพ์ได้: https://stackblitz.com/edit/angular-ivy-fjhpdu?embed=1&file=src/app/app.component.html
อันที่จริงฉันไม่เห็น js หรือ css ใด ๆ ที่คุณพยายามแก้ปัญหานี้
แต่ดูจากตัวอย่างที่ให้มาจาก xing พวกเขากำลังใช้ความกว้าง / ความสูงของพิกเซลแบบคงที่
width: 595px;
height: 842px;
สิ่งนี้ตรงกับPPI (พิกเซลต่อนิ้ว) ที่72สำหรับรูปแบบ A4 ที่กำหนดนั่นเป็นสิ่งสำคัญเพียงอย่างเดียวที่ต้องคำนึงถึง
เมื่อรู้อย่างนั้นคุณก็สามารถตรวจสอบได้ว่าสูงเกินขนาดนั้นหรือไม่ในขณะที่แก้ไขและจัดการ DOM ตามนั้น ("สร้างเพจใหม่", "แยก / ย้ายองค์ประกอบหรือส่วนต่างๆ" ฯลฯ ... ) ตอนนี้คุณควรมีทุกอย่างเพื่อแก้ปัญหาด้วยตัวเอง แต่คาดว่าจะมีเวลามากมายในเรื่องนี้โดยเฉพาะอย่างยิ่งเกี่ยวกับการรักษาขนาดตัวอักษร / การเรนเดอร์ / การพิมพ์ที่ตรงกันและไม่ต้องพูดถึงเวอร์ชันมือถือ;)
โชคดี
ในไฟล์ app.component.html:
<div class="container" id="container"></div>
<button type="button" class="buttonAdd" (click)="addBlock()">Add Block</button>
ในไฟล์ app.component.scss:
.buttonAdd {
position: fixed;
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
color: #fff;
background-color: #007bff;
border-color: #007bff;
padding: .25rem .5rem;
font-size: .875rem;
line-height: 1.5;
border-radius: .2rem;
top: 0;
}
.container {
height: 100%;
width: 100%;
}
.page {
// background: white;
display: block;
margin: 40px auto;
box-shadow: 0 0 0.5cm rgba(0, 0, 0, 0.5);
box-sizing: border-box;
.content {
overflow: auto;
outline: 0;
.block {
border: 1px solid rgba(0,0,0,0);
padding: .25rem;
cursor: default;
&:hover {
border: 1px solid #dee2e6;
border-radius: .25rem;
}
.title {
font-weight: bold;
}
.value {
cursor: text;
}
}
}
}
ในไฟล์ app.component.ts:
import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None,
})
export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
sizePage = {
width: 21, //cm
height: 29.7 //cm
}
paddingPage = {
top: 2, //cm
right: 2, //cm
bottom: 2, //cm
left: 2 //cm
}
data = [
{
title: "Name_1",
value: "Thomas K.Wilson"
}, {
title: "Email_1",
value: "[email protected]"
}, {
title: "Telephone_1",
value: "0123 456 789"
}, {
title: "Job_1",
value: "Teacher"
}, {
title: "Name_2",
value: "Thomas K.Wilson"
}, {
title: "Email_2",
value: "[email protected]"
}, {
title: "Telephone_2",
value: "0123 456 789"
}, {
title: "Job_2",
value: "Teacher"
}, {
title: "Name_3",
value: "Thomas K.Wilson"
}, {
title: "Email_3",
value: "[email protected]"
}, {
title: "Telephone_3",
value: "0123 456 789"
}, {
title: "Job_3",
value: "Teacher"
}, {
title: "Name_4",
value: "Thomas K.Wilson"
}, {
title: "Email_4",
value: "[email protected]"
}, {
title: "Telephone_4",
value: "0123 456 789"
}, {
title: "Job_4",
value: "Teacher"
}, {
title: "Name_5",
value: "Thomas K.Wilson"
}, {
title: "Email_5",
value: "[email protected]"
}, {
title: "Telephone_5",
value: "0123 456 789"
}, {
title: "Job_5",
value: "Teacher"
}, {
title: "Name_6",
value: "Thomas K.Wilson"
}, {
title: "Email_6",
value: "[email protected]"
}, {
title: "Telephone_6",
value: "0123 456 789"
}, {
title: "Job_6",
value: "Teacher"
}, {
title: "Name_7",
value: "Thomas K.Wilson"
}, {
title: "Email_7",
value: "[email protected]"
}, {
title: "Telephone_7",
value: "0123 456 789"
}, {
title: "Job_7",
value: "Teacher"
}, {
title: "Name_8",
value: "Thomas K.Wilson"
}, {
title: "Email_8",
value: "[email protected]"
}, {
title: "Telephone_8",
value: "0123 456 789"
}, {
title: "Job_8",
value: "Teacher"
}, {
title: "Name_9",
value: "Thomas K.Wilson"
}, {
title: "Email_9",
value: "[email protected]"
}, {
title: "Telephone_9",
value: "0123 456 789"
}, {
title: "Job_9",
value: "Teacher"
}, {
title: "Name_10",
value: "Thomas K.Wilson"
}, {
title: "Email_10",
value: "[email protected]"
}, {
title: "Telephone_10",
value: "0123 456 789"
}, {
title: "Job_10",
value: "Teacher"
}
]
heightPageWithoutPadding = this.convertCmtoPx(this.sizePage.height - (this.paddingPage.top + this.paddingPage.bottom));
elContainer;
anchorsBlockValue;
pageContent = [[]]; // Ex: [[0, 1, 2, 3], [4, 5]]
constructor (private elementRef: ElementRef){
}
ngOnInit() {
}
ngAfterViewInit() {
this.elContainer = document.getElementById('container');
this.elContainer.innerHTML += this.createHTMLPage(0);
this.insertListData();
this.anchorsBlockValue = this.elementRef.nativeElement.querySelectorAll('.block .value');
this.anchorsBlockValue.forEach((anchor: HTMLAnchorElement) => {
anchor.addEventListener('input', this.handleAnchorBlockValue)
});
}
insertListData() {
var html_ListBlock = "";
var html_Block = "";
var iPage = 0;
var iBlock = 0;
var elPageContent = document.getElementById('page-' + iPage + '-content');
for (let i = 0; i < this.data.length; i++) {
html_Block = this.createHTMLBlock(iPage, iBlock, this.data[i]);
elPageContent.innerHTML = html_ListBlock + html_Block;
if (elPageContent.offsetHeight > this.heightPageWithoutPadding) {
elPageContent.innerHTML = html_ListBlock;
iPage += 1;
this.elContainer.innerHTML += this.createHTMLPage(iPage);
elPageContent = document.getElementById('page-' + iPage + '-content');
this.pageContent[iPage] = [];
this.pageContent[iPage].push(iBlock);
html_Block = this.createHTMLBlock(iPage, iBlock, this.data[i]);
html_ListBlock = html_Block;
elPageContent.innerHTML = html_ListBlock;
} else {
this.pageContent[iPage].push(iBlock);
html_ListBlock += html_Block;
elPageContent.innerHTML = html_ListBlock;
}
iBlock += 1;
}
// nodes += `<button type="button" class="buttonAdd" (click)="addBlock()">Add Block</button>`;
}
handleAnchorBlockValue = (event: Event) => {
// Prevent opening anchors the default way
event.preventDefault();
const anchor = event.target as HTMLAnchorElement;
const id_anchorParentEl = anchor.parentElement.getAttribute('id'); // page-iPage-content-block-iBlock
var iPage = Number(id_anchorParentEl.slice(id_anchorParentEl.indexOf("page-") + ("page-").length, id_anchorParentEl.indexOf("-content")));
var iBlock = Number(id_anchorParentEl.slice(id_anchorParentEl.indexOf("block-") + ("block-").length, id_anchorParentEl.length));
var elPageContent = anchor.parentElement.parentElement;
if (elPageContent.offsetHeight > this.heightPageWithoutPadding) {
if (!this.pageContent[iPage + 1]) {
this.elContainer.innerHTML += this.createHTMLPage(iPage + 1);
this.pageContent[iPage + 1] = [];
}
if (this.pageContent[iPage].length == 1) {
// This is (Height Block == Height Content) > Height Page
alert("To be continue ...");
} else {
while (iPage < this.pageContent.length) {
var elPageContent = document.getElementById('page-' + iPage + '-content');
var iLastBlock_PageContent = this.pageContent[iPage][this.pageContent[iPage].length - 1];
var elLastBlock_PageContent = document.getElementById('page-' + iPage + '-content-block-' + iLastBlock_PageContent);
elLastBlock_PageContent.remove();
this.pageContent[iPage].pop();
if (!this.pageContent[iPage + 1]) {
this.elContainer.innerHTML += this.createHTMLPage(iPage + 1);
this.pageContent[iPage + 1] = [];
}
elLastBlock_PageContent.setAttribute('id', 'page-' + (iPage + 1) + '-content-block-' + iLastBlock_PageContent);
var elNextPageContent = document.getElementById('page-' + (iPage + 1) + '-content');
elNextPageContent.innerHTML = elLastBlock_PageContent.outerHTML + elNextPageContent.innerHTML;
this.pageContent[iPage + 1].unshift(iLastBlock_PageContent);
if (elPageContent.offsetHeight <= this.heightPageWithoutPadding) {
if (elNextPageContent.offsetHeight <= this.heightPageWithoutPadding) {
break;
} else {
iPage += 1;
}
}
}
}
this.anchorsBlockValue = this.elementRef.nativeElement.querySelectorAll('.block .value');
this.anchorsBlockValue.forEach((anchor: HTMLAnchorElement) => {
anchor.addEventListener('input', this.handleAnchorBlockValue)
});
}
}
addBlock() {
alert("To be continue ...");
}
convertPxToCm(px) {
return Math.round(px * 2.54/96 * 100) / 100;
}
convertCmtoPx(cm) {
return Math.round(cm * 96/2.54);
}
createHTMLBlock(iPage, iBlock, data) {
return `<div class="block" id="page-${iPage}-content-block-${iBlock}">
<div class="title">${data.title}</div> <div class="value" contenteditable>${data.value}</div>
</div>`;
}
createHTMLPage(iPage) {
return `<div class="page" id="page-${iPage}" style=" height: ${this.sizePage.height}cm;
width: ${this.sizePage.width}cm; padding-top: ${this.paddingPage.top}cm;
padding-right: ${this.paddingPage.right}cm; padding-bottom: ${this.paddingPage.bottom}cm;
padding-left: ${this.paddingPage.left}cm; "> <div class="content" id="page-${iPage}-content">
</div>
</div>`;
}
ngOnDestroy() {
// Cleanup by removing the event listeners on destroy
this.anchorsBlockValue.forEach((anchor: HTMLAnchorElement) => {
anchor.removeEventListener('input', this.handleAnchorBlockValue)
})
}
}
ในไฟล์ tsconfig.json:
เพิ่ม
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true
หน้าตาเป็นแบบนี้
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
],
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true
},
"angularCompilerOptions": {
"enableIvy": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}
ลิงก์ไปยัง Stackblitz
ฉันทำอย่างนั้นหลังจากดูหน้า CV ของ Xing
การออกแบบบล็อกและชนิดข้อมูลนี้ทำให้ง่ายต่อการแยกคอลัมน์สำหรับเพจและเพิ่มเนื้อหาที่มีส่วนหัวตามอำเภอใจ
คุณต้องแปลงข้อมูลของคุณเป็นประเภทข้อมูลของฉันโดยเพิ่มฟังก์ชัน dataConvert ใน ngOnInit ()
เนื่องจากคุณกำลังใช้ชนิดข้อมูลข้อความ หากมีความหลากหลายมากกว่าประเภทข้อมูลจะเป็น:
data = [
{
type: "Text",
title: "Full Name",
value: "Thomas K.Wilson"
}, {
type: "Image",
title: "My Avatar",
value: "linkImage.com"
}
]
หากคุณคิดว่าคุณสามารถทำได้ด้วย jquery ไม่มีข้อ จำกัด ในการใช้ jquery ในเชิงมุม
คุณจะต้องเพิ่มแพ็คเกจต่อไปนี้
jQuery (สำหรับ jquery ofcourse)https://www.npmjs.com/package/jquery
@ typescript / jquery (สำหรับการสนับสนุนใน typescript)https://www.npmjs.com/package/@types/jquery
ต่อไปนี้เป็นวิธีใช้ในไฟล์ ts ของคุณ (ตัวอย่างเช่น)
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
declare var $: any; @Component({ selector: 'app-a4', templateUrl: './a4.component.html', styleUrls: ['./a4.component.css'] }) export class A4Component implements OnInit { max_pages = 5; page_count = 0; constructor() { } ngOnInit(): void { this.snipMe(); } snipMe() { this.page_count++; if (this.page_count > this.max_pages) { return; } var long = $(this)[0].scrollHeight - Math.ceil($(this).innerHeight()); var children = $(this).children().toArray();
var removed = [];
while (long > 0 && children.length > 0) {
var child = children.pop();
$(child).detach(); removed.unshift(child); long = $(this)[0].scrollHeight - Math.ceil($(this).innerHeight()); } if (removed.length > 0) { var a4 = $('<div class="A4"></div>');
a4.append(removed);
$(this).after(a4);
this.snipMe.call(a4[0]);
}
}
}