基于 id 的一个组件中的多个角度模板(带模板存储)

Bil*_*ell 5 ide programming-languages development-environment angular

我有一个特殊的项目,但我无法找到有关如何实现这一目标的任何信息。

所以在这个网站上公司可以注册和登录。当公司登录时,他们可以看到设备和组的概览,其中设备可以分为不同的组以便于识别。现在网站的难点在于模板管理。每个设备将显示一个模板,该模板可以是通用指定模板、分配给特定组或单个设备的模板。选择的模板是标准提供的模板,公司制作的自定义模板或我定制的自定义模板。(最后 2 个选项仅对公司本身可见)

这样做的主要原因是显示不同的模板,我的意思是结构差异,如表格、卡片甚至自定义结构。

所以目前我可以根据公司的 id 显示模板。这些模板集成在 angular 应用程序中。所以现在它看起来像这样(这只是一个小例子):

    this.companyName = this.route.snapshot.params['company'];
    if(this.companyName == "Google"){
        this.template = `<div [ngStyle]="{'border-left':(tr.state=='busy')?'10px solid #D4061C':'10px solid #2CC52E'}">{{data}}</div>`;
        this.styles = "div{color: red}";
    }
Run Code Online (Sandbox Code Playgroud)

之后发生的是通过将编译器保留在构建中来动态创建组件。所以这意味着这个项目不能在生产模式下构建,因为需要编译器。这意味着部署项目很糟糕,因为代码在浏览器中是可见的,而且大小要大得多,因此加载所有内容需要花费太多时间。我有点想摆脱这种方法并使用其他更容易的方法用

所以我想知道的是:

  • 是否可以从数据库中的数据或 HTML 文件中加载 html。
  • 是否可以通过将任何其他库与 Angular 一起使用来实现。
  • 有没有办法创建我提供给公司的模板概览,并显示该模板的预览?
  • 有没有办法检索显示模板的设备的 ip 和 mac 地址。

如果不能为此使用 Angular,您建议使用什么环境,如语言、框架等?

如果需要更多信息,请随时询问!

提前致谢!

编辑1:

我曾尝试使用 [innerHTML] 加载模板,但这在数据绑定或数据插值字符串中无法正常工作。

我会给你一个我想加载的 HTML 的例子:

    <div class='exellys' style='width: 1080px ;height: 1920px;background-color: #212121;'>
        <div class='clr-row' style='padding:45px 0px 10px 25px; position: relative; width: inherit; height: 115px;'>
            <div class='clr-col-5' style='float: left;'>
                <div style='width: 230px; height: 60px; background: url(/assets/exellys/exellys.png); background: url(https://www.exellys.com/App_SkinMaster/images/sprite-new.svg), linear-gradient(transparent, transparent); background-repeat: no-repeat; float: left;'></div>
            </div>
            <div class='clr-col-7' style='padding-top: 10px; float: right;'>
                <div class='exellys medium' style='text-align: right;'>{{date | date: 'EEEE d MMMM y'}}</div>
            </div>
        </div>
        <div class='clr-row' style='position: relative; width: inherit;'>
            <div class='exellys medium' style='width: inherit;padding-right:10px; text-align: right;'>{{date | date: 'HH:mm'}}</div>
        </div>
        <div class='clr-row' style='position: relative; width: inherit;'>
            <div class='exellys large' style='padding-top: 150px; width: inherit; text-align: center; font-weight: bold;'>WELCOME TO EXELLYS</div>
        </div>
        <div class='clr-row' style='position: relative; width: inherit;'>
            <div class='exellys medium-large' style='padding-top: 75px; width: inherit; text-align: center;'>Training Schedule</div>
        </div>
        <div class='clr-row' style='position: relative; width: inherit;'>
            <table class='table table-noborder exellys' style='table-layout: fixed; padding: 100px 20px 0px 35px;'>
                <thead>
                    <tr>
                        <th class='left exellys hcell' style='font-weight: bold; font-size: 37px; width: 15%; padding-left: 0px;'>HOUR</th>
                        <th class='left exellys hcell' style='font-weight: bold; font-size: 37px; width: 40%;'>ROOM</th>
                        <th class='left exellys hcell' style='font-weight: bold; font-size: 37px;'>SUBJECT</th>
                    </tr>
                </thead>
            </table>
            <table class='table table-noborder exellys' style='table-layout: fixed; border-collapse: separate; border-spacing: 0 5px; padding: 0px 20px 0px 35px; margin-top:0px;'>
                <tbody style='padding-left: 0px;'>
                    <tr *ngFor='let tr of bookings'>
                        <td class='left exellys dcell' style='font-size: 37px; padding-left: 10px; width: 15%;' [ngStyle]="{'border-left': (tr.state=='busy')? '10px solid #D4061C' : '10px solid #2CC52E'}">{{tr.localeStart | date: 'HH:mm'}}</td>
                        <td class='left exellys dcell' style='font-size: 37px; width: 40%;' [innerHTML]="tr.scheduleLocation"></td>
                        <td class='left exellys dcell' style='font-size: 37px;'>{{tr.subject}}</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
