Статьи

Создание масштабируемого виджета с использованием YUI3: часть 1

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

Виджет, который мы создадим сегодня, — это поисковый клиент Twitter, который будет запрашивать API поиска Twitter и использовать ответ JSON для отображения твитов, которые содержат настроенный поисковый термин. Мы также можем добавить дополнительные функциональные возможности, например, позволить посетителю выбрать другой термин и выполнить новый поиск, а также просмотреть постраничные результаты. Присоединяйся ко мне после прыжка!


Все необходимые модули YUI будут извлекаться динамически, когда страница с нашим виджетом загружается

Нам понадобятся обычные папки css , img и js созданные в папке проекта, чтобы мы могли хранить наши различные ресурсы. Изображения, которые будет использовать наш виджет, можно найти в загрузке кода. Нам не нужно беспокоиться о загрузке копии самой библиотеки YUI, так как все необходимые модули YUI будут извлекаться динамически при загрузке страницы с нашим виджетом (мы рассмотрим это более подробно позже).


Создайте новый файл сценария и добавьте в него следующий код:

1
2
3
YUI.add(«tweet-search», function (Y) {
 
}, «0.0.1», { requires: [«widget», «substitute», «jsonp»] });

Это внешняя оболочка для нашего виджета; весь код, который мы напишем, будет находиться внутри функции, передаваемой в качестве второго аргумента в метод add() YUI. Метод add() объекта YUI позволяет нам добавить в библиотеку новый модуль, который может быть простой функцией или классом, виджетом, расширением или плагином.

  • Первый аргумент, который мы предоставляем, — это имя нашего виджета. Это имя используется в методе use() при реализации виджета.
  • Второй аргумент — это анонимная функция, которая используется для определения класса виджета. Аргумент, принятый этой функцией, является экземпляром YUI, к которому прикреплен виджет.
  • Третий аргумент используется для указания номера версии виджета.
  • Четвертый и последний аргумент — это объект, который мы можем использовать для предоставления дополнительной конфигурации для виджета.

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

Как видите, одним из обязательных компонентов является компонент Widget. При создании пользовательского виджета компонент библиотеки Widget должен быть расширен, чтобы использовать мощные конструкции, которые устанавливает Widget. Мы также используем компонент-заменитель для простой подстановки строк при создании необходимых элементов HTML и компонент JSONP для взаимодействия с поисковым API Twitter.


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

01
02
03
04
05
06
07
08
09
10
11
var Node = Y.Node,
    getClassName = Y.ClassNameManager.getClassName,
    i, j,
    baseClasses = [«_CLASS», «title», «loader», «viewer», «tweet», «ui», «label», «input», «button», «error»],
    templates = [«_TEMPLATE», «<hgroup class={titleclass}><h1>{title}</h1><h2>{subtitle}<span>{term}</span></h2></hgroup>», «<div class={loaderclass}>loading…</div>», «<div class={viewerclass}></div>», «<article><a href={userurl} title={username}><img src={avatar} alt={username} /><h1>{username}</h1></a><p>{text}</p></article>», «<div class={uiclass}></div>», «<label class={labelclass}>{labeltext}</label>», «<input class={inputclass} />», «<button class={buttonclass}>{buttontext}</button>», «<p class={errorclass}>{message}</p>»];
 
function TweetSearch(config) {
    TweetSearch.superclass.constructor.apply(this, arguments);
}
 
Y.namespace(«DW»).TweetSearch = TweetSearch;

Имя нашего виджета имеет заглавную первую букву его имени, как это принято для именования конструкторов.

Сначала мы кэшируем ссылки на компонент Y.Node и метод Y.ClassNameManager.getClassName() как мы будем часто их использовать. Мы также определим пару переменных для использования в for loop и создадим два новых массива; первая содержит серию строк, которые образуют часть имен классов, добавленных к элементам HTML, которые создаст наш виджет, а вторая содержит шаблоны HTML, также в формате строки, которые будут использоваться для создания самих элементов.

Затем мы добавляем функцию конструктора для нашего виджета; это функция, которую будут вызывать разработчики, реализующие наш виджет. Функция может принимать один аргумент, который будет принимать форму объекта, который устанавливает атрибуты конфигурации, предоставляемые нашим виджетом. Имя нашего виджета имеет заглавную первую букву его имени, как это принято для именования конструкторов. Внутри этой функции класс нашего виджета инициализируется с помощью метода apply() конструктора superclass's (Widget). Значение этого установлено для нашего экземпляра виджета.

Мы также можем создать пространство имен для нашего виджета с помощью метода namespace() YUI; это не обязательно, но это очень хорошая практика — запускать код в пространстве имен, чтобы минимизировать возможность именования коллизий, когда код используется в дикой природе. Метод namespace() принимает строку, которая представляет пространство имен, к которому прикреплено имя виджета в качестве свойства и виджет в качестве значения.

