Angular2视图在更改后未更新。可能的“此”上下文问题?

时间:2017-03-28 00:32:57

标签: angular typescript

每次收到来自WebSocket的消息时,我都会尝试更新totalRequestspayloadSizepayloadSizeType属性。这项工作成功,我可以通过console.log确认,因为它们会根据从服务器发送的内容正确更新每个日志。

问题是我的模板由于某种原因没有更新以反映这些更改。

我可以确认没有其他潜在的错误,因为当我创建一个测试函数并在点击时更新这些值时,它们会更新并且更改确实会反映在模板中,因此我得出结论,错误必定即将到来从我传递WS回调的方式...除非有其他我不知道的东西。

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

import { AttackService } from './attack.service';
import { WebSocketService } from '../../../lib/service/websocket/websocket.service';
import { WebSocketConfig } from '../../../lib/service/websocket/websocket.config';

@Component({
  selector: 'attack',
  templateUrl: './attack.component.html',
  styleUrls: ['./attack.component.css'],
  providers: [AttackService, WebSocketService]
})
export class AttackComponent implements OnInit {
  private model: Object = {
    url: "http://localhost",
    kb_per_request: 5,
    duration: 5
  };

  private hasReceivedMessage: boolean = false;
  private totalRequests: number = 0;
  private payloadSize: number = 0;
  private payloadSizeType: string = 'KB';

  constructor(private attackService: AttackService, private socket: WebSocketService) {}

  ngOnInit(): void {
    this.socket.create(new WebSocketConfig(this.sOnOpen, this.sOnClose, this.sOnMessage, this.sOnError));
  }

  sOnOpen(): void {
    console.log('WebSocket connection successfully established.');
  }

  sOnClose(code: number): void {
    console.log(`WebSocket connection closed (${code}).`);
  }

  sOnMessage(data: any): void {
    this.hasReceivedMessage = true;

    this.totalRequests = data.total_requests;
    this.payloadSize = data.payload_size;
    this.payloadSizeType = data.payload_size_type;

    console.log('====================================');
    console.log('Total requests: ', this.totalRequests);
    console.log('Payload size: ', this.payloadSize);
    console.log('Payload type: ', this.payloadSizeType);
  }

  sOnError(data: any): void {
    console.log('WebSocket error occurred: ', data);
  }

  submit(): void {
    this.attackService.attack(this.model).subscribe(
        res => {
          let data = res.json();

          if (data.success) {
            console.log(data.message);
          }
        },
        err => {
          console.log('Error:', err);
        }
      );
  }
}

我以为我使用错误的this上下文传递方法,但即使绑定正确的上下文,模板仍然不会更新以反映更改。

constructor(private attackService: AttackService, private socket: WebSocketService) {
    this.sOnOpen = this.sOnOpen.bind(this);
    this.sOnClose = this.sOnClose.bind(this);
    this.sOnMessage = this.sOnMessage.bind(this);
    this.sOnError = this.sOnError.bind(this);
  }

任何人都知道这个问题的解决方案吗?

更新

WebSocketService:

import { Injectable } from '@angular/core';
import { WebSocketConfig } from './websocket.config';

@Injectable()
export class WebSocketService {
  private socket = null;
  private uri = "ws://localhost:80/ws";

  create(config: WebSocketConfig): void {
    window.onload = () => {
      this.socket = new WebSocket(this.uri);
      this.socket.onopen = config.onOpen;
      this.socket.onclose = config.onClose;
      this.socket.onmessage = res => config.onMessage(JSON.parse(res.data));
      this.socket.onerror = config.onError;
    };
  }

  send(data: any): void {
    this.socket.send(data);
  }
}

4 个答案:

答案 0 :(得分:1)

实际上,它确实看起来像是一个背景问题。您是否尝试过以下方法:

exact={true}

修改 老实说,我正在拍摄这张照片。但也许可以将此方法更改为在config上绑定:

<Router>
  <Route path='/' exact={true} component={A} />
  <Route path='/A' exact={true} component={A} />
  <Route path='/B' exact={true} component={B} />  
 </Router>

答案 1 :(得分:1)

尝试更新您的sOnMessage方法以将其自身包装在setTimeout中。

sOnMessage(data: any): void {
  setTimeout(() => {
    this.hasReceivedMessage = true;

    this.totalRequests = data.total_requests;
    this.payloadSize = data.payload_size;
    this.payloadSizeType = data.payload_size_type;

    console.log('====================================');
    console.log('Total requests: ', this.totalRequests);
    console.log('Payload size: ', this.payloadSize);
    console.log('Payload type: ', this.payloadSizeType);
   }, 1);
  }

如果可行,那么您在用户界面中看不到更新的原因是因为当您的Web套接字收到值时,未正确触发更改检测。 Angular的更改检测通过使用Zone.js来工作,Zone.js包装所有异步行为,以便每次异步函数完成时都可以通知Angular。如果setTimeout有效,那么Zone.js没有正确捕获websocket异步行为。

通过使用NgZone服务有更优雅的方法来解决问题,但是setTimeout可以快速修复。

答案 2 :(得分:1)

我对此的解决方案并不完全是两个答案所提供的,但他们指出了我正确的方向。

首先,我在AttackComponent中创建了我的websocket连接,我宁愿在我的AppComponent中创建它,以便可以全局访问连接(与我的问题无关)。

我创建了一个间隔函数来检查套接字是否已连接,如果是,则调用一个函数调用,这是一个我从AttackComponent传入的回调函数。

该回调是通过AttackComponent中的箭头函数传递的,这为我提供了正确的this上下文。然后我调用另一个方法将套接字onmessage属性设置为我的AttackComponent方法,并将其绑定到箭头函数允许的正确this上下文。我也可以在那之外完成绑定。

以下是我所做的示例,现在可以正常运行并更新我的模板:

WebSocketService:

  setSocketOnMessage(fn: Function): void {
    this.socket.onmessage = function (message) {
      fn(JSON.parse(message.data), message);
    };
  }

  onReady(cb: Function): void {
    let socketInterval = setInterval(() => {
      if (this.isConnected()) {
        cb();

        clearInterval(socketInterval);
      }
    }, 50);
  }

AttackComponent:

  constructor(private webSocketService: WebSocketService) {
    this.webSocketService.onReady(() => {
      this.webSocketService.setSocketOnMessage(this.sOnMessage.bind(this));
    });
  }

  sOnMessage(data: any): void {
    this.hasReceivedMessage = true;
    this.totalRequests = data.total_requests;
    this.payloadSize = data.payload_size;
    this.payloadSizeType = data.payload_size_type;
  }

答案 3 :(得分:0)

我有类似的问题。在我的情况下,websocket注入组件,因此它可能不是相同的解决方案。基本上我使用了这里描述的方法(https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html#observables

this.socket$.subscribe( (message: Message) => {
    this.messages.push(message);
    this.cd.markForCheck();  // mark the path from our component until root to be checked for the next change detection run.
 }

然后当我收到消息时......

print(String(bytes: jsonData, encoding: String.Encoding.utf8))

希望这有帮助