javascript - Web组件html模板导入表单分隔文件
问题描述
一般来说,Web 组件和 Web 技术的新手,我正在关注这个有趣的教程https://ayushgp.github.io/html-web-components-using-vanilla-js-part-2/。本教程使用导入指令将模板作为单独的文件导入。不再支持这种导入方式。我正在尝试使用他教程中第 1 部分(更新)中提供的作者解释来解决这个问题,但它不起作用。
基于教程https://github.com/ayushgp/web-components-tutorial提供的 git repo ,这里是我的更改:
./index.html
<html>
<head>
<meta charset="UTF-8" />
<title>Web Component Part 2</title>
</head>
<body>
<people-controller></people-controller>
<script src="./components/PeopleController/PeopleController.js"></script>
</body>
</html>
./components/PeopleController/PeopleController.html
<template id="people-controller-template">
<link rel="stylesheet" href="/components/PeopleController/PeopleController.css">
<people-list id="people-list"></people-list>
<person-detail id="person-detail"></person-detail>
<script src="./components/PeopleController/PeopleList/PeopleList.js"></script>
<script src="/components/PeopleController/PersonDetail/PersonDetail.js"></script>
</template>
./components/PeopleController/PeopleController.js
(async () => {
const res = await fetch('/components/PeopleController/PeopleController.html');
const textTemplate = await res.text();
// Parse and select the template tag here instead
// of adding it using innerHTML to avoid repeated parsing
// and searching whenever a new instance of the component is added.
const HTMLTemplate = new DOMParser().parseFromString(textTemplate, 'text/html')
.querySelector('template');
function _fetchAndPopulateData(self) {
let peopleList = self.shadowRoot.querySelector('#people-list');
fetch(`https://jsonplaceholder.typicode.com/users`)
.then((response) => response.text())
.then((responseText) => {
const list = JSON.parse(responseText);
self.peopleList = list;
peopleList.list = list;
_attachEventListener(self);
})
.catch((error) => {
console.error(error);
});
}
function _attachEventListener(self) {
let personDetail = self.shadowRoot.querySelector('#person-detail');
//Initialize with person with id 1:
personDetail.updatePersonDetails(self.peopleList[0]);
self.shadowRoot.addEventListener('PersonClicked', (e) => {
// e contains the id of person that was clicked.
// We'll find him using this id in the self.people list:
self.peopleList.forEach(person => {
if (person.id == e.detail.personId) {
// Update the personDetail component to reflect the click
personDetail.updatePersonDetails(person);
}
})
})
}
class PeopleController extends HTMLElement {
constructor() {
super();
this.peopleList = [];
}
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
const instance = HTMLTemplate.content.cloneNode(true);
shadowRoot.appendChild(instance);
_fetchAndPopulateData(this);
}
}
customElements.define('people-controller', PeopleController);
})()
./components/PeopleController/PeopleList/PeopleList.html
<template id="people-list-template">
<style>
.people-list__container {
border: 1px solid black;
}
.people-list__list {
list-style: none
}
.people-list__name {
cursor: pointer;
}
.people-list__list>li {
font-size: 20px;
font-family: Helvetica;
color: #000000;
text-decoration: none;
}
</style>
<div class="people-list__container">
<ul class="people-list__list"></ul>
</div>
</template>
./components/PeopleController/PeopleList/PeopleList.js
(async () => {
const res = await fetch('/components/PeopleController/PeopleList/PeopleList.html');
const textTemplate = await res.text();
// Parse and select the template tag here instead
// of adding it using innerHTML to avoid repeated parsing
// and searching whenever a new instance of the component is added.
const HTMLTemplate = new DOMParser().parseFromString(textTemplate, 'text/html')
.querySelector('template');
function _createPersonListElement(self, person) {
let li = currentDocument.createElement('LI');
li.innerHTML = person.name;
li.className = 'people-list__name'
li.onclick = () => {
let event = new CustomEvent("PersonClicked", {
detail: {
personId: person.id
},
bubbles: true
});
self.dispatchEvent(event);
}
return li;
}
class PeopleList extends HTMLElement {
constructor() {
// If you define a constructor, always call super() first as it is required by the CE spec.
super();
// A private property that we'll use to keep track of list
let _list = [];
// Use defineProperty to define a prop on this object, ie, the component.
// Whenever list is set, call render. This way when the parent component sets some data
// on the child object, we can automatically update the child.
Object.defineProperty(this, 'list', {
get: () => _list,
set: (list) => {
_list = list;
this.render();
}
});
}
connectedCallback() {
// Create a Shadow DOM using our template
const shadowRoot = this.attachShadow({ mode: 'open' });
const instance = HTMLTemplate.content.cloneNode(true);
shadowRoot.appendChild(instance);
}
render() {
let ulElement = this.shadowRoot.querySelector('.people-list__list');
ulElement.innerHTML = '';
this.list.forEach(person => {
let li = _createPersonListElement(this, person);
ulElement.appendChild(li);
});
}
}
customElements.define('people-list', PeopleList);
})();
./components/PeopleController/PersonDetail/PersonDetail.html
<template id="person-detail-template">
<link rel="stylesheet" href="/components/PeopleController/PersonDetail/PersonDetail.css">
<div class="card__user-card-container">
<h2 class="card__name">
<span class="card__full-name"></span> (
<span class="card__user-name"></span>)
</h2>
<p>Website: <a class="card__website"></a></p>
<div class="card__hidden-content">
<p class="card__address"></p>
</div>
<button class="card__details-btn">More Details</button>
</div>
</template>
./components/PeopleController/PersonDetail/PersonDetail.js
(async () => {
const res = await fetch('/components/PeopleController/PersonDetail/PersonDetail.html');
const textTemplate = await res.text();
// Parse and select the template tag here instead
// of adding it using innerHTML to avoid repeated parsing
// and searching whenever a new instance of the component is added.
const HTMLTemplate = new DOMParser().parseFromString(textTemplate, 'text/html')
.querySelector('template');
class PersonDetail extends HTMLElement {
constructor() {
// If you define a constructor, always call super() first as it is required by the CE spec.
super();
// Setup a click listener on <user-card>
this.addEventListener('click', e => {
this.toggleCard();
});
}
// Called when element is inserted in DOM
connectedCallback() {
const shadowRoot = this.attachShadow({ mode: 'open' });
const instance = HTMLTemplate.content.cloneNode(true);
shadowRoot.appendChild(instance);
}
// Creating an API function so that other components can use this to populate this component
updatePersonDetails(userData) {
this.render(userData);
}
// Function to populate the card(Can be made private)
render(userData) {
this.shadowRoot.querySelector('.card__full-name').innerHTML = userData.name;
this.shadowRoot.querySelector('.card__user-name').innerHTML = userData.username;
this.shadowRoot.querySelector('.card__website').innerHTML = userData.website;
this.shadowRoot.querySelector('.card__address').innerHTML = `<h4>Address</h4>
${userData.address.suite}, <br />
${userData.address.street},<br />
${userData.address.city},<br />
Zipcode: ${userData.address.zipcode}`
}
toggleCard() {
let elem = this.shadowRoot.querySelector('.card__hidden-content');
let btn = this.shadowRoot.querySelector('.card__details-btn');
btn.innerHTML = elem.style.display == 'none' ? 'Less Details' : 'More Details';
elem.style.display = elem.style.display == 'none' ? 'block' : 'none';
}
}
customElements.define('person-detail', PersonDetail);
})();
以及我在使用 chrome 时遇到的错误
PeopleController.js:23 ReferenceError: currentDocument is not defined
at _createPersonListElement (PeopleList.js:12)
at PeopleList.js:59
at Array.forEach (<anonymous>)
at HTMLElement.render (PeopleList.js:58)
at HTMLElement.set (PeopleList.js:42)
at PeopleController.js:18
我该如何解决?
有没有比作者提供的更好的方法来将 html 模板作为单独的文件导入?
谢谢!
解决方案
只需您可以使用 webpack 设置导入 HTML 文件。
从'./header.html'导入html
欲了解更多详情,请访问:https ://roshan-khandelwal.medium.com/web-components-c7aef23fe478
推荐阅读
- sql - 如何为以下条件编写选择查询
- azure-active-directory - 支持 Office 365 Enterprise E1 用户的 Azure AD MFA OATH 令牌功能
- javascript - 在 Jest 中编写测试时 Ref 为空
- laravel - 我如何返回多个页面的值.. 在 laravel 中我在下面给出的代码
- javascript - SQL - 如何获取单元格的特定值并将其保存到变量中?
- angular - 为什么我得到属性“地图”未定义
- java - 当整行为空时如何不将数据放入地图中
- c# - 不应为空的 C# 变量引发空引用异常
- oracle - 如何获取必填字段中的数据?
- python - 如何加快对 python 数组的操作?