所以我想知道是否有一种方法可以传递ng-template并生成其所有内容以包括插值中使用的变量?
另外,我对angular还是很陌生,所以除了删除html元素之外,我还需要担心删除其他内容吗?
在这末尾,将有一个指向stackblitz.com仓库的链接,该仓库将具有下面显示的所有代码。
以下是实现我的指令的src / app / app.component.html代码:
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<!-- popup/popup.directive.ts contains the code i used in button tag -->
<button PopupDir="" body="this is a hardcoded message that is passed to popup box"> simple
</button>
<ng-template #Complicated="">
<div style="background-color: red;">
a little more complicated but simple and still doable
</div>
</ng-template>
<button PopupDir="" [body]="Complicated">
complicated
</button>
<ng-template #EvenMoreComplicated="">
<!-- name and data isn't being passed i need help here-->
<div style="background-color: green; min-height: 100px; min-width:100px;">
{{name}} {{data}}
</div>
</ng-template>
<button PopupDir="" [body]="EvenMoreComplicated">
more complicated
</button>
以下是我的src / app / popup / popup.directive.ts
import { Directive, Input, TemplateRef, ViewContainerRef, HostListener } from '@angular/core'
@Directive({
selector: 'PopupDir, [PopupDir]'
})
export class Popup {
@Input() body: string | TemplateRef<any>;
viewContainer: ViewContainerRef;
popupElement: HTMLElement;
//i dont know if i need this
constructor (viewContainer: ViewContainerRef) {
this.viewContainer = viewContainer;
}
//adds onlick rule to parent tag
@HostListener('click')
onclick () {
this.openPopup();
}
openPopup() {
//Pcreate pupup html programatically
this.popupElement = this.createPopup();
//insert it in the dom
const lastChild = document.body.lastElementChild;
lastChild.insertAdjacentElement('afterend', this.popupElement);
}
createPopup(): HTMLElement {
const popup = document.createElement('div');
popup.classList.add('popupbox');
//if you click anywhere on popup it will close/remove itself
popup.addEventListener('click', (e: Event) => this.removePopup());
//if statement to determine what type of "body" it is
if (typeof this.body === 'string')
{
popup.innerText = this.body;
} else if (typeof this.body === 'object')
{
const appendElementToPopup = (element: any) => popup.appendChild(element);
//this is where i get stuck on how to include the context and then display the context/data that is passed by interpolation in ng-template
this.body.createEmbeddedView(this.viewContainer._view.context).rootNodes.forEach(appendElementToPopup);
}
return popup;
}
removePopup() {
this.popupElement.remove();
}
}
这是显示我的问题的回购链接: https://stackblitz.com/edit/popupproblem
答案 0 :(得分:2)
首先让我们考虑一下如何将上下文传递给嵌入式视图。您写道:
public class XMLMaker {
private boolean docTypeDeclared = false;
public DocumentBuilderFactory docDriverSetup;
public DocumentBuilder driverSetup;
public int connectedDevices = 0;
Document doc;
TransformerFactory transformerFactory;
Transformer transformer;
public void setupDriverXMLFile(List <AppiumDriver<MobileElement>> driverList) {
try {
docDriverSetup = DocumentBuilderFactory.newInstance();
driverSetup = docDriverSetup.newDocumentBuilder();
doc = driverSetup.newDocument();
transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer();
if(docTypeDeclared == false) {
DOMImplementation domImpl = doc.getImplementation();
DocumentType docType = domImpl.createDocumentType("suite", "","http://testng.org/testng-1.0.dtd");
doc.appendChild(docType);
docTypeDeclared = true;
}
Element suiteElement = doc.createElement("suite");
for(AppiumDriver<MobileElement> driver: driverList) {
Element rootElement = doc.createElement("test");
suiteElement.appendChild(rootElement);
rootElement.setAttribute("name", (String) driver.getCapabilities().getCapability("deviceName"));
Element deviceNameEle = doc.createElement("parameter");
deviceNameEle.setAttribute("name", "deviceName");
deviceNameEle.setAttribute("value", (String) driver.getCapabilities().getCapability("deviceName"));
rootElement.appendChild(deviceNameEle);
Element platformEle = doc.createElement("parameter");
platformEle.setAttribute("name", "platform");
platformEle.setAttribute("value", driver.getPlatformName()+"");
rootElement.appendChild(platformEle);
Element udidEle = doc.createElement("parameter");
udidEle.setAttribute("name", "udid");
udidEle.setAttribute("value", (String)driver.getCapabilities().getCapability("udid"));
rootElement.appendChild(udidEle);
Element urlPort = doc.createElement("parameter");
urlPort.setAttribute("name", "URL");
urlPort.setAttribute("value", (String)driver.getCapabilities().getCapability("appiumURL"));
rootElement.appendChild(urlPort);
Element devicePort = doc.createElement("parameter");
if((driver.getPlatformName()+"").
toLowerCase().contains("android")) {
devicePort.setAttribute("name", "port");
devicePort.setAttribute("value", (String)driver.getCapabilities().getCapability("systemPort"));
}
if ((driver.getPlatformName()+"").
toLowerCase().contains("ios")) {
devicePort.setAttribute("name", "port");
devicePort.setAttribute("value", (String)driver.getCapabilities().getCapability("wdaLocalPort"));
}
rootElement.appendChild(devicePort);
Element packageName = doc.createElement("package");
packageName.setAttribute("name", "BaseTest");
devicePort.appendChild(packageName);
connectedDevices++;
}
suiteElement.setAttribute("parallel", "tests");
suiteElement.setAttribute("thread-count", connectedDevices+"");
doc.appendChild(suiteElement);
} catch(ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}
}
public void createDriverFile() throws TransformerConfigurationException {
transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File("./drivers.xml"));
try {
transformer.transform(source, result);
System.out.println("File Updated");
} catch (TransformerException e) {
e.printStackTrace();
}
}
}
您的this.body.createEmbeddedView(this.viewContainer._view.context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
组件托管在Popup
视图中,因此AppComponent
将是this.viewContainer._view.context
实例。但是我想让你说:
1)嵌入式视图已经可以访问定义AppComponent
的模板的范围。
2)如果我们传递上下文,则应仅通过模板引用变量使用它。
ng-template
因此,在这种情况下,您不需要传递上下文,因为它已经存在。
this.body.createEmbeddedView(this.viewContainer._view.context)
||
\/
this.body.createEmbeddedView({
name = 'Angular';
data = 'this should be passed too'
})
||
\/
<ng-template #EvenMoreComplicated let-name="name" let-data="data">
{{name}} {{data}}
角度变化检测机制依赖于视图树。
this.body.createEmbeddedView({})
||
\/
<ng-template #EvenMoreComplicated>
{{name}} {{data}}
我们看到有两种视图:组件视图和嵌入式视图。 AppComponent_View
/ \
ChildComponent_View EmbeddedView
|
SubChildComponent_View
(ng-template)表示嵌入式视图。
当Angular要更新UI时,它只是通过该视图执行两个检查绑定。
现在让我们提醒一下如何通过低级API创建嵌入式视图:
TemplateRef
TemplateRef.createEmbeddedView
它们之间的主要区别在于,前者仅创建EmbeddedView,而后者创建EmbeddedView,并且将其添加到Angular变化检测树。这样,嵌入式视图便成为变更检测树的一部分,我们可以看到更新的绑定。
是时候查看您的代码了:
ViewContainerRef.createEmbeddedView
很明显,您正在使用第一种方法。这意味着您必须自己进行更改检测:手动调用this.body.createEmbeddedView(this.viewContainer._view.context).rootNodes.forEach(appendElementToPopup);
或附加到树上。
简单的解决方案可能是:
viewRef.detectChanges()
但是它将只检测一次更改。我们可以在每个const view = this.body.createEmbeddedView({});
view.detectChanges();
view.rootNodes.forEach(appendElementToPopup);
钩子上调用detectChanges
方法,但是Angular本身有一种更简单的方法。
Popup.ngDoCheck()
我们使用了第二种方法来创建嵌入式视图,以便Angular自身会自动检查模板。
我仍然对角度不熟悉,所以除了删除html元素外,我还 需要担心删除其他内容吗?
我认为我们也应该在关闭弹出窗口时破坏嵌入式视图。
const view = this.viewContainer.createEmbeddedView(this.body);
view.rootNodes.forEach(appendElementToPopup);