【问题标题】:Try to add event (click) to dynamic create button angular2 DataTables.net尝试将事件(单击)添加到动态创建按钮 angular2 DataTables.net
【发布时间】:2019-10-15 14:52:02
【问题描述】:

我尝试为按钮元素设置事件(点击),但仍然有错误

错误错误:未捕获(承诺中):TypeError:无法读取未定义的属性“nativeElement”
TypeError:无法读取未定义的属性“nativeElement”

当我尝试调用<a (click)=EditButton(data)>Edytuj</a> 时没有任何动作,所以我尝试在几个版本中使用 ChildView、Renderer、nativeElement 但没有成功。

按钮嵌套在组件文件内的 DataTable.net 选项(作为列)中:

...
  columns: [
  { data: "Imie" },
  { data: "Nazwisko" },
  { data: "Brygada" },
  { data: "Stawka" },
  { data: "Aktywny" },
  {
    data: null, defaultContent :`
    <a #EditButton >Edytuj</a>
    <a #DeleteButton >Usuń</a>
    `
  },
],
buttons: [
...

下面是组件代码:

import { Component, OnInit, ViewChild, TemplateRef, Renderer2,  AfterViewInit } from '@angular/core';
import { FadeInTop } from "../shared/animations/fade-in-top.decorator";
import { Http, Response } from '@angular/http';

import { BsModalService, ModalDirective } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';

import { Observable } from "rxjs/Rx";
//import 'rxjs/add/operator/map';
//import 'rxjs/add/operator/catch';
import { bootstrapValidationRoutes } from '../+forms/+bootstrap-validation/bootstrap-validation.routing';


@FadeInTop()
@Component({
  selector: 'sa-czas-pracy',
  templateUrl: './czas-pracy.component.html',
  styleUrls: ['./czas-pracy.component.css']
})
export class CzasPracyComponent implements OnInit {
  
  public REST_ROOT = 'http://localhost:3000/pracownicy/pracownicy';
  
  //private static lgModal;

  options = {
    dom: "Bfrtip",
    ajax: (data, callback, settings) => {
      this.http.get(this.REST_ROOT)
        .map(this.extractData)
        .catch(this.handleError)
        .subscribe((data) => {
          console.log('data from rest endpoint', data);
          callback({
            aaData: data.slice(0, 100)
          })
        })
    },
    columns: [
      { data: "Imie" },
      { data: "Nazwisko" },
      { data: "Brygada" },
      { data: "Stawka" },
      { data: "Aktywny" },
      {
        data: null, defaultContent :`
        <button #EditButton >Edytuj</button>
        <button #DeleteButton >Usuń</button>
        `
      },
    ],
    buttons: [
      { text: 'Dodaj',
        action: () => {this.showAddModal()}
      }     
    ],
  };
  @ViewChild('AddEditModal') AddEditModal :ModalDirective;
  @ViewChild('EditButton') myButton;
  
  modalRef: BsModalRef;
  title: string;
  
  constructor(
          private http:Http, 
          private modalService: BsModalService,
          private renderer: Renderer2
        ) {}
 ngOnInit() {      
//   let simple1 = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
//     console.log('Clicking the button', evt);  
//    });    
    }
  ngAfterViewInit() {
    let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
      console.log('Clicking the button', evt);
    });
  }

  editPracownik(){
    console.log('edit');
  }
  ...

我搜索并尝试了很多方法,但没有任何效果。

任何解决方法都会有所帮助。


是的,我检查过,你说得对。 ngAfterContentChecked() 在 DataTable.option 之前和之后调用。所以我认为没关系,但 myButton 仍然是未定义的。

加载组件时的控制台:

czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:34 data from rest endpoint (3) [{…}, {…}, {…}]
3czas-pracy.component.ts:48 Create button
czas-pracy.component.ts:48 Create button
3czas-pracy.component.ts:48 Create button
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined

我尝试通过 methodw 引用引用,女巫链接到 html 模板中定义的按钮 - 而 myButton 仍然未定义

    <a class="btn btn-danger btn-xs" (click)="findMyButton()">FindMyButton</a>    

...

findMyButton(){    
  console.log('find my button clicked')
  console.log((this.myButton) ? 'is myButton' : "myButton undefined"); 
  if(this.myButton && !this.hasAttachedListener){
    let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
      console.log('Clicking the button', evt);
    });
    this.hasAttachedListener=true;
}

结果:

czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:90 find my button clicked
czas-pracy.component.ts:91 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined
czas-pracy.component.ts:79 AfterContentChecked
czas-pracy.component.ts:80 myButton undefined

更改代码

export class CzasPracyComponent implements OnInit, AfterContentChecked {
  
  public REST_ROOT = 'http://localhost:3000/pracownicy/pracownicy';
  
  //private static lgModal;

