【问题标题】:Angular ViewEncapsulation.ShadowDom elements in Protractor give StaleElementExceptionError量角器中的 Angular ViewEncapsulation.ShadowDom 元素给出 StaleElementExceptionError
【发布时间】:2021-07-22 12:54:34
【问题描述】:

我在一个角度组件中有一个 iframe,我使用组件工厂动态地向其中注入一个组件 X(将本机元素附加到 iframe 的主体)。我这样做的原因是因为我需要媒体查询才能在组件中运行。如果没有 ViewEncapsulation.ShadowDom,在框架内注入的组件将是“无样式”的。

到目前为止,一切都很好,而且功能完全按照我的意愿工作。但是,在 e2e 测试期间,我无法访问 shadow-root 中的任何元素,我尝试了多种方法,例如 document.querySelector('component-id').shadowRoot.querySelector('element-id') 甚至提到了 here 的库和变通方法。

一切都会导致同样的错误

StaleElementReferenceError:过时的元素引用:过时的元素不是 找到了

总结一下,

  • 一个带有 Shadow Dom 封装的 Angular 组件被注入到 iframe 中
  • 无法使用所有可用方法访问注入组件的 shadowroot 内的元素

【问题讨论】:

  • 你控制iframe的全部内容吗?还是来自另一个站点,而您只是在那里注入了自己的东西?
  • @AviadP。我控制框架的全部内容,iframe 被注入了一个组件(在同一个项目中)。主要使用 iframe,因为我们需要媒体查询才能正常工作
  • 问题,如果你控制了iframe内容,为什么不把它指向app中的一个url呢?为什么要麻烦从外部注入 iframe?
  • 这是个好问题。这样做的原因是整个应用程序是一个包含多层组件和路由的SPA,而最终iframe内部显示的内容只是一个简单的组件
  • 设置一个仅显示该组件并将 iframe 的源 URL 指向该组件的 Angular 路由有多难?

标签: angular protractor angular-e2e


【解决方案1】:

我意识到这个答案可能无法解决您的特定问题,并且我冒着因投票而失去代表的风险,但我想根据我对上述问题的 cmets 建议对架构进行更改。

由于您可以完全控制 iframe 的内容,我建议您设置 Angular 路由,以允许您为 iframe 指定一个路由,该路由将显示您的简单组件,同时保留应用程序的其余路由完好无损。

摘要

因此,假设您已正常设置应用程序的路由,并使用托管 <router-outlet> 标记的顶级组件(通常为 app.component),我将创建一个新的顶级组件(我们称之为 app-plain.component)在它的模板中只包含<router-outlet></router-outlet>,没有别的。我将设置应用程序以将此组件用作引导组件(而不是app.component),然后我将更改路由配置以将所有现有路由作为新的空白路径路由('')的子级与旧的app.component 作为它的组件。

最后,我将设置一条新路由,其中​​包含一些路径(例如 iframe),它将使用您希望在 iframe 中显示的组件作为其组件(我们称之为 iframe.component)。

这将允许您将诸如 <iframe src="/iframe"></iframe> 之类的元素放在组件中的任何位置,并让它只显示您的 iframe.component

实现

假设您现有的路由配置如下所示:

const routes = [
  { path: '', pathMatch: 'full', redirectTo: 'home' },
  { path: 'home', component: HomeComponent },
  // ... more routes here
];

我会修改路由配置如下:

const newRoutes = [
  { path: 'iframe', component: IframeComponent },
  {
    path: '', component: AppComponent, children: routes // <-- the old routes
  }
];

如前所述,我将创建一个名为 app-plain.component 的新普通顶级组件,其模板中只有一个 &lt;router-outlet&gt;,并将其设置为模块的引导组件:

@NgModule({
  declarations: [...],
  imports: [...],
  providers: [...],
  bootstrap: [AppPlainComponent] // <-- this is the change
})
export class AppModule { }

注意:由于引导组件已更改,您还必须在 index.html 中指定其选择器(而不是默认情况下的 app-root

演示/示例

我用这个解决方案概念创建了一个github repo

我无法制作 StackBlitz,因为我尝试时 iframe 永远不会完成加载,这是我的 failed attempt

【讨论】:

  • 感谢您的建议,在非常高的水平上它看起来可以工作,我需要在本周的空闲时间尝试一下,看看它是否适合我的用例。同时,我对设计进行了细微的更改,以使其正常工作并完全绕过 shadowdom 封装的需要。在解析组件工厂之后,并且在将解析的组件注入 iframe 的主体之前,我还将样式添加到主体的 innerHTML。这很好用并解决了用例,样式设置在模板文字中,这也意味着对 IDE 的支持。
  • 经过一番思考后,我仍然倾向于在 iframe 内注入组件而不是路由,因为我需要组件工厂的灵活性,因为在框架内渲染的组件可以是不同,并且基于 API 的配置。
猜你喜欢
  • 2018-01-04
  • 1970-01-01
  • 2015-12-28
  • 2018-04-20
  • 2015-03-23
  • 1970-01-01
  • 2015-11-30
  • 2014-07-06
  • 1970-01-01
相关资源
最近更新 更多