Angular2 - 表达式在检查后已更改 - 通过调整大小事件绑定到 div 宽度

2024-02-28

我已经对此错误进行了一些阅读和调查,但不确定适合我的情况的正确答案是什么。我知道在开发模式下,更改检测会运行两次,但我不愿意使用enableProdMode()来掩盖问题。

这是一个简单的示例,其中表格中的单元格数量应随着 div 宽度的扩展而增加。 (请注意,div 的宽度不仅仅是屏幕宽度的函数,因此 @Media 不能轻易应用)

我的 HTML 如下所示(widget.template.html):

<div #widgetParentDiv class="Content">
<p>Sample widget</p>
<table><tr>
   <td>Value1</td>
   <td *ngIf="widgetParentDiv.clientWidth>350">Value2</td>
   <td *ngIf="widgetParentDiv.clientWidth>700">Value3</td>
</tr></table>

这本身没有任何作用。我猜这是因为没有什么导致发生变化检测。但是,当我将第一行更改为以下内容并创建一个空函数来接收调用时,它开始工作,但有时我会收到“检查错误后表达式已更改”

<div #widgetParentDiv class="Content">
   gets replaced with
      <div #widgetParentDiv (window:resize)=parentResize(10) class="Content">

我最好的猜测是,通过此修改,会触发更改检测并且所有内容都开始响应,但是,当宽度快速变化时,会引发异常,因为更改检测的上一次迭代需要比更改 div 的宽度更长的时间才能完成。

  1. 有没有更好的方法来触发变化检测?
  2. 我是否应该通过函数捕获调整大小事件以确保 发生变化检测吗?
  3. 正在使用#widthParentDiv 来访问 div 的宽度可以接受吗?
  4. 有没有更好的整体解决方案?

有关我的项目的更多详细信息,请参阅this https://stackoverflow.com/questions/38518016/angular2-respond-to-div-size类似的问题。

Thanks


要解决您的问题,您只需获取并存储div在每个调整大小事件之后的组件属性中,并在模板中使用该属性。这样,当第二轮变化检测在开发模式下运行时,该值将保持不变。

我也推荐使用@HostListener而不是添加(window:resize)到你的模板。我们将使用@ViewChild以获得参考div。我们将使用生命周期钩子ngAfterViewInit()设置初始值。

import {Component, ViewChild, HostListener} from '@angular/core';

@Component({
  selector: 'my-app',
  template: `<div #widgetParentDiv class="Content">
    <p>Sample widget</p>
    <table><tr>
       <td>Value1</td>
       <td *ngIf="divWidth > 350">Value2</td>
       <td *ngIf="divWidth > 700">Value3</td>
      </tr>
    </table>`,
})
export class AppComponent {
  divWidth = 0;
  @ViewChild('widgetParentDiv') parentDiv:ElementRef;
  @HostListener('window:resize') onResize() {
    // guard against resize before view is rendered
    if(this.parentDiv) {
       this.divWidth = this.parentDiv.nativeElement.clientWidth;
    }
  }
  ngAfterViewInit() {
    this.divWidth = this.parentDiv.nativeElement.clientWidth;
  }
}

可惜这不起作用。我们得到

检查后表情发生了变化。先前值:“假”。当前值:“真”。

错误是抱怨我们的NgIf表达式——第一次运行时,divWidth是 0,那么ngAfterViewInit()运行并将值更改为 0 以外的值,然后运行第二轮更改检测(在开发模式下)。值得庆幸的是,有一个简单/已知的解决方案,这是一个一次性的问题,而不是像OP中那样的持续问题:

  ngAfterViewInit() {
    // wait a tick to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth);
  }

请注意,这里记录了这种等待一个周期的技术:https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-view-child https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-view-child

常常,在ngAfterViewInit() and ngAfterViewChecked()我们需要雇用setTimeout()这是一个技巧,因为这些方法是在组件视图组成之后调用的。

Here's a working plunker http://plnkr.co/edit/1XhUD6jQ4otDx12LsN5b?p=preview.


我们可以让这变得更好。我认为我们应该限制调整大小事件,以便角度变化检测仅每 100-250 毫秒运行一次,而不是每次发生调整大小事件时运行。这应该可以防止用户调整窗口大小时应用程序变得缓慢,因为现在每个调整大小事件都会导致更改检测运行(在开发模式下运行两次)。您可以通过将以下方法添加到之前的 plunker 中来验证这一点:

ngDoCheck() {
   console.log('change detection');
}

Observables 可以轻松地限制事件,所以不要使用@HostListener为了绑定到调整大小事件,我们将创建一个可观察的:

Observable.fromEvent(window, 'resize')
   .throttleTime(200)
   .subscribe(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth );

