一个组件中基于id的角形多个模板(带有模板存储) [英] Angular multiple templates in one component based on id (with template store)

查看:78
本文介绍了一个组件中基于id的角形多个模板(带有模板存储)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个特殊的项目,但是我找不到有关如何实现此目标的任何信息.

I have a special project and I haven't been able to find any information on how I can achieve this.

因此,公司可以在此网站上注册和登录.登录公司后,他们会看到设备和组的概述,可以将设备划分为不同的组,以便于识别.现在,网站的难点是模板管理.每个设备都将显示一个模板,该模板可以是常规指定的模板,也可以是分配给特定组或单个设备的模板.选择的模板是标准提供的模板,公司制造的自定义模板或我定制的自定义模板.(最后两个选项仅对公司本身可见)

So on this website companies can register and login. When a company is logged in they have an overview of devices and groups where devices can be divided in different groups for easy recognition. Now the hard part of the website is template management. Each device will display a template which could be a general specified template, a template that was assigned to a specific group or to an individual device. The templates that are chosen are either standard provided templates, custom templates made by the company or custom templates tailored by me. (The last 2 options are only visible for the company itself)

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

The main reason for this is to display different templates with that I mean structural differences like a table, cards and even custom structures.

因此,目前我能够根据公司的标识显示模板.这些模板已集成在angular应用程序中.所以现在看起来像这样(只是一个小例子):

So at the moment I am able to display templates based on the id of the company. These templates are integrated within the angular app. So now it kinda looks like this (its just a small example):

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

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

What happens afterwards is the creation of a component on the fly by keeping the compiler in the build. So this means that this project cannot be build in production mode as the compiler is required. Which means that deploying the project is awful because the code is visible in the browser and the size is much larger so it takes too much time to load everything in. I kinda want to step away from this method and use something else which is easier to use

所以我想知道的是:

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

如果无法在这种情况下使用Angular,您建议改用哪种环境,例如语言,框架等?

If it isn't possible to use Angular for this what environment like language, frameworks, etc. do you advise to use instead?

如果需要更多信息,请不要犹豫!

If more information is required don't hesitate to ask away!

预先感谢!

我尝试使用[innerHTML]加载模板,但这不适用于数据绑定或数据插值字符串.

I have tried to use [innerHTML] to load in the template but this doesn't work properly with data binding or data interpolation strings.

我将为您提供一个我想加载的HTML示例:

I'll give you an example of HTML I would like to load in:

    <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旁边,我还加载了以下样式:

Next to this HTML I am also loading following styles:

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

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

Entering this kind of template can easily generate issues especially in innerHTML because of XSS protection. So I would like to know whether there is a different solution to this since there might be hundreds of customers with hundreds of different templates.

示例模板的外观:

我的意思是

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

is this possible by using any other library with Angular.

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

is if it is not possible to achieve this using standard methods are there any libraries to could enable me to achieve this anyways.

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

So the idea of a template suggestion system is really nice, but the customer wants to create it and add it directly without other customers to see this.

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

This way I need to be able to save HTML files in the backend (whether it are templates or full pages doesn't matter) and load it inside of the angular application.

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

For as far as I am understanding all the answers below this will not be possible in Angular.

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

My question now is in which environment or language can I achieve this template mechanism? Or is there still a unknown method that is safe to use for production in Angular?

提前谢谢!

实施欧文·凯尔文(Owen Kelvins)的想法后,我发现了一些问题.使用ngFor循环来遍历数据不起作用.另外,在插值字符串内部添加管道是行不通的.

After implementing Owen Kelvins idea, I have found a few issues with this. Using ngFor loops to loop over data doesn't work. Also adding pipelines inside of the interpolation strings do not work.

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

To solve the pipeline issue you can solve this by making changes to the prev.toString() line:

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

当然,此方法不能完全正常工作,因为在某些情况下它仍无法正确显示.就像您有以下情况一样:< div> {{date |日期:'EEEE d MMMM y'}}-{{date |日期:'HH:mm'}}</div> ,因为在这种情况下,只有第一个是正确的.

Ofcourse this method doesn't work completely as in some cases it still doesn't display it correctly. Like when you have: <div>{{date | date: 'EEEE d MMMM y' }} - {{date | date: 'HH:mm' }}</div>, as in this case only the first one would be correct.

我想知道如何解决ngFor循环这两个管道问题.

I would like to know how I could fix both the ngFor loop as the pipeline issue.

预先感谢!

推荐答案

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

I believe the easiest solution will be to bind to [innerHTML] as earlier mentioned by @capc0

您提出的关注低于目标

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

Hi @capc0 your answer is completely correct. But, yes there is a but! I am using interpolation strings inside my html, innerHTML works fine but that is with static HTML. I am talking about HTML that has data interpolation strings which doesn't work properly with innerHTML

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

Consider below approach to deal with this problem

让我们说我们要从下面的对象中插入 title cost

Lets say we are to interpolate title and cost from the below object

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

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

Now the only challenge is to replace the interpolated section with the correct info

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

I will solve this with the below approach

定义变量以跟踪所选模板

Define variables to track the selected template

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

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

use combineLatest to combine the variables with template

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

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

The above works unfortunately styles will not be applied.

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

Option 1 With that we can use encapsulation: ViewEncapsulation.None, in our @Component({}) object see Angular 2 - innerHTML styling

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

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

With the above said, you now have a few options

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

参见此示例

选项2 另一种选择是使用 DomSanitizer 作为此帖子

Option 2 The other option is to use DomSanitizer as explainer in This Post

让我们假设用户包含了如下所示的内联样式

Lets assume users have included inline styles like below

  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))将结果字符串映射到受信任的字符串.代码看起来像

We can add the line map(template => this._sanitizer.bypassSecurityTrustHtml(template)) to map the resultant string to a trusted string. The code will look like

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

请参见在Stackblitz上进行演示

这篇关于一个组件中基于id的角形多个模板(带有模板存储)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