如何在 Angular 9 中将 HTML 页面拆分为 A4 大小

Posted

技术标签:

【中文标题】如何在 Angular 9 中将 HTML 页面拆分为 A4 大小【英文标题】:How to split HTML Page in A4 size in Angular 9 【发布时间】:2021-01-15 21:14:43 【问题描述】:

我正在尝试在 Xing the CV maker 中创建类似的东西 -> https://lebenslauf.com/。

我确实有不同的对象数组。 但我无法创建将呈现数据的 A4 页面,如果数组大于一页,则创建新页面 A4 并在那里添加数据。 如果数组对于一个大小来说更大,则该函数需要如此,然后创建一个新页面 a4 并将数据放在那里。 在 stackblitz 中,我添加了一个数组和一些随机文本,并设计了一个 A4 字母。 我提到了这个问题和答案,但对我没有太大帮助。 CSS to set A4 paper size. 我试图伪造分页并创建 A4 尺寸,但没有成功。 我在那里用jquery查看了这段代码。它可以工作,但我无法在 Angular 中编译它。 https://jsfiddle.net/tm637ysp/10/ 有人可以帮我吗?

我在 stackblitz 中创建了两个项目。也许他们会提供帮助。https://stackblitz.com/edit/angular-ivy-fjhpdu.https://stackblitz.com/edit/angular-ivy-uzmdwg

我想重新提出这个问题,因为来自@HirenParekh 的批准答案没有按我的意愿工作。 现在的问题是,如果一个对象中的文本非常大,它不会实时添加新页面,但前提是我重新加载页面。 我认为代码将用于添加新页面或编辑页面将仅在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)"  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": "max.musterman@outlook.com",
            "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 


【问题讨论】:

我 2 小时前刚看了你的问题。我可以要求更多时间来完成我的答案吗? @VươngHữuThiện 没问题,您将有足够的时间。 【参考方案1】:

实际上我看不到您尝试解决问题的任何 js 或 css。

但是查看 xing 提供的示例,他们使用的是静态像素宽度/高度

width: 595px;
height: 842px;

这与给定格式 A4 的 PPI(每英寸像素)72 匹配 这是唯一需要记住的重要事项。

知道了这一点,您可以在编辑和相应地操作 DOM 时检查是否超出该高度(“创建新页面”、“拆分/移动元素或部件”等......)您现在应该拥有一切自己解决。但是希望有很多时间来解决这个问题,尤其是在保持字体大小/渲染/打印环匹配方面,而不是谈论移动版本;)

祝你好运

【讨论】:

感谢您的时间和想法。从你的回答中我明白了很多事情。 我创建了另一个堆栈闪电战,我认为这会更有帮助。stackblitz.com/edit/angular-ivy-uzmdwg【参考方案2】:

强制 Web 浏览器显示与 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

【讨论】:

感谢您的回答,但我认为这对我没有太大帮助。 每次您都可以使用您的数据创建一个 pdf 并将其显示在带有分页的 HTML 文档中。我认为这将解决问题! 我也这么想,但不知道从哪里开始。 有用的 javascript 库值得 pdfmake。 pdfmake 操场 --> pdfmake.org/playground.html github 上的 pdfmake --> github.com/bpampuch/pdfmake【参考方案3】:

如果你想创建像 office word A4 这样的 html A4,你必须使用这些尺寸:

body
 width: 21cm ;
 height: 29.7cm;
 margin:30mm 45mm 30mm 45mm;

【讨论】:

你是对的,但我的问题更多的是如何根据我拥有的对象创建 A4 页面。【参考方案4】:

如果你认为你可以用 jquery 做到这一点,那么在 angular 中使用 jquery 是没有限制的

您需要添加以下软件包

jQuery(当然适用于 jquery)https://www.npmjs.com/package/jquery

@types/jquery(用于支持打字稿)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]);
    
  

【讨论】:

问题是innerHeight未定义。 尝试调试该对象。看看你怎样才能长高。【参考方案5】:

在 app.component.html 文件中:

<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;
    
  

Link to Stackblitz

这是一个简单的例子。 有一些错误,请提出您的建议以供进一步发展。

Backspace、Delete、Scale Page、...等一些功能尚未处理。

【讨论】:

@vuong 感谢您抽出宝贵时间,我尝试了 stackblitz 并且它有效,但我确实遇到了问题。相反,输入我需要用 ngFor 显示我的数据。这怎么可能? 我的输入格式会有不同的对象数组。看到这个 stackblitz,stackblitz.com/edit/angular-ivy-kk44fj 我有我的DataModel,我有一些 json 文件作为我的真实数据的例子。 这不是很困难。但这需要一些时间。目前我在办公时间。我会尽快将演示发送给您。 没问题,非常感谢。我正在尝试修复它。 我是为你做的。我使用了一些高级 Angular 技术,所以出现了一些错误。我正在修复它。【参考方案6】:

