Angular 基于 id 的一个组件中的多个模板(带有模板存储)
Angular multiple templates in one component based on id (with template store)
我有一个特殊的项目,但我找不到任何关于如何实现它的信息。
所以在这个网站上公司可以注册和登录。
当一家公司登录时,他们会看到设备和组的概览,其中设备可以分为不同的组以便于识别。
现在网站的难点是模板管理。每个设备将显示一个模板,该模板可以是通用指定模板、分配给特定组或单个设备的模板。选用的模板有标准提供的模板,有公司定制的模板,也有我自己定制的定制模板。 (最后2个选项只对公司本身可见)
这样做的主要原因是要显示不同的模板,我指的是结构差异,例如 table、卡片甚至自定义结构。
所以目前我可以根据公司的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', 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 保护,输入这种模板很容易产生问题,尤其是在内部HTML。所以我想知道是否有不同的解决方案,因为可能有数百个客户使用数百个不同的模板。
模板的示例:
编辑 2:
我的意思是:
is this possible by using any other library with 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))
);
当然这种方法并不完全有效,因为在某些情况下它仍然无法正确显示。就像你有:<div>{{date | date: 'EEEE d MMMM y' }} - {{date | date: 'HH:mm' }}</div>
,因为在这种情况下只有第一个是正确的。
我想知道如何解决 ngFor 循环和管道问题。
提前致谢!
您应该加载不同的组件,而不是不同的模板。 (仍然可以为一个组件应用不同的模板,但这很难做到,而且它会使您的应用程序性能变差,而且更难维护。如果您仍然需要此选项,请寻找动态编译)
您可以将一组组件注册为一些令牌,然后显示它们
{
provide: COMPONENTS_OF_CHOICE,
multi: true,
useValue: OneOfMyComponents
}
或
{
provide: COMPONENTS_OF_CHOICE,
useValue: [OneOfMyComponents, SecondOfMyComponents]
}
无法检索设备的 ip 和 mac 地址。它不安全并且浏览器不会公开该数据
is it possible to load in html from either data in the database or
from HTML files.
是的。例如,您可以创建一个“模板编辑器”,客户可以在其中构建模板并将该视图存储在数据库中。这不是很简单但可能。您可以从数据库中提取 HTML 并显示它,例如通过 <div [innerHTML]="data"></div>
。但是,由于注入安全风险 (xss),您必须清理用户输入等。如果您定义一组“构建块”,公司可以将这些块中的多个组合成一个模板,并且您动态构建 UI(并且不在数据库)。
is this possible by using any other library with Angular.
什么样的图书馆,能具体说说吗?通常我看不出有什么问题。
is there a way to create an overview of templates that I offer to
companies that displays a preview of that template as well?
是的。如上所述,如果您将所有模板存储在数据库中 table 例如templates
您可以查询所有模板(可能使用 companyId
上的键)并使用虚拟数据显示它们。
Is there a way to retrieve the ip and mac address of the device that
is displaying the template.
我不知道,但正如@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
};
- 将模板创建为 angular 组件...我知道这听起来很奇怪,因为包的大小,但我们接下来会解决这个问题
@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 攻击的问题。下一个问题是包大小
- 我们注意到不同的人群会加载不同的模板,所以最好的方法是使用延迟加载
您可以定义一个路由并将子路由设置为延迟加载的模板组件
如果我没看错,您想为最终用户创建网站构建器平台之类的东西,以便他们可以添加自己的设计。
如果是,我会说添加一些设计(通过特定部分的多个组件)并让他们选择添加已经在您的应用程序中的特定设计。
这样您就不需要使用 innerHTML 并且也会使用 angular 安全性。
顺便说一句,我认为这个问题与 angular 无关。它应该是你设计的一部分
我相信最简单的解决方案是绑定到 [innerHTML],正如之前@capc0
提到的那样
您提出了以下问题
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
考虑以下方法来处理这个问题
假设我们要从下面的对象
插入title
和cost
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 防止 XSS 攻击
综上所述,您现在有几个选择
- 在将模板字符串保存到数据库之前对其进行清理
- 在呈现之前手动清理模板字符串
- 只为发布模板的个人用户提供模板。这样他们可能只会攻击自己:)
选项 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>
`
},
...
我们可以添加行 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))
);
}
我有一个特殊的项目,但我找不到任何关于如何实现它的信息。
所以在这个网站上公司可以注册和登录。 当一家公司登录时,他们会看到设备和组的概览,其中设备可以分为不同的组以便于识别。 现在网站的难点是模板管理。每个设备将显示一个模板,该模板可以是通用指定模板、分配给特定组或单个设备的模板。选用的模板有标准提供的模板,有公司定制的模板,也有我自己定制的定制模板。 (最后2个选项只对公司本身可见)
这样做的主要原因是要显示不同的模板,我指的是结构差异,例如 table、卡片甚至自定义结构。
所以目前我可以根据公司的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', 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 保护,输入这种模板很容易产生问题,尤其是在内部HTML。所以我想知道是否有不同的解决方案,因为可能有数百个客户使用数百个不同的模板。
模板的示例:
编辑 2:
我的意思是:
is this possible by using any other library with 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))
);
当然这种方法并不完全有效,因为在某些情况下它仍然无法正确显示。就像你有:<div>{{date | date: 'EEEE d MMMM y' }} - {{date | date: 'HH:mm' }}</div>
,因为在这种情况下只有第一个是正确的。
我想知道如何解决 ngFor 循环和管道问题。
提前致谢!
您应该加载不同的组件,而不是不同的模板。 (仍然可以为一个组件应用不同的模板,但这很难做到,而且它会使您的应用程序性能变差,而且更难维护。如果您仍然需要此选项,请寻找动态编译)
您可以将一组组件注册为一些令牌,然后显示它们
{
provide: COMPONENTS_OF_CHOICE,
multi: true,
useValue: OneOfMyComponents
}
或
{
provide: COMPONENTS_OF_CHOICE,
useValue: [OneOfMyComponents, SecondOfMyComponents]
}
无法检索设备的 ip 和 mac 地址。它不安全并且浏览器不会公开该数据
is it possible to load in html from either data in the database or from HTML files.
是的。例如,您可以创建一个“模板编辑器”,客户可以在其中构建模板并将该视图存储在数据库中。这不是很简单但可能。您可以从数据库中提取 HTML 并显示它,例如通过 <div [innerHTML]="data"></div>
。但是,由于注入安全风险 (xss),您必须清理用户输入等。如果您定义一组“构建块”,公司可以将这些块中的多个组合成一个模板,并且您动态构建 UI(并且不在数据库)。
is this possible by using any other library with Angular.
什么样的图书馆,能具体说说吗?通常我看不出有什么问题。
is there a way to create an overview of templates that I offer to companies that displays a preview of that template as well?
是的。如上所述,如果您将所有模板存储在数据库中 table 例如templates
您可以查询所有模板(可能使用 companyId
上的键)并使用虚拟数据显示它们。
Is there a way to retrieve the ip and mac address of the device that is displaying the template.
我不知道,但正如@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
};
- 将模板创建为 angular 组件...我知道这听起来很奇怪,因为包的大小,但我们接下来会解决这个问题
@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 攻击的问题。下一个问题是包大小
- 我们注意到不同的人群会加载不同的模板,所以最好的方法是使用延迟加载
您可以定义一个路由并将子路由设置为延迟加载的模板组件
如果我没看错,您想为最终用户创建网站构建器平台之类的东西,以便他们可以添加自己的设计。
如果是,我会说添加一些设计(通过特定部分的多个组件)并让他们选择添加已经在您的应用程序中的特定设计。
这样您就不需要使用 innerHTML 并且也会使用 angular 安全性。
顺便说一句,我认为这个问题与 angular 无关。它应该是你设计的一部分
我相信最简单的解决方案是绑定到 [innerHTML],正如之前@capc0
提到的那样您提出了以下问题
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
考虑以下方法来处理这个问题
假设我们要从下面的对象
插入title
和cost
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 防止 XSS 攻击
综上所述,您现在有几个选择
- 在将模板字符串保存到数据库之前对其进行清理
- 在呈现之前手动清理模板字符串
- 只为发布模板的个人用户提供模板。这样他们可能只会攻击自己:)
选项 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>
`
},
...
我们可以添加行 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))
);
}