  options = {
    dom: "Bfrtip",
    ajax: (data, callback, settings) => {
      this.http.get(this.REST_ROOT)
        .map(this.extractData)
        .catch(this.handleError)
        .subscribe((data) => {
          console.log('data from rest endpoint', data);
          callback({
            aaData: data.slice(0, 100)
          })
        })
    },
    columns: [
      { data: "Imie" },
      { data: "Nazwisko" },
      { data: "Brygada" },
      { data: "Stawka" },
      { data: "Aktywny" },
      {
        data: null, render : function (data, type, row) {
          console.log('Create button');
          return `
                <button #EditButton >Edytuj</button>
                <button #DeleteButton >Usuń</button>
                `
              }
              },
    ],
    buttons: [
      { text: 'Dodaj',
        action: () => {this.showAddModal()}
      }     
    ],
  };
  @ViewChild('AddEditModal') AddEditModal :ModalDirective;
  @ViewChild('EditButton') myButton;
  @ViewChild('button') myButton1;
  
  modalRef: BsModalRef;
  title: string;
  hasAttachedListener:boolean=false;

  constructor(
          private http:Http, 
          private modalService: BsModalService,
          private renderer: Renderer2
        ) {}
 ngOnInit() {
 }

 ngAfterContentChecked() {
    console.log('AfterContentChecked');
    console.log((this.myButton) ? 'is myButton' : "myButton undefined"); 
    if(this.myButton && !this.hasAttachedListener){
      let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
          console.log('Clicking the button', evt);
      });
      this.hasAttachedListener=true;
    }
  }

  findMyButton(){
    console.log('find my button clicked')
    console.log((this.myButton) ? 'is myButton' : "myButton undefined"); 
    if(this.myButton && !this.hasAttachedListener){
      let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
          console.log('Clicking the button', evt);
      });
      this.hasAttachedListener=true;
    }
  }

你知道如何实现 Angular 可见的按钮代码吗?

【问题讨论】:

  • 你能告诉我你的 html 模板代码是如何插入数据表的吗?我真的无法弄清楚您的数据表是在何时何地生成的,使用您提供的选项启动 ajax 调用是什么,它是一个外部脚本吗?
  • 我正在使用来自 MyOragne 的 SmartAdmin 模板,并且实现了 DataTables.net,这里是代码 plnkr.co/edit/6rZfxiOmdy9d9b3tUMiu。 AngularJS有一个解决方案,但是我不能把它放到我的项目中datatables.net/examples/ajax/null_data_source.html
  • 我已经更新了我的答案,其中可能包含您可以使用的解决方案。

标签: jquery angular typescript dom-events


【解决方案1】:

我对 DataTable.net 不熟悉,但我认为这是正在发生的事情。通过 DataTable.net,您正在从后端加载信息以初始化您的视图。这是通过调用您的 REST_ROOT 链接来完成的,但您必须考虑到这需要时间,看起来结果在组件初始化时并不存在。

现在问题来了。您正在尝试访问一个尚不存在的按钮。

ngAfterViewInit() {
  let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
    console.log('Clicking the button', evt);
  });
}

ngAfterViewInit() 生命周期钩子在视图初始化后被调用一次。这是在组件创建后立即完成的,它不依赖于后期动态创建的内容。所以此时,您的 http 调用可能仍未完成,因此视图中不存在该按钮。所以你正在使用的生命周期钩子只是试图过早地找到你的按钮。这是文档中的内容:

Angular 初始化组件的视图和子组件后响应 意见。在第一个 ngAfterContentChecked() 之后调用一次。一个 仅组件挂钩。

您可能需要在另一个生命周期挂钩中实现它,例如 ngAfterViewChecked()。像这样的钩子被更频繁地调用,所以确保你总是检查你的按钮是否存在,特别是只附加一次点击事件,所以要跟踪你是否附加了它,否则你会有很多监听器按钮。

hasAttachedListener:boolean=false;

ngAfterViewChecked() {
    if(this.myButton && !this.hasAttachedListener){
        let simple = this.renderer.listen(this.myButton.nativeElement, 'click', (evt) => {
            console.log('Clicking the button', evt);
        });
        this.hasAttachedListener=true;
    }
}

最后一件事,我不确定@ViewChild 是否可以工作,如果 html 是刚刚创建的,例如 innerHTML 而不是通过渲染器或其他东西。我认为在这种情况下,角度不知道它应该观看#EditButton,因为它在创建时没有意识到它。因此,即使上面的修复可能会有所帮助(我不确定),但按钮引用仍然可能是未定义的。

编辑