这是可行的,但是......在进行实验时,我发现了一些非常有趣的东西......即使我们限制了调整大小事件,每次发生调整大小事件时,角度变化检测仍然会运行。即,限制不会影响更改检测的运行频率。 (托比亚斯·博斯证实了这一点:https://github.com/angular/angular/issues/1773#issuecomment-102078250 https://github.com/angular/angular/issues/1773#issuecomment-102078250.)

我只想在事件超过限制时间时运行更改检测。我只需要更改检测即可在该组件上运行。解决方案是在 Angular 区域外创建可观察对象,然后在订阅回调内手动调用更改检测:

constructor(private ngzone: NgZone, private cdref: ChangeDetectorRef) {}
ngAfterViewInit() {
  // set initial value, but wait a tick to avoid one-time devMode
  // unidirectional-data-flow-violation error
  setTimeout(_ => this.divWidth = this.parentDiv.nativeElement.clientWidth);
  this.ngzone.runOutsideAngular( () =>
     Observable.fromEvent(window, 'resize')
       .throttleTime(200)
       .subscribe(_ => {
          this.divWidth = this.parentDiv.nativeElement.clientWidth;
          this.cdref.detectChanges();
       })
  );
}

Here's a working plunker http://plnkr.co/edit/sMSIz2kKN5NKvN2s6rC8?p=preview.

在 plunker 中我添加了一个counter我使用生命周期钩子增加每个变更检测周期ngDoCheck()。您可以看到该方法没有被调用——计数器值在调整大小事件时不会改变。

detectChanges() https://angular.io/docs/ts/latest/api/core/index/ChangeDetectorRef-class.html#!#detectChanges-anchor将对此组件及其子组件运行更改检测。如果您希望从根组件运行更改检测(即运行完整的更改检测检查),则使用ApplicationRef.tick() https://angular.io/docs/ts/latest/api/core/index/ApplicationRef-class.html#!#tick-anchor相反(这在 plunker 中被注释掉了)。注意tick()会引发ngDoCheck()被称为。


这是一个很好的问题。我花了很多时间尝试不同的解决方案,并且学到了很多东西。感谢您发布这个问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular2 - 表达式在检查后已更改 - 通过调整大小事件绑定到 div 宽度 的相关文章

  • 为什么隐式符号到字符串转换会导致 JavaScript 中出现类型错误?

    有一个 toString on Symbol在 ES6 中 它返回字符串表示形式Symbol 但想知道为什么 Symbol 不起作用 运行这个表达式会抛出TypeError我没想到 后者只是打电话吗 toString 在一个新的Symbol
  • React Native:不透明视图内的透明视图

    我想用不透明框架和透明中心显示相机的视图 就像图片中的一样 黑色部分是相机的视图 我正在寻找具有纯反应本机组件的解决方案 没有额外的库 例如https github com gilbox react native masked view h
  • Angular 8 输入验证仅接受数字

    我正在创建动态输入字段 它将接受所有类型值 我需要限制只输入数字 模板 tr tr
  • 如何使用 JavaScript 选择预节点/块中的文本?

    我了解不允许 JS 将任意文本复制到剪贴板背后的安全原因 但是是否有一种方法可以通过单击按钮来选择预节点中的文本 类似于 select 函数在输入中的工作方式 我不是在寻找复制到剪贴板的 jQuery 插件 我只想突出显示预块中的文本 以便
  • 您可以将现有的 div 复制到模式对话框吗

    我有一个带有多个面板的仪表板来显示不同的信息 我希望能够添加一个按钮来以模式显示面板 我正在使用引导程序 我所能找到的只是已经编写的模态 我想复制作为面板的 div 标签的内容 然后将其显示在模型中 但我不确定如何进行 该面板的 html
  • 实现悬停信息框

    我有一个日历 当用户将鼠标悬停在单元格上时 会出现一个很大的信息框 其中包含该日期的详细信息 虽然当用户离开时使信息框消失 但我遇到了一些麻烦 我基本上想要它 这样当鼠标光标移出信息框隐藏的日历单元格时 它就会消失 但我遇到了麻烦 因为mo
  • React 应用程序中的 addEventListener 不起作用

    一些背景 我正在尝试消费自定义网络组件在 React 应用程序中并尝试监听来自 Web 组件的事件 我相信您不能只在自定义 Web 组件上以通常的反应方式处理事件 i e
  • 使用服务器端数据源时,标题复选框选择不起作用

    正如标题所示 我正在使用ag grid与 Angular 我使用一个自定义类来实现IServerSideDatasource用于从 API 获取数据rowModelType设置为 服务器端 问题是当我设置headerCheckboxSele
  • 如何在 javascript 中基于类型字符串创建新对象?

    如何基于变量类型字符串 包含对象名称 在 javascript 中创建新对象 现在我有 随着更多工具的出现 列表会变得更长 function getTool name switch name case SelectTool return n
  • 将 onclick 事件应用于页面加载时不存在的元素

    我将列表样式设置为看起来像选择框 并且当用户单击列表中的元素时我想触发一个函数 但是该元素是通过加载的AJAX因此 当页面加载并且我无法绑定时不存在onclick事件到它onDomReady 如果我把它作为一个普通的选择列表 我可以只标记一
  • 自定义指令链接中的 element.replaceWith 仅在第一次调用时有效

    我是 Angularjs 的新手 不太了解幕后的情况 基本上我想创建一个 E 扭结指令 基于控制器中的数据 我动态创建html 就像整个 表 一样 以替换该指令 我的 html 文件中的指令是这样的
  • @aspnet/signalr 与 @microsoft/signalr javascript 库

    aspnet signalr 与 microsoft signalr javascript 库有什么区别 两者似乎都对 DotNetCore SignalR 有效 两者似乎都很活跃 在一些教程中 我找到 aspnet signalr 在Do
  • jQuery 悬停时滚动到 div 并返回到第一个元素

    我基本上有一个具有设定尺寸的 div 和overflow hidden 该 div 包含 7 个子 div 但一次只显示一个 我希望当它们各自的链接悬停时能够平滑地垂直滚动 但是 第一部分 div 没有链接 并且是没有悬停链接时的默认部分
  • 如何读取firebase推送通知内容并在ionic2中触发方法?

    是否可以访问push notificationionic 2 中的内容并在通知到达时执行一堆代码或event fire 我建议使用科尔多瓦插件 firebase https github com arnesson cordova plugi
  • 检测浏览器选项卡是否具有焦点

    是否有可靠的跨浏览器方法来检测选项卡是否具有焦点 场景是 我们有一个定期轮询股票价格的应用程序 如果页面没有焦点 我们可以停止轮询并为每个人节省流量噪音 特别是当人们喜欢打开具有不同投资组合的多个选项卡时 Is window onblur
  • 在方法内部执行方法

    我目前正在 FreeCodeCamp 中进行 JavaScript 练习 我的代码应该使用的测试用例之一是函数调用 如下所示 addTogether 2 3 这是我得到的基本功能 function addTogether return 当我
  • 搜索多维数组 JavaScript

    我有一个如下所示的数组 selected products 0 r1 7up 61 Albertsons selected products 1 r3 Arrowhead 78 Arrowhead selected products 2 r
  • 当选项卡索引更改时,mat-tab-group 滚动到页面顶部

    我有奇怪的行为mat tab group在角度材料中 当我更改选项卡索引时 它会将页面滚动到顶部 知道为什么吗 这是角度材料库中的一个已知错误 请参阅here https github com angular material2 issue
  • p5 向量减法“sub”返回错误

    我一直在尝试将 p5 草图上传到 React 构建中 使用react p5 wrapper 我能够成功在屏幕上渲染画布 但是 某些矢量函数会导致错误 var distance this position dist ball position
  • DOM 解析器 Chrome 扩展内存泄漏

    问题 我开发了一个扩展程序 可以拦截 Web 请求 获取 Web 请求来源的 HTML 并对其进行处理 我使用 DOMParser 来解析 HTML 并且意识到 DOMParser 正在导致大量内存泄漏问题 最终导致 chrome 扩展崩溃

随机推荐

  • 如何通过按 Enter 退出 while 循环?

    I am trying to get a while loop to break by pressing the Enter key on a keyboard My code is package javaapplication4 imp
  • Android Espresso 执行长按而不是单击

    onData anything inAdapterView withId R id ScheduleOrderListViewListView atPosition 0 perform click 50 的时间执行长触摸 有没有好的解决方法
  • 用于收集对象的 Rails 模型类方法

    我在编写用于集合的类方法时遇到问题ActiveRecord对象 在过去的几个小时里 我已经两次遇到这个问题 这似乎是一个简单的问题 所以我知道我错过了一些东西 但我无法在其他地方找到答案 Example class Order lt Act
  • 无法在设备上调试应用程序

    我的所有设备都工作正常 这个特定的设备是三星的 但没有 当我插入手机时 手机会显示在 DDMS 设备列表中 但是 没有任何应用程序 进程出现 这是唯一发生这种情况的设备 是的 USB 调试已启用 是的 debuggable 在清单中设置为
  • css嵌入无滚动条

    好吧 我有一个带有 ajax 按钮的页面 当您点击 ajax 按钮时 它会将一个嵌入对象扔到 data div 中 一切正常 我的问题是嵌入正在创建一个我不想要的滚动条 关于如何防止滚动条有什么想法吗 我已经尝试过在任何地方都可以抛出 ov
  • Webgl使用视口+剪刀更新区域

    我一直在尝试创建一个多视口 webgl 应用程序 对于每个视图 我使用视口 剪刀将所有内容渲染得非常好 但现在我想改进渲染并只渲染更新的视图 因此跳过过度绘制 我做了一个小演示来展示这个想法 http kile stravaganza or
  • LIMIT then RAND 而不是 RAND then LIMIT

    我正在使用全文搜索来提取行 我根据分数对行进行排序 ORDER BY SCORE 然后在前 20 行 LIMIT 20 中 我想对结果集进行兰特 RAND 操作 因此 对于任何特定的搜索词 我想随机显示前 20 个结果中的 5 个 我的解决
  • MSI 安装程序错误 2810 中断安装,但仍然顺利完成

    我创建了一个安装应用程序的设置 并且仍然如此 但它最后开始突然发出奇怪的警告 因此 当安装过程完成时 会出现以下内容 安装程序在安装此软件包时遇到意外错误 这可能表明此包有问题 错误代码是2810 于是我查了2810 上面写着 在对话框 2
  • Angularjs 1.7.9 - 可能未处理的拒绝[重复]

    这个问题在这里已经有答案了 我经常遇到这个错误Possibly unhandled rejection即使在使用时 promise then success err or promise then success catch err 我应该
  • Oracle 12c - “number”列上的索引比“varchar”列上的索引执行得更快吗?

    假设我在 Oracle 12c 中有一个表 其中包含以下列 create table t1 a number 5 0 b varchar 5 0 d e 然后我在具有相同值的两列中插入 100 000 000 条记录 例如 20151 an
  • 将 Android Studio 项目与 Gradle 文件同步

    I was working on a project and then I got a prompt to update Android Studio After I did that I started getting this erro
  • 如何以编程方式触发浏览器的搜索?

    我喜欢做的是通过 JavaScript 以编程方式触发浏览器的页面搜索功能 I e when a page is loaded and the the user presses Ctrl F a search field is opened
  • JobStorage.Current 属性值尚未初始化。您必须在使用 Hangfire 客户端或服务器 API 之前设置它

    我在 mvc 应用程序中使用hangfire 我正在向用户发送提醒 预约 我已经在我的应用程序中安装了hangfire 我已经配置了hangfire 启动 cs 类 但是当我运行该应用程序时 它会产生以下错误 作业存储 当前属性值尚未初始化
  • Python中快速嵌套列表的意外更新结果[重复]

    这个问题在这里已经有答案了 为什么下面不能更新第一个元素而是整列 gt gt gt x 2 2 1 gt gt gt x 1 1 1 1 gt gt gt x 0 0 2 gt gt gt x 2 1 2 1 即使这是明显的重复但使用ran
  • Android:ListView.getScrollY() - 它有效吗?

    我正在使用它 但它总是返回 0 即使我已经滚动到列表末尾 getScrollY 实际上是View上的方法 而不是ListView上的方法 它指的是整个视图的滚动量 所以它几乎总是0 如果你想知道ListView的内容滚动了多远 可以使用li
  • 气球弹出 WPF

    我需要显示一个气球弹出窗口 WPF中有针对此类工作的控件吗 像下面这样
  • 在 Swift 中从 iPhone 加载联系人会崩溃

    我正在尝试加载我的应用程序的联系人 它在模拟器中运行良好 但在 iPhone 上却崩溃了 我正在使用的代码 func getContactNames let allContacts ABAddressBookCopyArrayOfAllPe
  • 将 SQL 列空值转换为 0

    我是 SQL Server 新手 有一个问题 我有这样的视图 其中公式中的某些列允许为空 我如何将这些空值转换为 0 因为如果它们为空 则公式的结果也将为空 Thanks CREATE VIEW vwAchizitii AS SELECT
  • while 循环内的表变量并非每次都初始化:SQL Server

    我想知道为什么 while 循环内的表变量的行为与其他变量不同 表变量仅创建一次 并将在整个循环中使用 但每次循环增加时其他变量都会被初始化 查看下面的代码以获取更多信息 declare tt int set tt 10 while tt
  • Angular2 - 表达式在检查后已更改 - 通过调整大小事件绑定到 div 宽度

    我已经对此错误进行了一些阅读和调查 但不确定适合我的情况的正确答案是什么 我知道在开发模式下 更改检测会运行两次 但我不愿意使用enableProdMode 来掩盖问题 这是一个简单的示例 其中表格中的单元格数量应随着 div 宽度的扩展而