Сделайте веб-компонент для редактора - PullRequest
1 голос
/ 06 июня 2019

Я хочу создать тег веб-компонента, который состоит из редактора с некоторыми дополнительными функциями. Сейчас я использую редактор ACE-JSON.

Основной код такой: у меня есть другой файл js.

<!DOCTYPE HTML>
<html>

<head>
  <title>JSONEditor</title>
  <meta charset="utf-8">
  <link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.32.5/jsoneditor.css" rel="stylesheet" type="text/css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.32.5/jsoneditor.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
  <script src="app.js"></script>
  <!-- <script src="worker.js"></script> -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">

  <style>
    html,
    body {
      font: 11pt sans-serif;
    }

    #jsoneditor {
      width: 700px;
      height: 600px;
    }

    #paste,
    #link,
    #file {
      margin-top: 25px;
      align-content: center;

    }
  </style>
</head>

<body>
  <div class="container-fluid">
    <h1 align="center">Load and save JSON documents</h1>
    <br><br><br>
    <div class="row">
      <div class="col">
        <div id="jsoneditor"></div>
      </div>
      <div class="col col-centered">
        <div class="row">
          <div class="col">
            <select id="test" name="form_select" onchange="showDiv(this)">
              <option value="paste">paste json</option>
              <option value="file">Upload file</option>
              <option value="link">use link</option>
            </select>
          </div>
          <div class="col">
            <p>
              Save a JSON document: <input type="button" id="saveDocument" value="Save" />
            </p>
          </div>
        </div>
        <div class="row">
          <div class="col">
            <div id="file" style="display: none;" class="col-sm col-centered">
              Load a JSON document: <input type="file" id="loadDocument" value="Load" />
              <button class="button" onclick="readFile()">LOAD JSON</button>
            </div>
            <div id="paste" style="display: none;" class="col-sm col-centered">
              <h3>Paste JSON data </h3>
              <textarea id="myText" rows="4" cols="50"></textarea>
              <button class="button" onclick="loadText()">LOAD JSON</button>
            </div>
            <div id="link" style="display: none;" class="col-sm col-centered">
              <h3>URL</h3>
              <input type="text" id="url">
              <button class="button" onclick="urlOnclick()">LOAD JSON</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

<script>
  var options = {
    mode: 'tree',
    modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
    onError: function (err) {
      alert(err.toString());
    },
    onModeChange: function (newMode, oldMode) {
      console.log('Mode switched from', oldMode, 'to', newMode);
    }
  };
  // create the editor
  var editor = new JSONEditor(document.getElementById('jsoneditor'), options);

  // Save a JSON document
  document.getElementById('saveDocument').onclick = function () {
    // Save Dialog
    fname = window.prompt("Save as...");

    // Check json extension in file name
    if (fname.indexOf(".") == -1) {
      fname = fname + ".json";
    } else {
      if (fname.split('.').pop().toLowerCase() == "json") {
        // Nothing to do
      } else {
        fname = fname.split('.')[0] + ".json";
      }
    }
    var blob = new Blob([editor.getText()], { type: 'application/json;charset=utf-8' });
    saveAs(blob, fname);
  };
</script>

</html>

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

1 Ответ

0 голосов
/ 06 июня 2019

Сторонние библиотеки обычно не предназначены для использования с веб-компонентами.

Проблема, с которой вы столкнетесь, заключается в использовании jQuery и плагинов jQuery. При создании веб-компонента вы создаете #document-fragment в #shadowRoot компоненте в shadowDOM. Это означает, что стандартные операторы DOM, которые мы используем для выбора элементов (document.getElementById(...)), не будут работать для выбора элементов в shadowDOM. Это потому, что shadowDOM изолирован от корневого документа. Вот почему вы найдете только примеры простых тегов.

JSONEditor, например, широко использует document.createElement() .

Я преобразовал ваш HTML / JS в веб-компонент , который не будет работать по причинам, указанным выше . По крайней мере, это покажет вам, как поступить с более сложным примером.

Как видно из примера ниже, внешние компоненты и таблицы стилей легко загружаются в компонент. Синтаксис модуля import может использоваться вместо создания тегов сценария, но эти библиотеки не поддерживают этот синтаксис.

Вот ключевые движущиеся части:

  1. Класс ES6, представляющий логику пользовательского элемента
  2. Ваш HTML был помещен в тег <template>, который используется для хранения элементов DOM пользовательского элемента
  3. Это утверждение, которое делает пользовательский элемент доступным для использования:
    • customElements.define('my-editor', MyEditor);
  4. Поместить <my-editor></my-editor> в файл HTML