在 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: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_1",
      value: "0123 456 789"
    , 
      title: "Job_1",
      value: "Teacher"
    , 
      title: "Name_2",
      value: "Thomas K.Wilson"
    , 
      title: "Email_2",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_2",
      value: "0123 456 789"
    , 
      title: "Job_2",
      value: "Teacher"
    , 
      title: "Name_3",
      value: "Thomas K.Wilson"
    , 
      title: "Email_3",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_3",
      value: "0123 456 789"
    , 
      title: "Job_3",
      value: "Teacher"
    , 
      title: "Name_4",
      value: "Thomas K.Wilson"
    , 
      title: "Email_4",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_4",
      value: "0123 456 789"
    , 
      title: "Job_4",
      value: "Teacher"
    , 
      title: "Name_5",
      value: "Thomas K.Wilson"
    , 
      title: "Email_5",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_5",
      value: "0123 456 789"
    , 
      title: "Job_5",
      value: "Teacher"
    , 
      title: "Name_6",
      value: "Thomas K.Wilson"
    , 
      title: "Email_6",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_6",
      value: "0123 456 789"
    , 
      title: "Job_6",
      value: "Teacher"
    , 
      title: "Name_7",
      value: "Thomas K.Wilson"
    , 
      title: "Email_7",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_7",
      value: "0123 456 789"
    , 
      title: "Job_7",
      value: "Teacher"
    , 
      title: "Name_8",
      value: "Thomas K.Wilson"
    , 
      title: "Email_8",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_8",
      value: "0123 456 789"
    , 
      title: "Job_8",
      value: "Teacher"
    , 
      title: "Name_9",
      value: "Thomas K.Wilson"
    , 
      title: "Email_9",
      value: "thomas.k.wilson@gmail.com"
    , 
      title: "Telephone_9",
      value: "0123 456 789"
    , 
      title: "Job_9",
      value: "Teacher"
    , 
      title: "Name_10",
      value: "Thomas K.Wilson"
    , 
      title: "Email_10",
      value: "thomas.k.wilson@gmail.com"
    , 
      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.heightcm;
                width: $this.sizePage.widthcm;
                padding-top: $this.paddingPage.topcm;
                padding-right: $this.paddingPage.rightcm;
                padding-bottom: $this.paddingPage.bottomcm;
                padding-left: $this.paddingPage.leftcm;
              ">
              <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
  

Link to Stackblitz

我是在查看 Xing 的简历页面后这样做的。

这种块和数据类型的设计使分隔页面的列和添加带有任意标题的内容变得容易。

你必须通过在ngOnInit()中添加dataConvert函数将你的数据转换成我的数据类型

因为您使用的是文本数据类型。如果它更多样化,那么数据类型将是:

data = [
    
      type: "Text",
      title: "Full Name",
      value: "Thomas K.Wilson"
    , 
      type: "Image",
      title: "My Avatar",
      value: "linkImage.com"
    
]

【讨论】:

以及我用*ngFor 显示数据的组件我是否需要它们?正如你在这里看到的。 stackblitz.com/edit/angular-ivy-fjhpdu 我将数据显示到父组件中,我添加了这样的内容,因为每个组件都有自己的设计和功能。但我会尝试,我会写信给你,非常感谢。 在使用*ngFor时,我们会使用ngValue,当数据内容改变时会导致页面重新加载。 我是在查看 Xing 的简历页面后这样做的。这种块和数据类型的设计使分隔页面的列和添加具有任意标题的内容变得容易。 我会试试然后回复你非常感谢你。 我创建了许多不同类型的字符串块。当您单击“添加块”按钮时,您然后选择块类型将如何将该类型的内部 HTML 添加到内容。【参考方案7】:

这就是将给定的内容划分为适合给定的页面大小。

我们可以创建一个组件来为我们处理分割功能。这是 StackBlitz Demo。

这里有一个简短的解释。

使用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,以便我们可以在PaginatedViewComponent 中使用@ContentChildren 来选择它们。

请注意,我们在这里使用原生 dom API 来更改 dom 结构。它只会将 dom 节点从一个地方移动到另一个地方,因此如果您添加了任何事件侦听器或将任何属性绑定到内容子项,它们将按原样工作。

编辑: 我还更新了你的 stackblitz https://stackblitz.com/edit/angular-ivy-zjf8rv

【讨论】:

您将如何处理我的json 文件,请参阅我的json 的stacklbitz。仅当您单击按钮保存数据时,您才创建了另一件事,但我从json 获取数据。 @Abedin.Zhuniqi 无论内容来自何处,此组件都会将其子组件显示到 a4 大小的页面中。您可以呈现&lt;app-paginated-view&gt;&lt;/app-paginated-view&gt; 之间的任何内容,组件会将内容拆分为多个 a4 大小的页面。 我需要写入&lt;app-paginated-view&gt; 组件或者我的ngFor 哪里? 在你的问题中你写了&lt;div class="A4"&gt; 你可以用 替换那个 div 你的代码在&lt;app-paginated-view>那里写到我的component ?

以上是关于如何在 Angular 9 中将 HTML 页面拆分为 A4 大小的主要内容,如果未能解决你的问题,请参考以下文章

通过 Angular js 在 index.html 页面中将一个页面导航到另一个页面

如何在JavaScript中将数组数组拆分为单个数字?

如何在 Angular 高阶组件 (HOC) 中将 html 元素作为子元素传递?

我如何在 Angular js 中将 64 位 HTML 文件显示到 iframe

在javascript中将数字转换为数组| angular7的打字稿[重复]

在 Angular 2 中将时间 hh:mm:ss 转换为 GMT 时间格式