Run Code Online (Sandbox Code Playgroud)

在此 HTML 旁边,我还加载了以下样式:

    .main {
        color: #b0943c;
        font-family: 'Omnes-Medium', Helvetica, sans-serif;
        width: 1080px;
        height: 1920px;
        background-color: #212121;
    }
    
    .exellys {
        color: #b0943c;
    }
    
    .exellys.medium {
        font-size: 43px;
        font-family: 'Omnes-Regular', Helvetica, sans-serif;
    }
    
    .exellys.medium-large {
        font-size: 55px;
    }
    
    .exellys.large {
        font-family: 'Refrigerator-Deluxe-Regular', Helvetica, sans-serif;
        font-size: 75px;
    }
    
    .exellys.dcell {
        line-height: 45px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        padding-left: 0px;
    }
    
    .exellys.hcell {
        padding: 0px 0px 20px 0px;
    }
    
    table.table.table-noborder th {
        border-bottom: 5px solid #996633;
    }
    
    table td {
        border-top: 2px dashed #996633;
    }
Run Code Online (Sandbox Code Playgroud)

由于 XSS 保护,输入这种模板很容易产生问题,尤其是在 innerHTML 中。所以我想知道是否有不同的解决方案,因为可能有数百个客户拥有数百个不同的模板。

模板的外观示例: 模板示例

编辑2:

我的意思是:

是否可以通过将任何其他库与 Angular 一起使用来实现。

是如果使用标准方法无法实现这一点,是否有任何库可以使我无论如何都能实现这一目标。

编辑 3:

所以模板建议系统的想法真的很好,但是客户想要创建它并直接添加它,而没有其他客户看到这个。

通过这种方式,我需要能够在后端保存 HTML 文件(无论是模板还是完整页面都无关紧要)并将其加载到 angular 应用程序中。

因为据我了解下面的所有答案,这在 Angular 中是不可能的。

我现在的问题是在哪种环境或语言中可以实现这种模板机制?或者还有一种未知的方法可以安全地用于 Angular 的生产?

提前致谢!

2020 年 12 月 15 日更新:

在实施 Owen Kelvins 的想法后,我发现了一些问题。使用 ngFor 循环遍历数据不起作用。在插值字符串内添加管道也不起作用。