Подробнее см. В комментариях к исходному коду.

class MyEditor extends HTMLElement {
  constructor () {
    super();
    this.requiredScripts = [
      'https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js',
      'https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js',
      'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js',
      'https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.32.5/jsoneditor.js',
      'https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js',
    ];
    // Grab a reference to the custom element
    let template = document.getElementById('my-custom-editor');
    // Get the #document-fragment
    let templateContent = template.content;
    // Place the template from the Web Component in to the active DOM
    this.attachShadow({ mode: 'open' }).appendChild(templateContent.cloneNode(true));
  }
  // lifecycle hook that fires when custom element is placed into DOM
  connectedCallback () {
    // load required external resources
    this.loadScripts(0);
  }
  
  loadScripts (idx) {
    const tag = document.createElement('script');
    tag.src = this.requiredScripts[idx];
    tag.onload = () => {
      if (idx < this.requiredScripts.length - 1) {
        // when a script has finished, go get the next one
        this.loadScripts(++idx);
      } else {
        // Once all the script files are loaded, initialize the working parts of the component
        this.initEditor();
      }
    }
    tag.onload.bind(this);
    document.head.appendChild(tag);
  }
  
  initEditor () {
    // Need a reference to the #document-fragment
    // Every instance of "document" in this method has been replaced with "component"
    const component = document.getElementById('my-custom-editor').content;
    const options = {
      mode: 'tree',
      modes: ['code', 'form', 'text', 'tree', 'view'], // allowed modes
      onError: function (err) {
        alert(err.toString());
      },
      onModeChange: function (newMode, oldMode) {
        console.log('Mode switched from', oldMode, 'to', newMode);
      }
    };
    // create the editor IN THE compoent (rathar than the DOM)
    var editor = new JSONEditor(component.getElementById('jsoneditor'), options);
    component.getElementById('saveDocument').onclick = function () {
      fname = window.prompt("Save as...");
      if (fname.indexOf(".") == -1) {
        fname = fname + ".json";
      } else {
        if (fname.split('.').pop().toLowerCase() == "json") {
        } else {
          fname = fname.split('.')[0] + ".json";
        }
      }
      var blob = new Blob([editor.getText()], {
        type: 'application/json;charset=utf-8'
      });
      saveAs(blob, fname);
    };
    
  }
}

customElements.define('my-editor', MyEditor);
html,
body {
    font: 11pt sans-serif;
}
<!DOCTYPE HTML>
<html>

<head>
    <title>JSONEditor</title>
    <meta charset="utf-8">
    <link href="styles.css" rel="stylesheet">
    <script defer src="lib/MyEditor.js"></script>
    <!-- <script src="app.js"></script> -->
    <!-- <script src="worker.js"></script> -->

</head>

<body>

<my-editor></my-editor>


<template id="my-custom-editor">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/5.32.5/jsoneditor.css" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">

    <style>
        #jsoneditor {
            width: 700px;
            height: 600px;
        }

        #paste,
        #link,
        #file {
            margin-top: 25px;
            align-content: center;
        }
    </style>
    <div class="container-fluid">
        <h1 align="center">Load and save JSON documents</h1>
        <br><br><br>
        <div class="row">
            <div class="col">
                <div id="jsoneditor"></div>
            </div>
            <div class="col col-centered">
                <div class="row">
                    <div class="col">
                        <select id="test" name="form_select" onchange="showDiv(this)">
                            <option value="paste">paste json</option>
                            <option value="file">Upload file</option>
                            <option value="link">use link</option>
                        </select>
                    </div>
                    <div class="col">
                        <p>
                            Save a JSON document: <input type="button" id="saveDocument" value="Save"/>
                        </p>
                    </div>
                </div>
                <div class="row">
                    <div class="col">
                        <div id="file" style="display: none;" class="col-sm col-centered">
                            Load a JSON document: <input type="file" id="loadDocument" value="Load"/>
                            <button class="button" onclick="readFile()">LOAD JSON</button>
                        </div>
                        <div id="paste" style="display: none;" class="col-sm col-centered">
                            <h3>Paste JSON data </h3>
                            <textarea id="myText" rows="4" cols="50"></textarea>
                            <button class="button" onclick="loadText()">LOAD JSON</button>
                        </div>
                        <div id="link" style="display: none;" class="col-sm col-centered">
                            <h3>URL</h3>
                            <input type="text" id="url">
                            <button class="button" onclick="urlOnclick()">LOAD JSON</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
</body>
</html>
...