尝试添加事件(单击)以动态创建按钮 angular2 DataTables.net
我尝试为按钮元素设置事件(单击),但仍然出现错误
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'nativeElement' of undefined
TypeError: Cannot read property 'nativeElement' of undefined
当我尝试调用
<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
我尝试通过链接到 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 可见的按钮代码吗?
我不熟悉 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 调用可能仍未完成,因此按钮不存在于视图中。所以你使用的生命周期钩子只是试图过早地找到你的按钮。文档中是这样说的:
Respond after Angular initializes the component's views and child views. Called once after the first ngAfterContentChecked(). A component-only hook.
您可能需要在另一个生命周期钩子中实现它,例如 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;
}
}
不过,最后一件事是,如果 html 只是作为 innerHTML 创建而不是通过渲染器或某些东西创建,我不确定 @ViewChild 是否有效。我认为在这种情况下,angular 不知道它应该监视 #EditButton,因为它在创建时没有意识到这一点。因此,即使上述修复可能有帮助(我不确定),按钮引用仍可能始终未定义。
编辑
我尝试使其更容易理解。使用
ViewChild()
和
#
运算符,angular 为用户提供了获取真实 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);
});
这应该可以解决问题。
我终于解决了这个问题。当然非常感谢 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;
}
}
}
...
希望我的解决方案有用 问候