要解决管道问题,您可以通过更改 prev.toString() 行来解决此问题:

    templateString$ = combineLatest([this.templatingInfo$, this.template$]).pipe(
        map(([info, template]) =>
            Object.entries(info).reduce((prev, next) => {
                var value = next[1].toString();
                var pipe = "";
                var pipevalue = "";
                var match = prev.toString().match(new RegExp(`{{\\s${next[0]}\\s(\\|\\s\\w*\\:\\s\\'\.*\\'\\s)+}}`));
                if (match != null) {
                    pipe = match[1].substring(2);
                    if (pipe.split(":")[0] == "date") {
                        pipevalue = pipe.substr(5).replace(/['"]/g, "");
                        value = formatDate(value, pipevalue, this.locale);
                        return prev.toString().replace(new RegExp(`{{\\s${next[0]}\\s(\\|\\s\\w*\\:\\s\\'\.*\\'\\s)+}}`), formatDate(next[1].toString(), pipe.substring(5).replace(/['"]+/g, ""), this.locale));
                    }
                }
                return prev.toString().replace(new RegExp(`{{\\s${next[0]}\\s}}`), next[1].toString());
            }, template)
        ),
        map(template => this._sanitizer.bypassSecurityTrustHtml(template))
    );
Run Code Online (Sandbox Code Playgroud)

当然,这种方法并不能完全奏效,因为在某些情况下它仍然无法正确显示。就像你有: 一样<div>{{date | date: 'EEEE d MMMM y' }} - {{date | date: 'HH:mm' }}</div>,在这种情况下,只有第一个是正确的。

我想知道如何将 ngFor 循环修复为管道问题。

提前致谢!

Owe*_*vin 1

我相信最简单的解决方案是绑定到 [innerHTML],正如 @capc0 之前提到的

您提出了以下担忧

嗨@capc0,你的答案是完全正确的。但是,是的,有一个但是!我在 html 中使用插值字符串,innerHTML 工作正常,但这是静态 HTML。我说的是具有数据插值字符串的 HTML,该字符串不能与 innerHTML 正常工作

考虑以下方法来处理这个问题

假设我们要从下面的对象中进行title插值cost

  templatingInfo$ = of({
  title: 'Template Title',
    cost: 200
  });
Run Code Online (Sandbox Code Playgroud)

我还将假设模板以以下形式收到:Observable

  templates$ = of([
    { 
      id: 1,
      name: 'Alpha',
      value: `
        <div class='red'> 
          <h1>{{ title }}</h1>
          <p> This is my lovely Template! Purchase it at \${{ cost }} </p>
        </div>
      `
    },
    { 
      id: 2,
      name: 'Beta',
      value: `
        <div class='blue'> 
          <h1>{{ title }}</h1>
          <p> This is my lovely Template! Purchase it at \${{ cost }} </p>
        </div>
      `
    },
   ...

Run Code Online (Sandbox Code Playgroud)

现在唯一的挑战是用正确的信息替换插值部分

我将用下面的方法解决这个问题

定义变量来跟踪所选模板

  selected = 1;
  selectedTemplateSubject$ = new BehaviorSubject(this.selected);
  selectedTemplate$ = this.selectedTemplateSubject$.asObservable();
Run Code Online (Sandbox Code Playgroud)

用于combineLatest将变量与模板结合起来

  template$ = combineLatest([this.templates$, this.selectedTemplate$]).pipe(
    map(([templates, selected]) => templates.find(({id}) => id == Number(selected)).value),
    )
  templateString$ = combineLatest([this.templatingInfo$, this.template$ ]).pipe(
    map(([info, template]) => 
      Object.entries(info).reduce((prev, next) => 
        prev.toString().replace(new RegExp(`{{\\s${next[0]}\\s}}`), next[1].toString())
            , template)
        ),
    )
Run Code Online (Sandbox Code Playgroud)

不幸的是,上述作品的样式将不会被应用。

选项 1 我们可以 encapsulation: ViewEncapsulation.None,在我们的@Component({})对象中使用它,请参阅Angular 2 - insideHTML 样式

注意:我们实际上正在停用针对 XSS 攻击的 Angular 保护

综上所述,您现在有几个选择

  • 在将模板字符串保存到数据库之前对其进行清理
  • 在渲染模板字符串之前手动清理它
  • 仅使模板可供发布该模板的个人用户使用。这样他们可能只会攻击自己:)

请参阅此示例

选项 2 另一种选择是在这篇文章DomSanitizer中用作解释者

假设用户已包含如下内联样式

  templates$ = of([
    {
      id: 1,
      name: "Alpha",
      value: `
        <div> 
          <h1 style='color: red'>{{ title }}</h1>
          <p style='color: blue'> This is Alpha! Purchase it at \${{ cost }} </p>
        </div>
      `
    },
    {
      id: 2,
      name: "Beta",
      value: `
        <div> 
          <h1 style='color: brown'>{{ title }}</h1>
          <p style='color: green'> This is Alpha! Purchase it at \${{ cost }} </p>
        </div>
      `
    },
    ...
Run Code Online (Sandbox Code Playgroud)

我们可以添加该行map(template => this._sanitizer.bypassSecurityTrustHtml(template))以将结果字符串映射到可信字符串。代码看起来像

import { Component } from "@angular/core";
import { of, BehaviorSubject, combineLatest } from "rxjs";
import { map } from "rxjs/operators";

import { DomSanitizer } from "@angular/platform-browser";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  constructor(private _sanitizer: DomSanitizer) {}
  templatingInfo$ = of({
    title: "Template Title",
    cost: 200
  });
  selected = 1;

  selectedTemplateSubject$ = new BehaviorSubject(this.selected);
  selectedTemplate$ = this.selectedTemplateSubject$.asObservable();
  templates$ = of([
    {
      id: 1,
      name: "Alpha",
      value: `
        <div> 
          <h1 style='color: red'>{{ title }}</h1>
          <p style='color: blue'> This is Alpha! Purchase it at \${{ cost }} </p>
        </div>
      `
    },
    {
      id: 2,
      name: "Beta",
      value: `
        <div> 
          <h1 style='color: brown'>{{ title }}</h1>
          <p style='color: green'> This is Alpha! Purchase it at \${{ cost }} </p>
        </div>
      `
    },
    {
      id: 3,
      name: "Gamma",
      value: `
        <div> 
          <h1 style='color: darkred'>{{ title }}</h1>
          <p style='color: green'> This is Alpha! Purchase it at \${{ cost }} </p>
        </div>
      `
    }
  ]);

  template$ = combineLatest([this.templates$, this.selectedTemplate$]).pipe(
    map(
      ([templates, selected]) =>
        templates.find(({ id }) => id == Number(selected)).value
    )
  );
  templateString$ = combineLatest([this.templatingInfo$, this.template$]).pipe(
    map(([info, template]) =>
      Object.entries(info).reduce(
        (prev, next) =>
          prev
            .toString()
            .replace(new RegExp(`{{\\s${next[0]}\\s}}`), next[1].toString()),
        template
      )
    ),
    map(template => this._sanitizer.bypassSecurityTrustHtml(template))
  );
}

Run Code Online (Sandbox Code Playgroud)

请参阅下面 Stackblitz 上的演示