一个组件中基于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}";
}

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

所以我想知道的是:

  • 是否可以从数据库中的数据或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>

在此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',sans-serif;
}

.exellys.medium-large {
    font-size: 55px;
}

.exellys.large {
    font-family: 'Refrigerator-Deluxe-Regular',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;
}

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

一个模板的示例:

Template example

编辑2:

我的意思是

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

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

编辑3:

因此,模板建议系统的想法确实不错,但是客户希望创建它并直接添加它,而没有其他客户看到它。

这样,我需要能够将HTML文件保存在后端(无论是模板还是整页都没有关系)并将其加载到有角度的应用程序内部。

据我所知,以下所有答案在Angular中都是不可能的。

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

谢谢!

解决方法

您应该加载不同的组件,而不是不同的模板。 (仍然可以对一个组件应用不同的模板,但是这样做很难,并且会使应用程序的性能变差,并且也很难维护。如果仍然需要此选项,请寻找动态编译​​)>

例如,您可以将一组组件注册为一些令牌,然后显示它们

{
 provide: COMPONENTS_OF_CHOICE,multi: true,useValue: OneOfMyComponents
}

{
 provide: COMPONENTS_OF_CHOICE,useValue: [OneOfMyComponents,SecondOfMyComponents]
}

无法检索设备的ip和mac地址。这将是不安全的,并且浏览器不会公开该数据

,

我相信最简单的解决方案将是绑定到[innerHTML],如先前@ capc0所述

您提出的担忧不足

@ capc0,您的回答完全正确。但是,是的,但是!我在html内使用插值字符串,innerHTML可以正常工作,但这是静态HTML。我说的是HTML的数据插值字符串不能与innerHTML一起正常工作

考虑以下处理此问题的方法

让我们说我们要从下面的对象中插值titlecost

  templatingInfo$ = of({
  title: 'Template Title',cost: 200
  });

我还将假设模板以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>
      `
    },...

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

我将通过以下方法解决此问题

定义变量以跟踪所选模板

  selected = 1;
  selectedTemplateSubject$ = new BehaviorSubject(this.selected);
  selectedTemplate$ = this.selectedTemplateSubject$.asObservable();

使用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)
        ),)

不幸的是,以上方法无法应用样式。

选项1 这样我们可以使用 encapsulation: ViewEncapsulation.None,对象中的@Component({})参见Angular 2 - innerHTML styling

注意:我们实际上是在停用XSS攻击的角保护

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

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

See this Sample

选项2 另一种选择是在This Post

中使用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>
      `
    },...

我们可以添加行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,{
      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}}`),template
      )
    ),map(template => this._sanitizer.bypassSecurityTrustHtml(template))
  );
}

请参见Below Demo on Stackblitz

,

是否可以从数据库中的数据或 来自HTML文件。

是的。例如,您可以创建“模板编辑器”,客户可以在其中创建模板并将该视图存储在数据库中。这不是很简单,但是可能。您可以从数据库中提取HTML并显示,例如通过<div [innerHTML]="data"></div>。但是,由于注入安全性风险(xss),您必须清理用户输入等。如果定义一组“构建块”,公司可以将其中的多个块组合成一个模板,然后动态构建该UI(并且不在数据库中存储任何内联HTML),那就更好了。

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

您可以指定哪种库?通常,我看不出为什么不这样。

是否可以创建我提供的模板概述 公司也显示该模板的预览?

是的。如上所述,如果您将所有模板都存储在数据库表中,例如templates您可以查询所有模板(也许用companyId上的键)并向其显示虚拟数据。

是否有一种方法可以检索该设备的ip和mac地址, 正在显示模板。

我不知道,但是正如@Andrei所说,我认为这是不可能的。

,

根据我对问题的了解,您需要为不同公司定制模板,但是如果将模板绑定到innerHTML上,并且绑定的文件包很大,则可能会遭受XSS攻击,这可能会导致页面加载缓慢。

这就是我要解决的问题

  • 定义一个Mixin,其中将保留有关每个公司的一般信息,例如,如果每个模板都有组和设备,我们可能会有一个如下所示的mixin
export type Constructor<T = {}> = new (...args: any[]) => T;
export const templateMixin = <T extends Constructor>(BaseClass: T = class { } as T) =>
  class extends BaseClass {
    devises$ = Observable<any[]>;
    groups$ = Observable<any[]>;
    data: any = { };
    // All other variables and functions that may be common in template file
  };
  • 将模板创建为有角组件...我知道这听起来很奇怪,因为捆绑包大小很大,但是接下来我们将解决此问题
@Component({
  selector: 'app-alpha-template',template: `
    `<div 
       [ngStyle]="{'border-left':(tr.state=='busy')?'10px solid #D4061C':'10px solid #2CC52E'}">
     {{data}}
    </div>`
  `,styleUrls: ['./e-learning-edit-course.component.css']
})
export class AlphaTemplate extends templateMixin { };

以上仅是示例,如果模板数量多于希腊字母,则可能需要更好的命名方式 现在,我们已经解决了XSS攻击的问题。下一个问题是包装尺寸

  • 我们注意到,不同的个人组将加载不同的模板,因此最好的方法是使用延迟加载

您可以定义一条路线并将子路线设置为延迟加载的templateComponents

,

如果我说对了,您想为最终用户创建类似网站构建器平台的东西,以便他们可以添加自己的设计。

如果是的话,我会说添加一些设计(针对特定部分由多个组件组成),并让他们选择添加应用程序中已经存在的特定设计。

这样,您无需使用innerHTML,也将使用角度安全性。

顺便说一句,我不认为这个问题与角度有关。它应该是您设计的一部分