outerHTML
createLink(emp: Employee): string {
var anchor = document.createElement('a');
anchor.innerHTML = 'Details';
anchor.onclick = () => { this.handleDetailsClick(emp); };
anchor.href = '#';
return anchor.outerHTML;
}
Оператор return anchor.outerHTML
сериализует элемент привязки, используя алгоритм сериализации фрагмента HTML . Шаг 3.2 алгоритма описывает, как он принимает атрибуты элемента (которые имеют текстовые значения) и преобразует их в разметку для элемента.
Эта строка:
anchor.onclick = () => { this.handleDetailsClick(emp); };
не создает атрибут - он создает свойство функции («метод»), прикрепленное к элементу привязки. Он не обрабатывается алгоритмом сериализации и не сохраняется при вставке в пользовательский элемент innerHTML
как часть текстовой строки.
Быстрый тест показывает, что даже в виде текста обработчик щелчка должен быть явно добавлен в качестве сериализуемого атрибута:
"use strict";
var anchor = document.createElement('a');
anchor.innerHTML = 'Details';
anchor.onclick = "clickHandle()"
console.log("Setting a property doesn't work: " + anchor.outerHTML);
anchor.setAttribute("onclick", "clickHandle");
console.log("Setting an attribute does work: " + anchor.outerHTML);
Потенциальные решения
Цель дизайна - показать детали сотрудника при нажатии на ссылку. Ограничение состоит в том, что если ссылка должна быть внутренне обработана как текст, ее обработчик щелчков должен будет вызывать глобальный обработчик щелчков из фрагмента кода - который будет пахнуть.
Одной из альтернатив будет делегирование обработки кликов одному обработчику, добавленному к пользовательскому элементу во время создания, и добавление атрибута данных для ссылки на элементы, содержащие индекс сотрудника, используемый для доступа к данным частного класса (который не может быть достигнут из HTML исходный код).
Пример
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Custom Element</title>
<script>
class MyCustomElement extends HTMLElement {
constructor() {
super();
this.onclick = event => {
let link = event.target;
let index = +link.dataset.index;
if( link.tagName === 'A' && index >= 0) {
this.handleDetailsClick( this._serviceResponse[ index]);
}
};
// FOR TESTING ONLY:
function Employee( index) {
this.FirstName = "Firstname" + index;
this.LastName = "Lastname" + index;
this.Type = "1";
}
this._serviceResponse = [ new Employee(1), new Employee(2)];
setTimeout( this.renderHtml.bind(this), 10)
}
renderHtml() {
this.innerHTML = `
<table style="width: 100%;" border="2" >
<thead>
<th>Header1</th>
<th>Header2</th>
<th>Header3</th>
</thead>
<tbody>
${this._serviceResponse.map( this.getEmployeeTemplate.bind( this)).join(" ")}
</tbody>
</table>
`;
}
// ts: getEmployeeTemplate(employee: Employee, number: index ) {
// js:
getEmployeeTemplate( employee, index) {
switch (employee.Type) {
case "1":
return this.getRegularTemplate(employee, index);
case "2":
return `<tr><td colspan=3>Test Row}</td></tr>`;
}
}
// ts: getRegularTemplate(emp: Employee, number: index): string {
// js:
getRegularTemplate(emp, index){
return `
<tr>
<td> ${emp.FirstName} </td>
<td> ${emp.LastName} </td>
<td>
${this.createLink(index)}
</td>
</tr>
`;
}
createLink(index){
return `<a href="#" data-index="${index}">Details</a>`;
}
handleDetailsClick(emp) {
console.log('Details link clicked: ', emp);
}
} // end of class
customElements.define('employee-s', MyCustomElement);
</script>
</head>
<body>
<employee-s></employees>
</body>
</html>
Обратите внимание, что приведенный выше фрагмент имеет
- закомментировал TypeScript, чтобы он мог работать здесь,
- тестовый код добавлен в конструктор,
- изменено
renderHtml
для использования getEmployeeTemplate
в качестве функции карты,
- передал второй параметр карты (то есть индекс массива) в
createLink
,
- упрощенный
createLink
.
Также обратите внимание, что пользовательские элементы не могут иметь дочерние узлы при создании из пользовательских тегов - из-за этого тестовый код вызывает renderHTML
асинхронно.