Подтвердить что ты не робот

Как я могу достичь разделения модели представления в компоненте Javascript для редактирования HTML?

Мне нужно создать редактор WYSI (более или менее) WYSI в браузере для определенного подмножества HTML. Это требует, чтобы элементы модели HTML могли быть украшены дополнительной разметкой для поддержки редактирования. Возможно, некоторые модели HTML-элементов должны быть полностью заменены с целью редактирования.

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

Вместо этого я хочу чистое разделение модели и представления, предпочтительнее использовать одну из клиентских структур MVC, таких как React.js.

Как это можно достичь? До сих пор я придумал следующие идеи:

  • Представьте модель в качестве DOM браузера и используйте ее в качестве модели для представлений, которые генерируют разметку (и поведение) редактора.
  • Представьте модель, используя React virtual DOM (возможно ли это?)
  • Сверните мое собственное представление модели, используя типы данных Javascript. Однако для этого потребуется написать синтаксический анализатор и сериализатор, которые мне очень хотелось бы избежать.

Как это делается в других компонентах редактора? Возможно ли, что этот подход возможен?

Изменить: Чтобы пояснить вариант использования немного больше, я не ищу архитектуру, которая поддерживает обычное редактирование встроенных элементов, таких как <strong>, <a> et al. но сочетание внутри-редактирования встроенных элементов (я рассматриваю возможность использовать что-то вроде CKEditor для этого), а также более структурные редактирование, например клонирование <div> и макет <table>, и перемещение их вокруг.

4b9b3361

Ответ 1

Если бы я правильно понял ваш вопрос, вы хотите отредактировать несколько элементов на одной странице?

В таком случае я бы рекомендовал написать этот "модуль" с помощью директив angularjs (проводной по атрибуту, поэтому ваша семантика в html такая же, как и вообще не применяется для документа). Эта директива (при загрузке приложения angular) должна изменять атрибуты "редактируемый", которые позволят пользователю редактировать содержимое элемента. Поэтому давайте скажем, что шаблон выглядит следующим образом:

<div id="title-editor" my-editor>...</div>

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

Вот рабочая директива, которая может быть кандидатом для ее просмотра https://github.com/TerryMooreII/angular-wysiwyg

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

Еще одно примечание: укажите имя каждого заполнителя, чтобы вы могли идентифицировать их на сервере и заполнить данные, если это так, или вы можете использовать jquery/ angular, чтобы заполнить их при загрузке страницы. Вопрос в том, каков ваш предпочтительный способ:)

На стороне сервера вы можете использовать redis или mongo для хранения этой логической структуры владельца места, а затем пытаться представить полную структуру DOM и воссоздавать ее каждый раз, когда читаете ее из бэкэнд. Так что было бы намного быстрее загрузить этот путь.

Для частей, которые вы хотите перетащить на страницу, вам понадобятся некоторые владельцы мест, но это другой "редактор", поэтому вам понадобится дополнительная директива angular. Вот еще один, который мог бы помочь получить информацию о том, как это сделать. http://codef0rmer.github.io/angular-dragdrop.

Объединив эти два, вы можете найти что-то близкое к тому, что вам нужно. Еще раз. Вы должны (как разработчик) отвечать за макет страницы, так как вам нужно определить, по крайней мере, где находятся верхние уровни (перетаскивание).

Ответ 2

Из того, что я понимаю, речь не идет о том, какую библиотеку использовать или какой шаблон. Это скорее основанная на ролях архитектура. Допустим, что одним из элементов, которые вы можете создать и отредактировать в своем редакторе, является тег <strong>. Конечные пользователи будут видеть

<strong class="some-class">Some content</strong>

Пока ваши редакторы будут видеть один и тот же элемент (следовательно, WYSIWYG), но с дополнительными стилями или элементами управления, скажем, он будет красным и будет иметь кнопку "удалить".

<strong class="some-class">Some content <sup>Remove</sup></strong>

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

.canvas strong{
    font-weight: bold;
}
.canvas strong sup{
    display:none;
}

.editing.canvas strong{
    color:red;
}
.editing.canvas strong sup{
    display:inline;
}

// your end users will see
<div class="canvas">
    // but the <sup> element will be display:none; by default
    <strong class="some-class">Some content <sup>Remove</sup></strong>
</div>

// your editors will see
<div class="canvas editing">
    // here, the 'editing' class will make the <sup> visible and change the color to red
    <strong class="some-class">Some content <sup>Remove</sup></strong>
