Замена элемента виджетов на новую структуру DOM

<script>
(function( $ ) {

    $.widget( "my.dropbox", {
        errorText: function(text) {
            $(this.element).next().html(text);
        },

        _create: function() {
             var id = $(this.element).attr("id");
             var customDropbox = $(
                "<div class='form-group'>"+
                   "<label for='"+id+"'>"+getLabelFor(id)+"</label>"+
                   "<select id='"+id+"'></select>"+
                   "<div class='errors'></div>"+
                "</div>"
             );
             customDropbox.attr("id", id);

             $(this.element).replaceWith(customDropbox); // This removes original element from DOM

             populateOptions(id);
        },


    });

}( jQuery ));

$(document).ready(function(){
    $("#field1").dropbox(); //blank input field turns into a select with a label, populated options e.t.c..
    $("#button1").on("click", function(){
        $("#field1").dropbox("errorText", "This is a validation error message"); //throws an error saying dropbox is not initialized 
    });
});
</script>
<html>
     <body>
         <input id="field1" />
         <button id="button1">Press me</button>
     </body>
</html>

Поэтому я хочу, чтобы виджет с общедоступными методами заменил исходный элемент всеми связанными с ним данными виджетов. Проблема с приведенным выше кодом заключается в том, что элемент <select..> является всего лишь элементом DOM, и если вы вызываете .dropbox(..) на нем, он скажет, что виджет не инициализирован. Есть ли способ сделать элемент select в объекте виджета с помощью метода .errorText()? Все примеры виджета онлайн добавляют материал вокруг исходного элемента, но не заменяют его. Что касается большей картины, я пытаюсь создать универсальный инструмент для динамического конфигурирования форм. Это будет все <input id="..."> в html, но затем javascript будет запрашивать базу данных, получить конфигурацию для поля и превратить ее в dropbox, checkbox или, скажем, a date picker со всеми labels, validation, и другие звонки и свистки.

Ответ 1

Существует несколько проблем с кодом вашего виджета. Я попытаюсь их обобщить:

1. Скопируйте данные

Вы не копируете data во вновь созданный customDropbox, поэтому перед

this.element.replaceWith(customDropbox);

вы должны скопировать data:

customDropbox.data(this.element.data());

Теперь виджет будет помнить, что он был инициализирован.

2. этот элемент отсутствует

После

this.element.replaceWith(customDropbox);

вам следует обновить this.element, чтобы он указывал на вновь созданный customDropbox:

this.element = customDropbox;

3. Сообщение errorText принимает неправильный элемент

Так как элемент widgets (this.element) теперь указывает на элемент <div class='form-group'></div>, функция errorText должна быть слегка изменена на:

this.element.find(".errors").html(text);

4. id должен быть уникальным

Теперь оба обертки <div> и <select> имеют одинаковый идентификатор, который не разрешен в HTML, поэтому удалите тег в теге <select>. К счастью, <label> может работать без атрибута for, просто напишите его следующим образом:

<label>labelForId <select></select></label> 

Затем, чтобы получить элемент <select>, используйте this.element.find("select") в виджетах.

Боковое примечание

`this.element` is already a jQuery element, so no need for the additional `$()` wrapping.

Смотрите jsFiddle

Ответ 2

function show(){
   $("#field1").input({....});
}

function hide(){
    $("#field1").input("hide");
}

<button onclick="show()">show</button>
<button onclick="hide()">hide</button>

Ответ 3

Я думаю, что заменить исходный элемент, который исходный dropbox() не является хорошим решением,

потому что это заставит вас опираться на детали реализации jQuery ui factory,

легко сделать ошибку или ввести ошибки, иногда труднее для других людей понять ваш код

Если jquery ui factory изменит реализацию в будущем, вам придется изменить весь свой код, чтобы заставить его работать

(извините за мой лимит за понимание jquery ui)

Я думаю, мы можем поместить <input/> в контейнер и исходный dropbox() в контейнере, который запустил

замените <input/> на <select> datepicker..etc.. мы можем легко создавать модули, делая это:

<form>
  <div class="dropbox"><label for="someID">aaaaaa</label><input id="someID"/></div>
  <div class="datepicker"></div>
  <div class="othermodule"></div>
</form>

JS:

$(".dropbox").dropbox(); // init dropbox you defined 
$(".datepicker").datepicker(); // ...
$(".othermodule").othermodule(); // ...

$(".dropbox").dropbox("errorText", "error"); // invoke it smoothly

вот простая демонстрация: http://jsfiddle.net/m4A3D/

@Wouter Ответ Huysentruit предоставляет список хороших предложений для меня

<form>
  <div class="dropbox">
      <label for="someID">aaaaaa</label>
      <input id="someID"/>
  </div>
  <div class="datepicker"></div>
  <div class="othermodule"></div>
</form>
<button id="button1">Press me</button>



  <script>
  (function ($){
     $.widget("my.dropbox", {
        _create: function () {
            var $input = this.element.find("input");
            var sID = $input.attr("id");
            var $select = $("<select>");
            $select.attr("id", sID);
            $input.replaceWith($select);

            this.element.append("<div class='errors'></div>");
        }, // end _create()
        errorText: function (text) {
            this.element.find(".errors").text(text);
        } // end errorText()
    }); 
  }(jQuery));


$(".dropbox").dropbox();
$("#button1").click(function () {
    $(".dropbox").dropbox("errorText", "this is error");
});
  </script>