Как штамповать шаблон в автономных пользовательских элементах с помощью vanilla js?

У меня есть пользовательский компонент, который просто показывает карту Таро. Перед пользовательским элементом я определил шаблон.
В подключенном обратном вызове моего wc я прикрепил сам шаблон к корню теней, а затем удалил его, клонировав его там же в корень теней. Я сделал это по 2 причинам:

  1. Я хочу, чтобы мой wc-компонент был автономным модулем; поэтому я хочу определить свой шаблон в том же месте, что и мой пользовательский элемент.
  2. Кажется, это единственный способ удалить мой шаблон, чтобы сделать его пригодным для использования, не вставляя его в документ владельца.

    var tmpl = `
        <template id="tmpl">
        <h1 class="tarot-title"><slot name="title">NEED TITLE</slot>
        </h1>
        <img src="${this.imageurl}" alt="">
        <p><slot name="subtitle">NEED A SUBTITLE</slot></p>
    </template>`;
    
    
    
    class BdTarot extends HTMLElement {
    
        ...constructor etc...
    
        connectedCallback() {
            this._shadowRoot.innerHTML = tmpl;
            var _tmpl = this._shadowRoot.querySelector('#tmpl');
            this._shadowRoot.appendChild(_tmpl.content.cloneNode(true));
    
        }
    
    }
    
    customElements.define('bd-tarot', BdTarot);
    

Проблема, которую это создало, состоит в том, что у каждого компонента карты Таро, который я использую на своей странице, есть тот же самый шаблон, является дочерним, все с тем же идентификатором. Раз они в тени корней, имеет ли это значение? Хотя пахнет смешно...

Моя цель - просто попытаться понять, как все спецификации веб-компонентов сочетаются друг с другом. У меня вопрос, есть ли лучший способ сделать это, чтобы сохранить код моего компонента вместе и не ссылаться на документ владельца? Является ли спецификация шаблона в основном несовместимой с пользовательскими элементами, поскольку импорт html не принимается большинством поставщиков браузеров?

3 ответа

Решение

В двух словах: если вы используете шаблоны литералов, то вы не должны использовать <template>элемент.

Вам не нужно дублировать шаблон, чтобы сохранить пользовательский элемент и коды шаблона вместе.

Вы можете просто заключить свой код в самозапускаемую функцию, чтобы быть уверенным, что переменная tmpl не будет переопределена.

(function () {

var tmpl = `
    <h1 class="tarot-title"><slot name="title">NEED TITLE</slot></h1>
    <img src="${this.imageurl}" alt="">
    <p><slot name="subtitle">NEED A SUBTITLE</slot></p>`;


class BdTarot extends HTMLElement {
    constructor() {
        super()      
        this.attachShadow( { mode: 'open' } ) 
             .innerHTML = tmpl;
    }
}

customElements.define('bd-tarot', BdTarot);

})()
<bd-tarot>
<span slot="title">Queen</span>
</bd-tarot>

Если вы хотите сохранить локальную копию шаблона, вы можете скопировать ее в переменную экземпляра (this.tmpl).

Попробуйте использовать приведенный ниже код, который добавляет пользовательскую аннотацию штампа в заголовок. Убедитесь, что размер пользовательского изображения штампа составляет 100*100 пикселей. В противном случае изображение штампа размыто или уменьшено.

      const customStampTool = new Tools.RubberStampCreateTool(documentViewer);
const customImage = '../stamp.png';

// Register tool
instance.UI.registerTool({
    toolName: stampToolName,
    toolObject: customStampTool,
    showPresets: true,
    dataElement: 'customStampToolButton',
    buttonImage: 'icon-tool-stamp-fill',
    buttonName: 'customStampToolButton',
    tooltip: 'Custom Stamp',
});

// Add tool button in header
instance.UI.setHeaderItems(header => {
    header
        .getHeader('toolbarGroup-Annotate')
        .get('highlightToolGroupButton')
        .insertBefore({
            type: 'toolButton',
            toolName: stampToolName,
            onClick: async () => {
                const imageSrc = customImage;
                const canvasHeight = 140;
                const canvasWidth = 160;
                const imageSize = 80;
                const stampAnnot = new Annotations.StampAnnotation();
                stampAnnot.PageNumber = 1;
                stampAnnot.X = 50;
                stampAnnot.Y = 50;
                stampAnnot.Width = canvasWidth;
                stampAnnot.Height = canvasHeight;

                // create a canvas in memory to draw your text to
                const canvas = document.createElement('canvas');
                canvas.width = canvasWidth;
                canvas.height = canvasHeight;
                const context = canvas.getContext('2d');

                // Create an image object. This is not attached to the DOM and is not part of the page.
                var image = new Image();

                // When the image has loaded, draw it to the canvas and add the annot to annotation manager
                image.onload = function () {
                    context.imageSmoothingEnabled = false;
                    context.drawImage(image, 40, 0, imageSize, imageSize);
                    context.font = "7pt Calibri";
                    context.fillText("Revise Name", 0, imageSize + 25);
                    context.fillText(userName, 0, imageSize + 30);

                    var currentdate = new Date();
                    var datetime = currentdate.getDate() + "/"
                        + (currentdate.getMonth() + 1) + "/"
                        + currentdate.getFullYear() + " "
                        + currentdate.getHours() + ":"
                        + currentdate.getMinutes() + ":"
                        + currentdate.getSeconds();
                    context.fillText(datetime, 0, imageSize + 35);

                    // convert your canvas to a data URL
                    const dataURL = canvas.toDataURL();

                    // put your data URL here
                    stampAnnot.ImageData = dataURL;

                    annotationManager.addAnnotation(stampAnnot);
                    annotationManager.redrawAnnotation(stampAnnot);
                }

                // set width and height of the image and set the source.
                image.width = imageSize;
                image.height = imageSize;
                image.src = imageSrc;
            }
        });
});

Хотя я согласен с утверждением @Supersharp о том, что литерал шаблона и шаблон не должны использоваться вместе, я думаю, что есть лучший способ управлять HTML-шаблоном компонента, несколько сочетая ваши подходы.

По сути, вместо того, чтобы иметь дело со строковым или шаблонным литералом, следует определить template один раз и вводить его оттуда при создании экземпляра каждого компонента.

const template = document.createElement('template');
template.innerHTML = `
    <h1 class="tarot-title">
        <slot name="title">NEED TITLE</slot>
    </h1>
    <img src="${this.imageurl}" alt="">
    <p>
        <slot name="subtitle">NEED A SUBTITLE</slot>
    </p>
`;

customElements.define('bd-tarot', class extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' }) 
            .appendChild(template.content.cloneNode(true));
    }
})();

Я писал здесь об этом, упомянув также мою собственную библиотеку, которая объединяет большую часть вышеуказанного шаблона в несколько API.

Другие вопросы по тегам