Я установил пространство имен равным моим инициалам, но это может быть что угодно; у вас уже может быть пространство имен, в котором находятся все ваши веб-приложения, или это может быть название вашей компании, имя вашего клиента или что-то еще, что имеет смысл. Этот виджет будет доступен через Y.DW.TweetSearch


Далее мы можем определить статические константы, необходимые при расширении класса Widget. Добавьте следующий код непосредственно после метода namespace() :

01
02
03
04
05
06
07
08
09
10
TweetSearch.NAME = «tweetsearch»;
 
for (i = 1, j = baseClasses.length; i < j; i++) {
    var current = baseClasses[i].toUpperCase(),
        prop1 = current + baseClasses[0],
        prop2 = current + templates[0];
 
    TweetSearch[prop1] = getClassName(TweetSearch.NAME, baseClasses[i]);
    TweetSearch[prop2] = templates[i];
}

Сначала мы устанавливаем свойство NAME нашего виджета; Соглашение об именовании всех заглавных букв здесь означает значение, которое будет постоянным в течение жизненного цикла нашего экземпляра виджета. Заданное нами имя используется виджетом в качестве префикса при запуске событий и создании имен классов для элементов HTML.

Далее следует for loop мы используем для добавления необходимых имен классов и шаблонов разметки в наш виджет. Мы инициализируем переменные i и j которые мы объявляем в верхней части функции; Переменная i которая используется в качестве счетчика, изначально устанавливается в 1, а не в 0, как это обычно бывает (вы поймете, почему только через мгновение), а переменная j устанавливается в длину нашего массива baseClasses ( baseClasses и массивы templates имеют одинаковую длину, так как каждому создаваемому элементу присваивается имя класса. Это не всегда так).

Внутри цикла мы baseClasses ссылку на текущий элемент из массива baseClasses и в верхнем регистре, а затем создаем две новые строки с prop1 и prop2 . Эти строки состоят из только что созданной переменной и первого элемента в нашем массиве baseClasses , поэтому на первой итерации, например, эта строка будет равна TITLE_CLASS для prop1 и TITLE_TEMPLATE для prop2 .

Затем мы добавляем эти новые свойства в наш экземпляр виджета; первое свойство устанавливается на результат вызова getClassName() (помните, мы используем созданный ранее Y.ClassNameManager.getClassName который указывает на Y.ClassNameManager.getClassName ). Мы передаем имя нашего виджета в качестве первого аргумента этому методу и текущий элемент из массива baseClasses . Это приведет к созданию сгенерированных имен классов, таких как yui3-tweetsearch-title , доступных, TweetSearch.TITLE_CLASS свойства TweetSearch.TITLE_CLASS .

Второе свойство, которое мы добавляем — это текущий элемент из массива templates . Продолжая пример заголовка, мы получаем имя свойства TweetSearch.TITLE_TEMPLATE со значением <hgroup class={titleclass}><h1>{title}</h1><h2>{subtitle} <span>{term}</span></h2></hgroup> . Целью цикла for loop просто является то, что нам не нужно присоединять все классы и шаблоны к нашему экземпляру вручную.


Теперь мы можем определить настраиваемые атрибуты, которые будут иметь наш виджет, что позволит разработчикам, реализующим виджет, включать или отключать различные функции. Добавьте следующий код непосредственно после цикла for loop :

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
TweetSearch.ATTRS = {
    term: {
        value: «yui3»,
        validator: «_validateTerm»
    },
    numberOfTweets: {
        value: 5
    },
    baseURL: {
        value: «http://search.twitter.com/search.json?&with_twitter_user_id=true&include_entities=true&callback={callback}»
    },
    tweets: {
        value: null
    },
    showTitle: {
        value: true
    },
    showUI: {
        value: true
    },
 
    strings: {
        value: {
            title: «Twitter Search Widget»,
            subTitle: «Showing results for:»,
            label: «Search Term»,
            button: «Search»,
        errorMsg: «I’m sorry, that search term did not return any results. Please try a different term»
        }
    }
};

Библиотека YUI добавляет согласованный способ добавления атрибутов в любой класс или модуль.

Константа ATTRS используется для хранения настраиваемых атрибутов, которые разработчик-разработчик может установить при создании экземпляра виджета. Библиотека YUI добавляет согласованный способ добавления атрибутов к любому классу или модулю, и этот механизм автоматически доступен при расширении виджета.

