Статьи

Создание динамической системы редактирования контента с помощью jQuery UI

В этом уроке по Nettuts + мы узнаем, как создать динамическую систему редактирования контента с использованием фабрики виджетов пользовательского интерфейса jQuery. Мы рассмотрим, как разработать логический объектно-ориентированный виджет пользовательского интерфейса jQuery, преобразовать различные узлы в редактируемые текстовые поля, делегировать события в рамках виджета, управлять контекстом, переключать значки, сериализовывать данные и, конечно, редактировать, восстанавливать и удалять данные. используя фантастические функциональные возможности JQuery AJAX!



Первое, что мы сделаем, это создадим план для нашего виджета, который мы назовем «редактируемым» и чье пространство имен будет «ui». Имя виджета будет первым аргументом при вызове метода фабрики виджетов. Второй аргумент будет литералом объекта, содержащим различные свойства и методы для самого виджета. Это очень простой, чистый и эффективный способ создания плагинов. Для виджета пользовательского интерфейса есть ряд присущих ему свойств и методов. Мы будем использовать следующие ванильные реквизиты $.widget :

  • Этот литерал объекта является единственным свойством нашего виджета, которое будет доступно пользователям. Здесь пользователи смогут передавать свои аргументы виджету, который, конечно, переопределяет любые параметры по умолчанию, которые мы указываем. Примеры включают в себя обратные вызовы, селекторы элементов, классы элементов и т. Д.

  • Это первый метод, вызываемый во время инициализации виджета, и он вызывается только один раз. Это наш метод “установки”, в котором мы будем создавать / манипулировать элементами в DOM, а также связывать обработчики событий.

  • Этот метод является единственным общедоступным методом нашего виджета, в котором пользователи могут указать «уничтожить» в качестве аргумента для вызова виджета. Этот метод используется для разрушения / уничтожения созданного экземпляра виджета. Например, мы будем использовать команду destroy для удаления любого HTML-кода, сгенерированного нашим виджетом, отмены привязки обработчиков событий и, конечно, для уничтожения экземпляра виджета относительно элемента, для которого он был вызван.