我尽量让它更容易理解。使用ViewChild()# 运算符,角度为用户提供了获取真实DOM 元素引用的机会,这类似于您通常使用$ 选择器所做的事情。但这仅在 HTML 通过 angular 本身生成时才有效。例如,如果您有一个组件的模板代码,Angular 将获取该 HTML 代码并将其写入 DOM 本身,这样就知道里面的所有内容。如果我打开浏览器的开发人员工具并即时编辑一些 HTML 代码,那么 Angular 将对此一无所知。因此,由 Angular 生成的 HTML 代码与由人或与 Angular 无关的外部脚本生成的 HTML 代码是有区别的。

那么这里的问题是什么?问题是您的数据表 HTML 是在没有 Angular 的帮助下生成的,它是由根本不了解 Angular 的外部脚本生成的。这就像您在浏览器中打开开发人员工具并复制一些代码一样。Angular 不知道它刚刚发生。现在,如果您在此代码中有一个按钮,例如 #MyButton,那么您的 Angular 组件只有在 Angular 自己创建它时才能知道该引用存在。但是在您使用外部脚本的情况下,angular 根本不知道此按钮存在,因此您对@ViewChild() 的引用始终未定义。

通过顶层 ViewChild 的解决方法

您可以尝试以下方法。虽然您不能使用@ViewChild 引用动态创建的 DOM 元素,但您可以引用模板中的顶级 DOM 元素,之后在其中创建数据表代码。

<div #table>
    <!-- this is where your table will be placed -->
</div>

@ViewChild('table') tableRef;

现在有了这个参考,你可以等到你的代码准备好(你已经附加了日志文件,你可以在其中准确地看到数据表何时完成加载)并创建了 HTML。您的参考现在可以完全访问其所有子 div,包括您创建的表。您可以使用此引用来迭代其子项。

this.tableRef.nativeElement.childNodes

或者什么可能是更好的解决方案,您可以通过 查询选择 找到您的按钮,您可以在此顶级节点上调用该按钮。假设您已经更新了您的按钮,它现在将有一个 ID,而不是一个角度参考标记。

<button id="EditButton">Edytuj</button>

然后,当创建表时,我们在顶级节点上调用 querySelection 并找到具有 ID 的对象。

var editButton = this.tableRef.nativeElement.querySelector("#EditButton");
this.renderer.listen(editButton, 'click', (evt) => {
    console.log('Clicking the button', evt);
});

这应该可以解决问题。

【讨论】:

  • 谢谢,我已经编辑了问题并添加了更多细节我不确定我是否正确理解了你最后写的内容 > 最后一件事,我不确定@ViewChild 如果 html 是否有效......所以你的意思是如果我很好,这不是实现按钮的好地方了解
  • 在您的 smartadmin 模板中,您可以检查生成代码上方的最后一个 DOM 元素是什么,我认为这可能是 sa-datatable 标记。给它一个角度参考,这应该在这里工作,因为这是你的组件模板,所以角度会自己创建它。
  • 您的解释非常清晰和有用。它非常彻底,并为我指明了正确的方向。非常感谢。
【解决方案2】:

我终于解决了这个问题。当然非常感谢 Benedikt Schmidt。 下面的解决方案:

...
  dataPracownicy : any;

  options = {
    dom: "Bfrtip",
    ajax: (data, callback, settings) => {
      this.http.get(this.REST_ROOT)
        .map(this.extractData)
        .catch(this.handleError)
        .subscribe((data) => {
            this.dataPracownicy = data;
          console.log('data from rest endpoint', data);
          callback({
            aaData: data.slice(0, 100)
          })
        })
    },
    columns: [
      { data: "Imie" },
      { data: "Nazwisko" },
      { data: "Brygada" },
      { data: "Stawka" },
      { data: "Aktywny" },
      {
        data: null, render : function (data, type, row) {
          console.log('Create button');
          return `
                <button id=EditButton >Edytuj</button>
                <button #DeleteButton >Usuń</button>
                `
              }
              },
    ],
    buttons: [
      { text: 'Dodaj',
        action: () => {this.showAddModal()}
      }     
    ],
  };
  @ViewChild('AddEditModal') AddEditModal :ModalDirective;
  @ViewChild('TablePracownicy') tableRef;


  modalRef: BsModalRef;
  title: string;
  hasAttachedListener:boolean=false;

  constructor(
          private http:Http, 
          private modalService: BsModalService,
          private renderer: Renderer 
        ) {}
 ngOnInit() {}

ngAfterViewChecked() {
    if (!this.hasAttachedListener){
      var buttons = document.querySelectorAll("#EditButton");
      console.log(buttons.length  );
      if(buttons.length>0){
        console.log(buttons);
        for(let i=0;i<buttons.length;i++){
        this.renderer.listen(buttons[i], 'click', (evt) => {
          console.log('Clicking the button'+[i], evt);
          });
        }
        this.hasAttachedListener=true;
   }
  }
}
...

我希望我的解决方案有用 问候

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-08
    • 2022-01-09
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多