Вместо установки значения каждого атрибута на простое собственное значение, такое как sting или Boolean, используется объект. Значение по умолчанию для каждого атрибута задается с помощью свойства value этого объекта. В первом атрибуте мы также используем свойство validator , которое позволяет нам указать функцию, которая будет автоматически вызываться при каждом обновлении значения. Это позволяет нам проверить, что значение имеет определенный формат или соответствует другим пользовательским критериям. Есть также ряд других свойств, которые мы можем установить для каждого атрибута, включая; пользовательские методы получения и установки, является ли атрибут доступным только для чтения и многое другое.

Атрибуты, используемые нашим виджетом, включают поисковый термин, количество отображаемых твитов, baseURL запроса, отправленного в Twitter, показывать ли заголовок виджета и отображать ли пользовательский интерфейс поиска. Есть ряд других атрибутов, которые наш виджет получит автоматически, и которые мы можем использовать. Мы рассмотрим это более подробно позже в руководстве.

Последний атрибут, который мы определяем — это атрибут strings , который доступен для всех модулей, которые являются подклассами Widget. Значение этого атрибута также является объектом, и в него мы добавляем все текстовые строки, которые будет отображать наш виджет. Использование атрибута для определения любых слов, которые виджет должен отображать таким образом, делает наш виджет очень простым для интернационализации; разработчикам-разработчикам нужно только переопределить атрибут strings своей собственной коллекцией строк на том языке, который они выберут.


Суперкласс Widget предоставляет нам статическое свойство HTML_PARSER которое может извлекать значения из любых элементов HTML, присутствующих в контейнере виджета, и использовать эти значения в качестве атрибутов, что позволяет нам невероятно легко создавать виджеты, которые преобразуют базовую разметку в нечто более функциональный и / или красивый.

Нам не нужно беспокоиться об этом для нашего виджета; Если JavaScript отключен, AJAX-запрос не будет направляться в API поиска Twitter, и в любом случае не будет данных для отображения. Тем не менее, они предоставляют разработчикам больше способов создания экземпляров виджета и настройки атрибутов. Мы можем сделать условие, что если текст <input> присутствует в контейнере виджета, значение поля будет использоваться в качестве поискового термина вместо значение атрибута по умолчанию. Чтобы получить это значение, мы можем использовать HTML_PARSER ; добавьте следующий код непосредственно после определения ATTRS :

01
02
03
04
05
06
07
08
09
10
11
12
TweetSearch.HTML_PARSER = {
   term: function (srcNode) {
       var input = srcNode.one(«input»);
 
       if (input) {
           var val = input.get(«value»);
               input.remove();
           }
 
           return val;
       }
   };

Свойство HTML_PARSER является литералом объекта, где каждое свойство в этом объекте отображается непосредственно в атрибут. Единственный атрибут, для которого мы хотим добавить поддержку прогрессивного расширения, — это атрибут term , значение которого установлено на функционал, который будет автоматически вызываться при инициализации нашего виджета.

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

Первое, что мы делаем, это пытаемся выбрать элемент <input> из srcNode используя метод one() YUI, который выбирает один соответствующий элемент из DOM. Если элемент получен, мы сохраняем его value в переменной с именем val , а затем удаляем элемент со страницы (мы создадим альтернативный элемент <input> для случая, когда пользовательский интерфейс поиска будет включен позже). Затем мы возвращаем val . Если val не установлен, т.е. если в srcNode не было <input> , будет возвращено srcNode , а атрибуту term будет присвоено настроенное значение. Если val содержит значение, оно станет значением атрибута term .


Прежде чем мы закончим эту часть руководства, мы рассмотрим метод, который мы используем для расширения класса Widget функциональностью, характерной для нашего виджета. Этот метод сформирует основную часть нашего виджета. Сразу после HTML_PARSER добавьте следующее:

1
2
3
TweetSearch = Y.extend(TweetSearch, Y.Widget, {
 
});

Метод extended extend() YUI принимает три аргумента:

  • Первый — это объект для расширения, который в этом примере является классом нашего виджета.
  • Второй аргумент — это объект, который мы расширяем, в данном случае класс Widget.
  • Третий аргумент — это объект, содержащий методы-прототипы для добавления или переопределения нашего виджета. Объект, переданный в качестве третьего аргумента, будет оболочкой для остальной части нашего кода, о котором мы расскажем в следующей части этого урока.

Сохраните этот файл в папке js как tweet-search.js .


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

Определив некоторые переменные верхнего уровня, мы сначала увидели, как определить функцию конструктора для нашего виджета, чтобы виджет мог быть инициализирован библиотекой, а также увидели, насколько легко пространство имен нашего виджета. Затем мы рассмотрели статические константы, которые унаследованы от базового класса Widget, который мы расширяем. К ним относятся NAME виджета, коллекции _CLASS и _TEMPLATE и объект ATTRS , последний из которых позволил нам установить атрибуты, которые разработчик-разработчик может переопределить, если они того пожелают.

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

Оставайтесь с нами и большое спасибо за чтение!