</div>

Конечно, это простой пример, но вы можете увидеть, как более сложные элементы вписываются в эту архитектуру. Теперь для части js.

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

function StrongElement(){
    var id,
        el,
        data = {
           content: 'default content',
           classname: 'default classname'
        };

    function setId(id){
        id = id;
        render();
    }

    function setContent(content){
        data.content = content;
        render();
    }

    (function create(){
        // this is done only once!
        el = document.createElement('strong'); // this in more complex elements would be a template
        var remove = document.createElement('sup');
        remove.innerHTML = 'remove';
        el.appendChild(remove);
    })();

    function render(){
        el.id = id;
        el.className = data.classname;
        el.innerHTML = data.content;
        return el;
    }

    function onHover(){
        alert('Im strong and I like it');
    }

    function remove(){
        // more on editor later*
        if (editor.isInEditingMode()){
            editor.remove(id);
        }
    }

    // expose the public methods
    return {
        render: render
    };
}

Мы определили js 'class' для обработки любого взаимодействия этого элемента. Это может быть более сложным, и элемент может иметь вложенные элементы. Что дополнительно к общему плагину html, так это то, что эти элементы имеют id, а некоторые методы будут работать, только если существует переменная editor. Давайте посмотрим на редактор:

function Editor(){
    var canvas = document.getElementById('canvas'), // note that the canvas can be a text area, a div, or even a html5 canvas where your elements are rendered graphically instead of the DOM
        elements = [],
        editingMode = true;

    function addElement(element){
        // keep track of the js instance of the element
        elements.push(element);
        // give it an id that both the element and the editor know
        element.setId(elements.length);
        // append the graphic representation of the instance (some html tags) into the graphic representation of the editor
        canvas.appendChild(element.render());
        // we return the id so that anyone who added an element can keep track of it
        return elements.length; // the id, will be the index in the array of elements
    }

    function removeElement(id){
        // this could be way better but its just for demo purposes
        elements[id] = null;
    }

    function refresh(){
        // elements are already in the canvas, so we iterate over all of them 
        for (var i in elements){
            // and re render them
            elements[i].render();
        }
    }

    function setEditingMode(mode){
        editingMode = mode;
        if (editingMode){
             canvas.className = 'editing';
        }else{
             canvas.className = '';
        }
    }

    function isInEditingMode(){
        return editingMode;
    }

    return {
        isInEditingMode: isInEditingMode,
        addElement: addElement,
        removeElement: removeElement,
        refresh: refresh
    }
}

Это очень простой редактор, чтобы получить это на уровне готовой продукции, вам нужно будет иметь дело с позициями каждого элемента, в этом случае мы могли бы предположить, что элементы отображаются в том же порядке, в каком они были добавлены. Но это становится более интересным при представлении древовидной структуры или x, y и z позиций для элементов, нарисованных на холсте пикселей. Данные этой позиции будут добавлены к каждому элементу, и каждая функция рендеринга должна иметь возможность обрабатывать ее со ссылкой на родительский редактор.

Этот простой редактор будет работать примерно так:

<div id="canvas"></div>

var editor = new Editor(),
    element = new Strong();

element.setContent("I'm Stronger");
editor.addElement( element );
editor.refresh();
editor.setEditingMode(true); // this should add the 'editing' class and you should now see the remove button and red colored content
editor.setEditingMode(false); // this should return the element to a end user state and if you hover you should get an alert

Демо: http://jsfiddle.net/pzef52gh/1/

В принципе, путь, который я хотел бы сделать, - это дизайн и реализация элементов для конечного пользователя, включая стили и функциональные возможности. После этого я построил бы поверх этих элементов дополнительные стили и функции, когда вы находитесь в режиме редактирования. Вы можете сделать это на любом языке и в любой среде. Пример может показаться знакомым для пользователей backbonejs, но если бы мне пришлось сделать что-то подобное сегодня, я бы использовал angular, и каждый "элемент" был бы директивой с состоянием редактирования/без редактирования. Надеюсь, это поможет!

Ответ 3

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

Ответ 4

Не используйте внешние инструменты и создавайте свои собственные. Больше контроля, менее бесполезный код.

MVC: модель - это отредактированные данные, представление - отображаемый html, контроллер - SQL I/O и функции сохранения/редактирования.

В чем проблема с этим?

Быть методичным, понятным и менее ориентированным, вы преуспеете. И осваивайте его во всех экстентах.