1
2
3
4
5
6
7
8
$.widget(“ui.editable”, {
 
    options: {},
 
    _create: function(){},
 
    destroy: function(){}
}

Обратите внимание на подчеркивание, которое предшествует методу _create . Подчеркивания означают закрытый / внутренний метод, который недоступен извне виджета.

Для получения дополнительной информации о разработке виджетов пользовательского интерфейса, а также о других присущих методах, таких как _trigger (), _getData (), _setData (), вы можете проверить следующие полезные ресурсы:


Поскольку мы создаем виджет редактирования контента, нам, очевидно, потребуется какой-то способ редактирования контента. Кроме того, нам понадобится возможность удалять контент, а также отменять ревизии. Нам также понадобятся триггеры «редактирования», скажем значки, которые при нажатии будут переключать состояние нашего контента, а также состояние между значками по умолчанию и значками редактора. Имея в виду эту функциональность, давайте теперь определим некоторые общие методы:

  • Как правило, хорошо разделять логику логичным, понятным и понятным способом. Хотя большая часть создания и вставки DOM может выполняться внутри самого метода _create (), поскольку мы будем создавать редакторы для нескольких наборов содержимого (например, строк таблицы), давайте использовать независимый метод, который мы можем вызвать из цикла for! _CreateEditor () будет вызываться для обработки создания и вставки основных элементов редактора. Такое разделение логики должно облегчить понимание виджета, а также помочь нам поддерживать наш код в будущем.

  • Этот метод говорит сам за себя, это обработчик, который будет использоваться, когда пользователь нажимает на один из наших значков «редактора». Хотя мы можем привязать обработчик к каждому отдельному типу значков, давайте просто воспользуемся одним обработчиком и переключателем регистра для вызова соответствующих методов, основанных на том, какой значок был нажат. Существуют незначительные преимущества и недостатки, связанные с привязкой одного обработчика для всех значков и отдельных обработчиков для каждого значка, так что на самом деле это просто вопрос предпочтений и производительности.

  • Этот метод будет использоваться для изменения наших значков из состояния «по умолчанию» в состояние «редактор». Как уже упоминалось выше, нам понадобится возможность редактировать / удалять элемент, а также сохранять / отменять изменения. Нам нужно отображать только две иконки в любой момент времени (редактировать / удалять и сохранять / отменять соответственно), поэтому этот метод переключает наши иконки между этими двумя состояниями.

  • Вот где берется основная логика преобразования элементов туда и обратно из исходного состояния в редактируемые поля и наоборот. Этот метод будет использоваться, когда мы делаем элементы редактируемыми, сохраняем изменения или отменяем изменения. Этот метод также будет отвечать за заполнение / повторное заполнение элемента / ввода соответствующим текстом.

  • Этот метод будет отвечать за удаление элементов из DOM, когда элемент удаляется пользователем.

  • Поскольку мы будем сохранять / удалять данные с помощью мощных функций JJuery AJAX, нам понадобится способ преобразовать эти текстовые узлы и сформировать входные данные вместе с соответствующими им «именами полей» в стандартную строку в кодировке URL. Таким образом, этот метод будет обрабатывать сериализацию текстовых / входных значений.

  • Этот метод будет обрабатывать всю нашу логику AJAX. Он будет отвечать как за сохранение, так и за удаление данных, а его обратные вызовы будут использоваться для обновления DOM при необходимости. Кроме того, любые определенные пользователем обратные вызовы AJAX также будут запускаться отсюда.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$.widget(“ui.editable”, {
 
    options: {},
 
    _create: function(){},
 
    _createEditor:function(){},
 
    _clickHandler: function(event){},
 
    _showIconSet: function(){},
 
    _transformElements: function(){},
 
    _removeElements: function(){},
 
    _encode: function(){},
 
    _post: function(){},
 
    destroy: function(){}
 
});

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




Теперь давайте определим некоторые изменяемые пользователем опции для нашего виджета! Это всегда забавный шаг, потому что есть множество действительно интересных и изящных вещей, которые можно сделать здесь. Например, создание обратных вызовов. Мы можем создавать необязательные обратные вызовы для почти чего угодно, сохраняя данные, удаляя данные, ошибки, успех, завершение, события и т. Д. Кроме того, мы можем легко определить контекст этих обратных вызовов. Итак, хотите оживить цвет строки после успешного обновления? Нет проблем! Хотите, чтобы каждое поле мигало в быстрой последовательности в качестве визуального подтверждения? Проще простого! Для меня это одна из самых забавных и мощных функций любого виджета. Способность формировать функциональность и интерактивность в соответствии с потребностями пользователей.

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

1
<strong>options: {…}</strong>

Здесь мы определим некоторые основные параметры для нашего виджета, такие как селекторы элементов, имена классов, имена полей и так далее …

  • Это будет строка селектора для тех элементов, которые мы хотим сделать редактируемыми. Мы будем использовать селектор для элементов ячейки таблицы "td" в качестве селектора по умолчанию. Это может быть отменено, хотя. Любая допустимая строка селектора jQuery будет работать. Например:

    • «тд»
    • «тд> маленький»
    • “.editable-контент”
    • “. editable p”
    • «ДИВ: есть (р)»

    Для получения дополнительной информации о селекторах jQuery см. API селекторов jQuery.

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

    Таким образом, этот селектор очень важен. Мы будем перебирать каждый elementParent при создании, добавлении, связывании и кэшировании элементов! Как и в случае со свойством elements , любой действительный селектор jQuery будет работать здесь в контексте элемента, для которого был вызван виджет. В качестве значения по умолчанию мы будем использовать tbody tr чтобы выбрать все строки таблицы в теле таблицы.

    Примечание. Если вам интересно, почему мы не указываем родительский контейнер, выполняя что-то вроде этого:

    1
    this.element.parent();

    Учтите, что непосредственный родительский элемент не может быть общим родительским для всех редактируемых узлов для данной строки / блока. Что если редактируемые узлы вложены в другой элемент? Указывая неявный родительский контейнер, мы знаем, что имеем дело, в частности, с соответствующим контейнером верхнего уровня для всего редактируемого содержимого в данной строке / блоке.

  • Здесь мы позволим пользователю определить, какой метод вставки jQuery будет использоваться для добавления значков и их контейнера. insertAfter должна быть указана правильная строка метода вставки jQuery, например appendTo , prependTo , insertBefore или insertAfter . Метод вставки по умолчанию будет appendTo .

  • Разрешение пользователю указывать свои собственные / дополнительные имена классов открывает множество новых возможностей, таких как использование CSS-инфраструктуры, такой как jQuery UI CSS Framework, или, возможно, классы иконок из созданного ими спрайта изображения! Здесь пользователь может указать дополнительные имена классов в виде строки для ряда различных элементов. Мы установим некоторые классы по умолчанию для значков, используя классы значков jQuery UI Themeroller. Таким образом, classes будут литералом объекта со свойствами следующим образом:

    • iconContainer: строка, разделенная пробелами, представляющая дополнительные классы для родительского элемента значков
    • edit: разделенная пробелами строка, представляющая дополнительные классы для иконки редактирования
    • удалить: разделенная пробелами строка, представляющая дополнительные классы для значка удаления
    • save: разделенная пробелами строка, представляющая дополнительные классы для значка сохранения
    • cancel: строка, разделенная пробелами, представляющая дополнительные классы для значка отмены
    • th: разделенная пробелами строка, представляющая дополнительные классы для элемента заголовка таблицы, который будет создан условно

    Для получения дополнительной информации о CSS-структуре jQuery UI Themeroller см. API-интерфейс jQuery UI Themeroller, а также jQuery UI Themeroller.

  • Эта опция будет простым массивом, представляющим численно индексированные имена полей содержимого, которое делается редактируемым. Так, например, если редактируемый контент представляет собой поля «фамилия», «имя» и «адрес», мы указали бы имена этих полей в хронологическом порядке, порядке, в котором эти элементы отображаются в DOM, из first to last, [“фамилия”, “имя” “адрес”]. Например:

    1
    fieldNames: [“last-name”, “first-name”, “address”]

    Мы инициализируем пустой массив как значение по умолчанию.

    1
    fieldNames: []
  • Эта опция будет литералом объекта, который используется для указания свойства псевдодействия для наших запросов AJAX. Проще говоря, он будет содержать строки url, в которые будет отправляться $.ajax jQuery. Он будет содержать два свойства:

    • Строка URL, используемая при редактировании содержимого.

    • Строка URL, используемая при удалении содержимого.

    В качестве значений по умолчанию мы будем использовать свойство location.href javascript, которое является URL текущей страницы:

    1
    2
    3
    4
    action : {
        edit : location.href,
        remove : location.href
    }
  • Эта строка определяет тип запроса $.ajax мы будем использовать. По умолчанию используется метод post но пользователь может указать что-то еще, если возникнет такая необходимость.


Здесь мы определим несколько пустых функций / заполнителей для пользовательских обратных вызовов AJAX …

  • Этот необязательный обратный вызов будет вызываться в конце метода _create в контексте this.element .

  • Этот необязательный предварительный обратный вызов будет вызываться в начале метода destroy также в контексте this.element .

  • Этот необязательный обратный вызов будет вызван до того, как запрос AJAX начнется в контексте общего родительского объекта редактируемого содержимого. Это может быть использовано для ряда вещей, таких как, например, отображение GIF-файла загрузчика AJAX.

  • Этот необязательный обратный вызов будет вызван в случае ошибки в запросе AJAX в контексте общего родительского объекта редактируемого содержимого. С помощью этого обратного вызова вы даете пользователю обратную связь, сообщая ему, что его сообщение не завершено.

  • Этот необязательный обратный вызов будет вызван, опять же, в контексте общего родительского объекта редактируемого содержимого, если есть успешный запрос вызова AJAX. Это, как указано выше, может использоваться для ряда различных вещей, таких как уведомление пользователя о том, что запрос был успешным.

  • Этот необязательный обратный вызов будет вызван, когда запрос AJAX будет завершен – независимо от успеха или ошибки также в контексте общего родительского объекта редактируемого содержимого. Это может, например, использоваться для удаления GIF-файла загрузчика AJAX, означающего, что запрос завершен.


01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$.widget(“ui.editable”, {
 
    options: {
        elements : “TD”,
        elementParent : “TBODY TR”,
        insertMethod : “appendTo”,
        classes : {
            iconContainer : “”,
            edit : “ui-icon ui-icon-pencil”,
            remove : “ui-icon ui-icon-closethick”,
            save : “ui-icon ui-icon-check”,
            cancel : “ui-icon ui-icon-arrowreturnthick-1-w”,
            th: “”
        },
        fieldNames : [],
        ajaxType : “post”,
        action : {
            edit : location.href,
            remove : location.href
        },
        create : null,
        destroy : null,
        ajaxBefore : null,
        ajaxError : null,
        ajaxSuccess : null,
        ajaxComplete : null
    },
 
    _create: function(){},
 
    _createEditor:function(){},
 
    _clickHandler: function(event){},
 
    _showIconSet: function(){},
 
    _transformElements: function(){},
 
    _removeElements: function(){},
 
    _encode: function(){},
 
    _post: function(){},
 
    destroy(){}
 
});


Определив план нашего виджета, а также его параметры, нашим следующим шагом будет создание самого виджета! Этот метод вызывается только один раз, при инициализации виджетов.

В методе _create мы сначала определим количество внутренних реквизитов (читай: свойства), которые будут использоваться позже в виджете. По сути, мы будем кэшировать селекторы и логические значения, а также определять имена классов и тому подобное. Затем мы перейдем к созданию, вставке и кэшированию элементов, а также добавлению классов и привязке обработчика событий.

1
<strong>_create: function(){}</strong>

Как упоминалось выше, первым шагом в _create будет определение некоторых свойств. Давайте рассмотрим их по одному:

  • Это свойство будет селектором jQuery, представляющим элемент, который использовался для создания экземпляра виджета.

    1
    this._element = $(this.element),
  • Это логическое свойство будет использовать троичный оператор, чтобы определить, является ли this._element таблицей. Если это свойство имеет значение true, позже мы создадим элемент заголовка таблицы, чтобы количество столбцов в thead и tbody соответствовало.

    Для получения дополнительной информации о троичных / условных операторах см. Сеть разработчиков Mozilla или MDN.

    1
    this._table = (this._element.is(‘table’) ? true : false);

    Примечание. Вместо использования троичного здесь мы могли бы просто использовать выражение “this._element.is (‘table’)”, так как оно возвращает логическое значение. Здесь лучше просто кэшировать логическое значение, а не повторно вызывать метод is . Это также намного более читабельно.

  • Используя строку селектора jQuery из аргумента options, мы будем кэшировать селектор jQuery для общего родительского элемента редактируемого содержимого в контексте this._element

    1
    this._elementParent = $(this.options.elementParent, this._element),
  • Здесь мы инициализируем пустой массив, который вскоре будет заполнен коллекциями jQuery редактируемого содержимого относительно индекса их родительского контейнера. Ключ массива будет напрямую соответствовать индексу родительского элемента. Другими словами, _editable[0] будет коллекцией jQuery всего редактируемого содержимого в _elementParent[0] . Это отличный / быстрый способ кэширования редактируемых коллекций для последующего использования.

    1
    this._editable = [],
  • Это будет литерал объекта, содержащий классы “action” для наших значков редактора. Я называю их классами действий, поскольку эти классы будут использоваться для определения того, какое действие следует предпринять в нашем обработчике событий.

    При определении этих классов мы будем использовать другое свойство, предоставляемое каркасом виджета, this.widgetBaseClass . Это свойство всегда возвращает базовый класс для виджета, который в этом случае будет ui-editable .

    Итак, по сути, мы будем объединять widgetBaseClass с соответствующими именами классов для каждого из наших значков. Эти триггерные классы не зависят от любых других классов, которые могут иметь значки. Хотя они могут использоваться для стилизации значков с помощью css, они в основном используются в нашем обработчике событий.

    Для получения дополнительной информации о конкатенации строк см. Mozilla Develops Network.

    1
    2
    3
    4
    5
    6
    this._triggers = {
        edit : this.widgetBaseClass + “-edit”,
        remove : this.widgetBaseClass + “-remove”,
        save : this.widgetBaseClass + “-save”,
        cancel : this.widgetBaseClass + “-cancel”
    },
  • В отличие от свойства _triggers приведенного выше, this._classes также будет объектным литералом имен классов. Разница здесь в том, что _classes будет содержать объединение предопределенных и определенных пользователем имен классов.

    Используя тернарный оператор, мы будем объединять базовые классы с любыми необязательными классами, указанными в options.classes .

    1
    2
    3
    4
    5
    6
    7
    8
    9
    this._classes = {
        editable : this.widgetBaseClass + ‘-element’,
        iconContainer : this.widgetBaseClass + “-icons” + (this.options.classes.iconContainer ? ” ” + this.options.classes.iconContainer : “”),
        tableHeader : this.widgetBaseClass + “-header” + (this.options.classes.th ? ” ” + this.options.classes.th : “”),
        edit : this._triggers.edit + (this.options.classes.edit ? ” ” + this.options.classes.edit : “”),
        remove : this._triggers.remove + (this.options.classes.remove ? ” ” + this.options.classes.remove : “”),
        save : this._triggers.save + (this.options.classes.save ? ” ” + this.options.classes.save : “”),
        cancel : this._triggers.cancel + (this.options.classes.cancel ? ” ” + this.options.classes.cancel : “”)
    },

Теперь, когда мы определили ряд свойств, мы готовы добавить соответствующий класс в наш редактируемый контент.

Этот шаг настолько прост, насколько простым может быть. Мы будем использовать селектор jQuery для всего редактируемого контента в контексте this._elementParent . Мы определим селектор со строкой, данной нам в аргументе options.elements . Затем, используя метод addClass jQuery, мы добавим имя класса, которое мы кэшировали недавно в объекте _classes .

  • 1
    $(this.options.elements, this._elementParent).addClass(this._classes.editable);

Для получения дополнительной информации о методе addClass в jQuery см. API jQuery.


Иконки и их контейнеры будут создаваться, когда мы перебираем нашу коллекцию _elementParent .
Другими словами, каждый _elementParent будет иметь свой собственный набор иконок.
Мы также кешируем селекторы, в контексте, для этих значков, их контейнера и редактируемого содержимого относительно текущего _elementParent в цикле.

  • Перед тем как стать циклом for(){...} , мы инициализируем переменную для начального выражения, то есть итератор, который увеличивает цикл. Затем мы определим целое число, представляющее, сколько элементов кэшируется в коллекции _elementParent , используя метод size jQuery.

    1
    var i, total = this._elementParent.size();

    Для получения дополнительной информации о заявлениях см. Сеть разработчиков Mozilla.
    Для документации метода size jQuery см. API jQuery

  • Здесь мы будем _iconContainer пустой массив _iconContainer , присваивая ему возвращаемое значение метода _createEditor в качестве нового реквизита, i Как вы увидите, на мгновение _createEditor вернет селектор jQuery для вновь созданного контейнера значков.

    Таким образом, поскольку i равен индексу текущего _elementParent в нашем цикле, _iconContainer[i] будет контейнером для наших значков только внутри _elementParent[i] !

    При выполнении итерации мы будем использовать итератор, i в качестве ключа массива для нескольких коллекций jQuery, включая массив _icons массив _icons а также массив _iconContainer .

    1
    this._iconContainer[i] = this._createEditor(i);

    Примечание. Ключи массива для _editable , _iconContainer и _icons всегда будут соответствовать индексу _elementParent в текущей итерации цикла for .

  • _iconContainer массиву _iconContainer , теперь мы будем кэшировать селектор для всего редактируемого содержимого в контексте _elementParent[i] .

    Мы определим строку селектора редактируемого содержимого путем объединения a . с соответствующим именем класса, хранящимся в нашей _classes.editable prop.

    1
    this._editable[i] = $(‘.’ + this._classes.editable, this._elementParent[i]);
  • Последнее, что мы сделаем в нашем цикле for – это привязка обработчика к событию click наших вновь созданных значков. Так как мы связывали обработчик в контексте виджета / объекта пользовательского интерфейса jQuery, нам необходимо обратить особое внимание, поскольку контекст this в типичном обработчике событий jQuery относится к элементу, который получил событие. Это одна из самых приятных функций jQuery, поскольку она стандартизирует то, что происходит во всех браузерах. (Я смотрю на тебя IE;)

    Итак, вот два простых подхода к обработке контекста в обработчике события виджета.

    • Прежде всего, мы можем просто передать этот экземпляр нашего виджета обработчику в качестве аргумента event.data который предоставляется некоторыми из различных методов bind типа jQuery. По сути, второй аргумент, который обрабатывает вызов функции, может быть литералом объекта данных, которые мы хотим передать обработчику. В обработчике эти данные доступны в объекте event как свойство event.data ! Вот как это выглядит:

      1
      this._icons[i].bind(‘click’, {widget:this, i:i}, this._clickHandler);

      Таким образом, event.data.widget будет ссылкой на наш экземпляр виджета, а event.data.i будет ссылкой на наш итератор!

      Передача аргумента event.data – это здорово, если вы не хотите изменять контекст this в своем обработчике. Кроме того, он не требует другого вызова функции, как следующий метод, $.proxy .

    • Как указано в jQuery API, «этот метод наиболее полезен для присоединения обработчиков событий к элементу, контекст которого указывает на другой объект».

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

      Даже если целевой элемент больше не будет назначен this , он все равно будет доступен в объекте event jQuery как свойство event.target . Поскольку многие разработчики уже имеют привычку кэшировать селектор для $(this) в обработчиках событий, было бы не слишком сложно кешировать селектор $(event.target) вместо этого.

      Без лишних слов, вот метод $ .proxy в действии:

      1
      this._icons[i].bind(‘click’, $.proxy(this, this._clickHandler);
  • С пониманием как event.data и $.proxy , давайте использовать аргумент event.data для передачи нашего виджета и итератора в обработчик.

    1
    this._icons[i].bind(‘click’, {widget:this, i:i}, this._clickHandler);
01
02
03
04
05
06
07
08
09
10
11
12
var i, total = this._elementParent.size();
 
for(i=0; i<total; i++){
 
    this._iconContainer[i] = this._createEditor(i);
 
    this._editable[i] = $(‘.’ + this._classes.editable, this._elementParent[i]);
 
    this._icons[i].bind(‘click’, {widget:this, i:i}, this._clickHandler);
 
};

С иконками, созданными, вставленными и связанными, нам нужно сделать небольшую корректировку, если this.element является таблицей.

Хотя мы еще не определили метод _createEditor , тип _createEditor элемента контейнера значков соответствует this.element .

Таким образом, если вы работаете за столом, он создаст td и добавит его в таблицу. Если бы работал любой другой тип элемента, он создаст div .

Таким образом, добавление еще одного td к строке таблицы оставляет нам неполный заголовок таблицы, поскольку в теле будет больше ячеек таблиц, чем в заголовке таблицы.

Итак, нам нужно проверить, является ли this.element таблицей или нет, и если это так, мы создадим, insertMethod и insertMethod , используя insertMethod указанный в аргументе options , элемент th . Хотя создание DOM-элементов с помощью jQuery простое и чистое, мне иногда нравится писать его в простом javascript. Давайте посмотрим на логику:

  • Сначала мы проверим наше свойство table которое, как вы помните, является логическим значением, представляющим, является ли this.element таблицей.

    1
    2
    3
    if(this._table){
     
    }
  • Если this.element является таблицей, нам нужно создать и добавить новый th в первую и, надеюсь, единственную строку таблицы, вот как мы это сделаем:

    • Если this.element является таблицей, мы создадим новый элемент th в качестве переменной th .

      1
      2
      3
      if(this._table){
          var th = document.createElement(‘th’);
      }
    • Далее мы установим свойство className для этого элемента как tableHeader класса tableHeader указанное в объекте _classes .

      1
      2
      3
      4
      if(this._table){
          var th = document.createElement(‘th’);
              th.className = this._classes.tableHeader;
      }
    • Затем мы установим innerHtml , который в данном случае является просто текстом, этого элемента.

      1
      2
      3
      4
      5
      if(this._table){
          var th = document.createElement(‘th’);
              th.className = this._classes.tableHeader;
              th.innerHTML = “Edit”;
      }
    • Мы определим одно правило CSS, text-align как center . Это отцентрирует текст заголовка и выглядит хорошо, так как сами значки также будут центрированы через css.

      1
      2
      3
      4
      5
      6
      if(this._table){
          var th = document.createElement(‘th’);
              th.className = this._classes.tableHeader;
              th.innerHTML = “Edit”;
              th.style.textAlign = “center”;
      }
    • Наконец, используя jQuery insertMethod, указанный пользователем, мы динамически вставим элемент в заголовок таблицы нашей таблицы. Обратите внимание на нашу вставку DOM:

      1
      2
      3
      4
      5
      6
      7
      8
      if(this._table){
          var th = document.createElement(‘th’);
              th.className = this._classes.tableHeader;
              th.innerHTML = “Edit”;
              th.style.textAlign = “center”;
       
          $(th)[this.options.insertMethod](‘thead tr:first’, this._element)
      }

      Вы заметили, что у нас есть метод вставки в квадратных скобках сразу после селектора jQuery?

      Вам может быть интересно, что это такое и как это работает. По сути, думайте о jQuery как об одном большом (удивительном) объекте. В качестве объекта вы можете использовать ассоциативную нотацию с массивом / квадратной скобкой! Этот сокращенный вариант дает нам большую гибкость в динамическом вызове методов в зависимости от условий!


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

Существует множество причин для использования обратного вызова в методе _cerate. Вот один пример!

Допустим, пользователь с обратным вызовом об успешном изменении цвета ячеек таблицы отражает успешное обновление. Если виджет будет уничтожен, им понадобится способ восстановить цвет ячеек таблицы. Конечно, они могли бы неявно установить цвет вручную в обратном вызове destroy, но как насчет сохранения цвета ячейки таблицы с помощью метода $.data jQuery, который является гораздо более динамичным подходом! Эти данные будут прикреплены к этому this.element как это будет контекстом нашего обратного вызова.

1
2
3
4
5
6
create: function(){
  
    var color = $(“td”, this).css(“color”);
    $.data(this, “color”, color);
  
}

Таким образом, когда виджет уничтожен, пользователь может просто установить цвет обратно к значению по умолчанию, открыв соответствующий $ .data

1
2
3
4
5
6
destroy: function(){
  
    var color = $.data(this, “color”);
    $(“td”, this).css(“color”, color);
  
}

Поскольку определяемые пользователем обратные вызовы являются необязательными, их значения по умолчанию всегда будут равны null .

Таким образом, мы будем использовать метод isFunction jQuery, чтобы определить, является ли тип обратного вызова function а затем вызвать его, если это так.

В качестве альтернативы, мы могли бы использовать собственный оператор typeof javascript, чтобы определить, является ли обратный вызов typeof функцией. Мы должны были бы заключить оператор typeof в parens, чтобы мы могли взять его возвращаемое значение и преобразовать его в нижний регистр, используя собственный метод toLowerCase javascript, чтобы стандартизировать вывод. Тем не менее, гораздо проще использовать метод isFunction jQuery, но мы рассмотрим эту технику в vanilla javascript:

  • 1
    if((typeof this.options.create).toLowerCase() === “function”){…}

Вот как мы сделаем это с помощью jQuery:

1
if($.isFunction(this.options.create)) this.options.create.call(this.element);

Итак, если обратный вызов является функцией, мы выполним ее, используя собственный метод call javascript.

Метод call позволяет нам определять контекст наших обратных вызовов. Другими словами, он будет определять, к чему this относится в функции обратного вызова.

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



В предыдущем разделе мы обнаружили логику того, как мы будем создавать элементы. Теперь давайте посмотрим на реализацию. Метод _createEditor будет вызываться изнутри методов _create for цикла, чтобы выполнить все создание и вставку элемента относительно индекса текущего _elementParent в цикле.

1
<strong>_createEditor: function(i){}</strong>

Метод _createEditor принимает один аргумент i , который равен индексу текущего _elementParent из методов _create for цикла.


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

1
var edit, remove;

С этими инициализированными переменными давайте продолжим и создадим наши иконки, используя простой JavaScript! По сути, мы будем использовать те же методы, что и для условного заголовка таблицы, но с одним исключением. Вместо того, чтобы устанавливать innerHtml ссылки, которую мы создаем, мы вместо этого установим атрибут title. Таким образом, при наведении курсора на пользователя мы увидим описание всплывающей подсказки о том, что делает иконка! Вот посмотрите на наше создание якоря / иконки:

1
2
3
4
5
6
7
edit = document.createElement(‘a’);
edit.className = this._classes.edit;
edit.title = ‘Edit’;
 
remove = document.createElement(‘a’);
remove.className = this._classes.remove;
remove.title = ‘Remove’;

Когда значки готовы к работе, нам нужно создать для них контейнер. Тип элемента этого контейнера будет зависеть от того, работаем мы с таблицей или нет. Таким образом, используя логическое значение _table мы определили в методе _create и троичный оператор, мы создадим элемент td если будем работать с элементом table и div если мы будем работать с чем-то еще. Затем мы установим className контейнера, используя класс _iconContainer мы определили в объекте _classes .

1
2
_iconContainer[i] = document.createElement( (this._table) ? ‘td’ : ‘div’);
_iconContainer[i].className = this._classes.iconContainer;

Теперь мы начнем _icons массив _icons селектором jQuery для вновь создаваемых значков, мы передадим эти значки в виде массива.

Ключом массива для этой коллекции будет i , поэтому _icons[i] будет представлять все значки, содержащиеся в _elementParent[i] .

1
this._icons[i] = $([edit,remove]);

Теперь мы скомпонуем несколько методов jQuery вместе, чтобы установить HTML _iconContainer[i]и добавить его в DOM.

Во-первых, используя htmlметод jQuery , мы установим html _iconContainer[i]для this._icons[i].

Затем, добавив к значкам наши значки _iconContainer[i], мы динамически добавим их в DOM, используя options.insertMethodстроку в квадратных скобках / ассоциативный массив.

Наконец, так как большинство методов jQuery используют returnфактический селектор jQuery, используемый в самом выражении, это выражение будет return $(iconContainer[i]).

Таким образом, то, this._iconContainer[i]что определяется в цикле _createметода for, будет равно $(iconContainer[i]).

1
return $(_iconContainer[i]).html(this._icons[i])[this.options.insertMethod](this._elementParent[i]);

Для получения дополнительной информации о возврате значений из функции, проверьте Сеть разработчиков Mozilla

Документацию по методу jQuery html можно найти в API jQuery.



Теперь, когда наш виджет создан и готов к работе, пришло время справиться с его поведением. Логика этого виджета такова, что обработчик щелчков будет служить своего рода «контроллером» в том смысле, что он будет определять, какое действие необходимо предпринять, основываясь на классе значка, получившего щелчок.

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

1
<strong>_clickHandler: function(event){}</strong>

_clickHandlerМетод принимает один аргумент, event. Указав здесь пустой аргумент, jQuery автоматически назначит eventобъект этому аргументу. Объект события содержит ряд очень полезных свойств , таких как target, timeStamp, preventDefaultи , stopPropagationчтобы назвать несколько. Мы будем использовать пару таких методов / методов в нашем обработчике событий.


Первое, что мы сделаем в нашем обработчике событий, это вызов preventDefaultметода eventобъекта jQuery . Этот метод предотвратит любые действия по умолчанию в результате события. Для нас это означает, что когда пользователь нажимает на один из значков, который на самом деле является якорем, preventDefaultостановит запуск любого типа навигации / прокрутки – довольно изящно, а?

1
event.preventDefault();

Сначала мы кэшируем селектор jQuery для this, который, конечно, является элементом, который получил событие.

Далее мы определим переменную, iкоторая была передана нашему обработчику в event.dataЭто был итератор, используемый в цикле _createметода forдля кэширования селекторов для различных элементов относительно _elementParent[i].

Затем мы определим переменную widget, присвоив evet.data.widgetей ссылку на текущий экземпляр нашего виджета.

Теперь мы определим своего рода переменную быстрого доступаtriggers как widget._triggersсвойство. Это свойство содержит все классы триггеров наших значков, которые мы будем использовать, чтобы определить, какие действия предпринять.

Наконец, мы инициализируем переменные, dataи inputs. Эти переменные будут использоваться только тогда, когда нам нужно сериализовать данные для запросов AJAX.

1
2
3
4
5
var target = $(this),
     i = event.data.i,
     widget = event.data.widget,
     triggers = widget._triggers,
     data, inputs;

Оператор switchочень похож на ifоператор в том, что он вычисляет выражение и выполняет определенный код, только если выполняется условие. В некоторых случаях он может быть чище / легче для чтения. Тем не менее, у каждого утверждения есть свои преимущества и недостатки, но оно будет уместным при соблюдении надлежащих условий.

Мы будем оценивать четыре условия на основе switchметки операторов, которую мы установим как true. Мы собираемся проверить класс иконки, используя hasClassметод jQuery, который возвращает a boolean. Если значок имеет имя класса, указанное в аргументе, он будет return true.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
switch(true){
 
    case(target.hasClass(triggers.edit)):
        break;
 
    case(target.hasClass(triggers.remove)):
        break;
 
    case(target.hasClass(triggers.save)):
        break;
 
    case(target.hasClass(triggers.cancel)):
        break;
 
    default:
        break;
};

Как вы можете видеть, это простой и понятный способ оценить, какая иконка была нажата. Очевидно, что есть несколько разных способов сделать это, и ifзаявление будет работать так же хорошо. Теперь давайте подробнее рассмотрим каждый случай и то, что мы будем делать!

  • Если щелкнуть по значку редактирования, нам нужно, во-первых, переключить значки по умолчанию («Редактировать» и «Удалить») на значки редактора «Сохранить» и «Отмена». Итак, мы будем называть _showIconSetметод.

    Вы можете вспомнить этот метод из проекта виджета, который мы создали ранее. Хотя мы еще не определили его, он принимает два аргумента, iкоторые будут использоваться для выбора значков, которые мы будем переключать и editor. В editorаргумент определяет , какие IconSet , чтобы показать, по умолчанию или редактора.

    Как только мы определили больше методов нашего виджета, вы увидите, что большинство из них попадают в соответствие с jQuery, как обычно return this. В результате мы объединяем большинство этих методов вместе. Итак, давайте добавим еще один метод к этому выражению transformElements.

    transformElementsэто своего рода рабочая лошадка в том смысле, что она отвечает за то, чтобы сделать контент редактируемым, восстановить его в прежнее состояние, а также обновить отредактированный контент. Этот метод также принимает два аргумента, iкоторые, опять же, мы будем использовать для выбора соответствующей _editableколлекции, и i, typeкоторый будет определять тип «преобразования». Вот посмотрите:

    1
    2
    3
    case(target.hasClass(triggers.edit)):
        widget._showIconSet(i, "editor")._transformElements(i, "edit");
        break;
  • Если щелкнуть значок удаления, нам нужно удалить этот редактируемый контент как из DOM, так и из базы данных.

    Мы, конечно, будем использовать AJAX-запрос для удаления этого элемента из базы данных, но, поскольку мы не отправляем здесь форму и у нас нет никаких входных данных, которые автоматически сериализуются при отправке, нам потребуется способ сериализации / кодирования текста этих элементов в допустимую строку URL.

    Итак, мы вызовем _encodeметод нашего виджета для сериализации этих данных. Мы передадим один аргумент _encodeселектору jQuery для удаляемых элементов. _encodeбудет сериализовать текст этих элементов для нас и вернет правильную строку URL. Итак, мы определим переменную dataкак это значение.

    Наконец, мы вызовем _postметод нашего виджета, который отправит запрос AJAX на сервер. Мы передадим три аргумента _post, сериализованные данные, переменную, которая указывает, сохраняем ли мы или удаляем данные, и iкоторая будет использоваться в $.ajaxобратном вызове успеха для фактического удаления удаленных элементов из DOM.

    1
    2
    3
    4
    case(target.hasClass(triggers.remove)):
        data = widget._encode(widget._editable[i]);
        widget._post(data, "remove", i)
        break;
  • Если щелкнуть значок сохранения, нам нужно будет сериализовать наши входные данные, как мы делали выше, используя _serializeметод. Затем нам нужно вызвать метод post нашего виджета, чтобы отправить запрос AJAX на сервер.

    Таким образом, мы определим две переменные здесь, inputsи data. inputsбудет определен как набор входных данных jQuery в контексте этого элемента и dataбудет returnзначением _encodeметода, как указано выше.

    Наконец, мы вызовем _postметод с его тремя аргументами: сериализованным data, типом поста, который мы создаем (в данном случае «save») и iкоторый используется в $.ajaxобратном вызове success для обновления соответствующих элементов в DOM.

    1
    2
    3
    4
    5
    case(target.hasClass(triggers.save)):
        inputs = $('input', widget._editable[i]);
        data = widget._encode(inputs);
        widget._post(data, "save", i)
        break;
  • Наконец, если щелкнуть значок отмены, мы вернем значки и редактируемый контент в состояние по умолчанию.

    Для этого мы сначала вызовем _showIconSetметод с двумя аргументами, iи default. iбудет использоваться, чтобы выбрать соответствующую коллекцию из нашего _iconsмассива и defaultуказать, какой значок установить для отображения.

    Далее мы будем связывать _transformElementsметод с этим выражением одним аргументом, iкоторый, как обычно, используется для выбора соответствующей коллекции из нашего _editableмассива.

    1
    2
    3
    case(target.hasClass(triggers.cancel)):
        widget._showIconSet(i, "default")._transformElements(i, "restore");
        break;

    Примечание. Если вы не совсем уверены в том, что происходит в вызываемых нами методах, это нормально. Мы еще не определили эти методы, поэтому нет причин, по которым вы действительно должны понимать. Держитесь крепко, мы скоро определим эти методы.

  • 01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    switch(true){
     
        case(target.hasClass(triggers.edit)):
            widget._showIconSet(i, "editor")._transformElements(i, "edit");
            break;
     
        case(target.hasClass(triggers.remove)):
            data = widget._encode(widget._editable[i]);
            widget._post(data, "remove", i);
            break;
     
        case(target.hasClass(triggers.save)):
            inputs = $('input', widget._editable[i]);
            data = widget._encode(inputs);
            widget._post(data, "save", i);
            break;
     
        case(target.hasClass(triggers.cancel)):
            widget._showIconSet(i, "default")._transformElements(i, "restore");
            break;
        default:
            break;
    }

Вот как _clickHandlerвыглядит завершенный метод теперь, когда мы его определили:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
_clickHandler: function(event){
 
    event.preventDefault();
 
    var target = $(this),
         i = event.data.i,
         widget = event.data.widget,
         triggers = widget._triggers,
         data, inputs;
 
    switch(true){
 
        case(target.hasClass(triggers.edit)):
            widget._showIconSet(i, "editor")._transformElements(i, "edit");
            break;
 
        case(target.hasClass(triggers.remove)):
            data = widget._encode(widget._editable[i]);
            widget._post(data, "remove", i);
            break;
 
        case(target.hasClass(triggers.save)):
            inputs = $('input', widget._editable[i]);
            data = widget._encode(inputs);
            widget._post(data, "save", i);
            break;
 
        case(target.hasClass(triggers.cancel)):
            widget._showIconSet(i, "default")._transformElements(i, "restore");
            break;
        default:
            break;
    }
 
}


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


Используя удивительные возможности jQuery для создания цепочек, давайте посмотрим, как мы можем изменить состояние значка с помощью одной переменной и оператора!

  • Прежде всего, собирались определить новую переменную, titlesкак массив, используя троичный оператор.

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

    Мы проверим iconSetаргумент, чтобы определить, какие значки мы отображаем, и соответствующим образом заполним массив соответствующими заголовками.

    1
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
  • Теперь, используя, iмы выберем соответствующую коллекцию элементов из нашего _iconsмассива.

    1
    2
    3
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
     
    this._icons[i]
  • eqМетод используется в качестве фильтра против коллекции JQuery в том , что он возвращает элемент с указанным индексом. Таким образом, вызов eq(0)в контексте _icons[i]метода сузит наш выбор до иконки с индексом 0 в этой коллекции. Другими словами, первая иконка.

    1
    2
    3
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
     
    this._icons[i].eq(0)

    Для получения дополнительной информации о eqметоде jQuery, проверьте API jQuery.

  • Используя toggleClassметод jQuery , мы добавим и удалим классы для наших иконок соответственно. Таким образом, если указанный класс присутствует, он будет удален. И наоборот, если указанный класс отсутствует, он будет добавлен.

    Мы объединяем соответствующие классы для каждого значка с пробелом, так как toggleClassметод принимает несколько имен классов в виде списка, разделенного пробелами. Довольно изящно!

    1
    2
    3
    4
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
     
    this._icons[i].eq(0)
        .toggleClass(this._classes.edit + ' ' + this._classes.save)

    Документацию по toggleClassметоду jQuery можно найти в jQuery API.

  • Теперь, когда классы нашего первого значка установлены, мы определим его атрибут title с помощью attrметода jQuery . Мы установим заголовок, используя соответствующее значение, хранящееся в titleмассиве, который мы создали всего минуту назад. Ключи массива для titleмассива соответствуют индексу иконки, выбранной в данный момент, поэтому заголовок для иконки доступен как titles[0].

    1
    2
    3
    4
    5
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
     
    this._icons[i].eq(0)
        .toggleClass(this._classes.edit + ' ' + this._classes.save)
        .attr('title', titles[0])

    Чтобы прочитать о attrметоде jQuery , проверьте API jQuery

  • endМетод jQuery используется для удаления самого последнего фильтра, примененного к коллекции. Позвонив endсюда, мы удалим eqфильтр из нашей коллекции. Другими словами, мы будем работать с_icons[i] again.

    1
    2
    3
    4
    5
    6
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
     
    this._icons[i].eq(0)
        .toggleClass(this._classes.edit + ' ' + this._classes.save)
        .attr('title', titles[0])
    .end()

    Документацию по endметоду jQuery можно найти в jQuery API.

  • Мы снова будем использовать метод eQ jQuery, чтобы указать второй значок. Второй значок будет иметь индекс, равный единице, поскольку индикаторы начинаются с нуля, и поэтому мы будем использовать его eq(1)для выбора.

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

    1
    2
    3
    4
    5
    6
    7
    8
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
     
    this._icons[i].eq(0)
        .toggleClass(this._classes.edit + ' ' + this._classes.save)
        .attr('title', titles[0])
    .end().eq(1)
        .toggleClass(this._classes.remove + ' ' + this._classes.cancel)
        .attr('title', titles[1]);
  • Последнее, что мы сделаем здесь, это return thisчтобы не нарушать цепочку методов.

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
     
    this._icons[i].eq(0)
        .toggleClass(this._classes.edit + ' ' + this._classes.save)
        .attr('title', titles[0])
    .end().eq(1)
        .toggleClass(this._classes.remove + ' ' + this._classes.cancel)
        .attr('title', titles[1]);
     
    return this;

Теперь, когда мы определили нашу переменную и выражение, давайте теперь посмотрим на весь _showIconSetметод:

01
02
03
04
05
06
07
08
09
10
11
12
13
_showIconSet: function(i, iconSet){
 
    var titles = (iconSet === "default") ? ['Edit','Remove'] : ['Save','Cancel'];
 
    this._icons[i].eq(0)
        .toggleClass(this._classes.edit + ' ' + this._classes.save)
        .attr('title', titles[0])
    .end().eq(1)
        .toggleClass(this._classes.remove + ' ' + this._classes.cancel)
        .attr('title', titles[1])
 
    return this;
},


В этом методе мы будем перебирать редактируемый контент, чтобы получить / установить соответствующую строку text / html для этого элемента относительно типа преобразования, которое мы делаем. Затем мы просто установим html элемента.

После цикла, как обычно, мы будем return this!


Этот метод принимает два аргумента iи type.

i, Будет использоваться , чтобы выбрать соответствующую коллекцию JQuery из _editableмассива относительно _iconContainer[i]от _createметода.

typeАргумент просто определяет тип преобразования , который вызывается. Этот метод обрабатывает три типа преобразования :

  • Создание содержимого для редактирования и сохранение текущих значений для возможного поиска
  • Восстановление редактируемого содержимого до состояния по умолчанию с исходными значениями
  • Восстановление редактируемого содержимого до состояния по умолчанию с новыми значениями

Таким образом, тип будет либо «редактировать», «восстанавливать» или «обновлять».

1
_transformElements: function(i, type){},

Во-первых, нам нужно определить переменную, которая ссылается на options.fieldNamesмассив.

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

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

1
var fieldNames = this.options.fieldNames;

Используя $.eachметод jQuery , мы проведем итерацию _editable[i]. Мы будем передавать пустой аргумент, indexк $.each. Числовой индекс каждого элемента в цикле будет присвоен этому аргументу.

1
this._editable[i].each(function(index){...});

В рамках $.each, мы определим / инициализируем еще две переменные соответственно:

  • Хорошо инициализируйте htmlпеременную здесь, в области действия $.each. Эта переменная будет содержать HTML-строку, если мы делаем контент редактируемым, и текстовую строку, если мы обновляем или восстанавливаем контент.

    1
    2
    3
    4
    5
    this._editable[i].each(function(index){
     
        var html;
     
    });
  • Мы будем кэшировать селектор jQuery для thisселектора текущего элемента в цикле.

    1
    2
    3
    4
    5
    this._editable[i].each(function(index){
     
        var html, self = $(this);
     
    });

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

  • Если дело обстоит так edit, мы сделаем этот элемент редактируемым. В результате нам нужно сделать три вещи:

    • Прежде всего, мы определим новую переменную valкак значение thisэлемента text.

      1
      2
      3
      4
      switch (type){
          case "edit":
              var val = self.text();
              break;
    • Используя $.dataметод jQuery , мы прикрепим valк thisэлементу! Этот $.dataметод является фантастическим способом связать данные с элементом. Для настройки данных нам нужно указать три аргумента element, keyи value.

      1
      2
      3
      4
      5
      switch (type){
          case "edit":
              var val = self.text();
              $.data(this, 'value', val);
              break;

      Для получения дополнительной информации о методе $ .data в jQuery обратитесь к API jQuery.

    • Наконец, мы определим htmlvar как строковое представление входного элемента. Эта строка будет использоваться как для создания, так и для внедрения этого элемента с помощью всего одного вызова html-метода jQuery.

      Мы установим атрибут имени для этого ввода путем объединения соответствующего значения из fieldNamesмассива в строку html. Здесь мы будем использовать indexаргумент, так как он представляет индекс текущего элемента в цикле. Таким образом, если вы работаете с _editable[0]соответствующим именем поля, оно должно быть сохранено в fieldNames[0]!

      Таким же образом мы будем объединять valв строку атрибут входного значения.

      1
      2
      3
      4
      5
      6
      switch (type){
          case "edit":
              var val = self.text();
              $.data(this, 'value', val);
              html = '<input type="text" name="' + fieldNames[index] + '" value="' + val + '">';
              break;
  • Если typeоценивать restore, нам нужно только получить текст для этого элемента.

    • Получить $ .data так же просто, как установить его. Нам нужно только вызвать метод $ .data с двумя аргументами elementи key.

      1
      2
      3
      case "restore":
          html = $.data(this, 'value');
          break;
  • Если typeаргумент оценивается как updateмы определим нашу htmlпеременную как обновленное значение из valueатрибута этого элемента input.

    Затем, $.dataснова используя метод jQuery , мы обновим значение, которое мы прикрепили к этому элементу, чтобы он отражал новое значение!

    • Используя attrметод jQuery . мы определим htmlvar как атрибут значения для ввода.

      1
      2
      3
      4
      case "update":
          html = $("input", self).attr('value');
          $.data(this, 'value', html);
          break;
    • У нас будет переключатель по умолчанию для возврата в случае, если аргумент типа как-то не вычисляется в одну из соответствующих строк.

      1
      2
      default:
          return;

Определив все наши случаи, мы рассмотрим весь оператор switch

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
switch (type){
        case "edit":
            var val = self.text();
            $.data(this, 'value', val);
            html = '<input type="text" name="' + fieldNames[index] + '" value="' + val + '">';
            break;
 
        case "restore":
            html = $.data(this, 'value');
            break;
 
        case "update":
            html = $("input", self).attr('value');
            $.data(this, 'value', html);
            break;
 
        default:
            return;
    };

Используя htmlметод jQuery , мы теперь установим строку, которую мы определили как html для thisэлемента.

1
self.html(html);

$.eachЗакончив наш цикл, мы готовы завершить этот метод. Мы сделаем это, вернув, thisчтобы этот метод был цепным.

1
return this;

Вот и все, вот посмотрите на завершенный метод _transformElements:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
_transformElements: function(i, type){
 
    var fieldNames = this.options.fieldNames;
 
    this._editable[i].each(function(index){
 
        var html, self = $(this);
 
        switch (true){
            case(type === "edit"):
                var val = self.text();
                $.data(this, 'value', val);
                html = '<input type="text" name="' + fieldNames[index] + '" value="' + val + '">';
                break;
 
            case(type === "restore"):
                html = $.data(this, 'value');
                break;
 
            case(type === "update"):
                html = $("input", self).attr('value');
                $.data(this, 'value', html);
                break;
 
            default:
                return;
        };
 
        self.html(html);
    });
 
    return this;
},



_removeElementsМетод будет вызываться только тогда , когда пользователь успешно удалены данные с запросом AJAX. Другими словами, этот метод будет обратным вызовом для успешного удаления AJAX.


Этот метод принимает только один аргумент, iкоторый будет использоваться для выбора соответствующего селектора для удаляемого _elementParentнами.

1
<strong>_removeElements: function(i){...},</strong>

Сначала мы укажем, что _elementParentмы удаляем, используя iквадратную скобку / обозначение ассоциативного массива. Это даст нам элемент с индексом, iа не селектором jQuery. Итак, мы сделаем новый выбор jQuery из этого выражения.

Мы могли бы также легко использовать jQuery getили eqметоды здесь, не делая новый выбор jQuery. Однако, в соответствии с соглашением о кодировании, которое мы использовали, давайте просто сделаем следующее:

1
2
3
4
5
_removeElements: function(i){
 
    $(this._elementParent[i])
 
},

Для получения дополнительной информации о методе get jQuery, проверьте API jQuery

Документацию по методу eQ в jQuery можно также найти в API jQuery.


Используя removeметод jQuery , мы удалим эти элементы из DOM!

1
2
3
4
5
_removeElements: function(i){
 
    $(this._elementParent[i]).remove();
 
},

Документацию по методу удаления в jQuery можно найти в jQuery API


Наконец, мы будем, return thisчтобы не разорвать цепь.

1
2
3
4
5
6
7
_removeElements: function(i){
 
    $(this._elementParent[i]).remove();
 
    return this;
 
},


_encodeМетод вызывается непосредственно перед запроса AJAX. Используя jQuery serializeи $.paramметоды, _encodeсериализует пары ключ / значение и возвращает строку в кодировке URL.


Этот метод принимает один аргумент, inputsпредставляющий коллекцию jQuery элементов, которые мы хотим сериализовать.

1
_encode: function(inputs){...},

Используя isметод jQuery , который возвращает логическое значение, мы проверим inputsаргумент против "input"селектора. Если имеют дело с входами, isбудет return trueи как таковой, мы будем называть то JQuery в serializeметод на этой коллекции. serializeпринимает форму или набор входных данных и возвращает закодированную в URL строку пар ключ / значение.

1
2
3
4
5
_encode: function(inputs){
 
    if(inputs.is("input")) return inputs.serialize();
 
},

Для получения дополнительной информации о isметоде jQuery , проверьте API jQuery

документацию по serializeметоду jQuery можно также найти в API jQuery


Если inputsколлекция не состоит из элементов ввода, мы создадим литерал объекта, состоящий из пар ключ-значение, представляющих, своего рода, псевдо-имя и атрибуты значения.

Как только мы создали этот объект, мы можем передать его jQuery $.paramв качестве аргумента. $.paramбудет сериализовать массив или объект и вернет строку, закодированную в URL, в отличие от serializeметода jQuery .

  • Давайте инициализируем переменную dataпустым объектом. Мы заполним этот объект через мгновение, пока будем перебирать наш inputs.

    1
    var data = {},
  • Далее мы определим переменную fieldNamesкак ссылку на this.options.fieldNames. Делая это, мы делаем this.options.fieldNamesдоступной внутреннюю функцию этого метода $.each. fieldNamesтакже служит своего рода сокращенным способом доступа к options.fieldNamesмассиву.

    1
    2
    var data = {},
        fieldNames = this.options.fieldNames;
  • Используя $.eachметод jQuery , мы будем итерировать inputs. Опять же, мы передаем пустой аргумент index, чтобы inputs. В результате индекс текущего элемента в цикле будет присвоен index. Далее, логика нашего цикла!

    1
    2
    3
    inputs.each(function(index){
     
    });
    • Сначала давайте кешируем селектор jQuery для thisтекущего элемента в нашем цикле.

      1
      2
      3
      inputs.each(function(index){
          var self = $(this),
      });
    • Переменная keyбудет представлять nameатрибут для этого элемента. Значение ключа хранится в fieldNamesмассиве, и его индекс в этом массиве равен индексу текущего элемента в нашем цикле. Таким образом, fieldNames[index]должно содержать соответствующее имя поля для inputs[index].

      1
      2
      3
      4
      inputs.each(function(index){
          var self = $(this),
              key = fieldNames[index],
      });
    • Наконец, мы определяем переменную valкак своего рода псевдоатрибут value. Мы будем использовать textметод jQuery, чтобы получить текст текущего элемента в цикле.

      1
      2
      3
      4
      5
      inputs.each(function(index){
          var self = $(this),
              key = fieldNames[index],
              val = self.text();
      });
    • Определив наши пары ключ / значение, мы начнем заполнять объект, используя квадратную скобку / обозначение ассоциативного массива. Мы установим ключ для каждого элемента как keyи значение как val. Преимущество использования этого типа записи в данном конкретном контексте заключается в том, что мы можем установить ключ с помощью переменной. В буквальном обозначении переменная keyбудет интерпретироваться как строка "key".

      1
      2
      3
      4
      5
      6
      7
      inputs.each(function(index){
          var self = $(this),
              key = fieldNames[index],
              val = self.text();
       
          data[key] = val;
      });
  • Теперь, когда наша итерация завершена и dataобъект заполнен, мы будем вызывать $.paramметод jQuery с dataаргументом. $.paramвернет закодированное в виде строки строковое представление объекта или массива, переданного ему.

    1
    return $.param(data);

    Для получения дополнительной информации о методе jQuery $ .param проверьте API jQuery .


Вот посмотрите на полный _serializeметод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
_encode: function(inputs){
 
    if(inputs.is("input")) return inputs.serialize();
 
    var data = {},
        fieldNames = this.options.fieldNames;
 
    inputs.each(function(index){
 
        var self = $(this),
            key = fieldNames[index],
            val = self.text();
 
        data[key] = val;
    });
 
    return $.param(data);
 
},