Управление контекстом функций обратного вызова

В главе 2 была представлена функция hitch, которая может использоваться, чтобы обеспечить выполнение некоторой функции в определенном контексте. Довольно часто функция hitch используется совместно с объектом XHR, потому что иногда требуется, чтобы контекст функции обратного вызова отличался от контекста блока программного кода, в котором она вызывается. Следующий фрагмент демонстрирует один из случаев применения функции hitch и представляет собой типичный шаблон использования псевдонима для ссылки this, чтобы решить проблему контекста в функции обратного вызова:

//Предположим, что имеется следующий блок addOnLoad block,
//который фактически может быть любым объектом JavaScript
dojo.addOnLoad(function() {
//объект foo связан с контекстом этой анонимной функции
this.foo = "bar";
//создать псевдоним для "this", чтобы на него можно было
//ссылаться внутри функции load...
var self=this;
dojo.xhrGet({
url : "./data",
load : function(response, ioArgs) {
//для обращения к объекту foo внутри этой функции
//следует использовать псевдоним "this"...
console.log(self.foo, response);
},
error : function(response, ioArgs) {
console.log("error", response, ioArgs);
}
});
});


Хотя в этом коротком примере все выглядит достаточно понятным, но программный код, в котором для работы многократно создавались новые объекты – псевдонимы контекста this, будет выглядеть беспорядочным. В следующий раз, когда вам потребуется создать псевдоним для this, подумайте о применении следующего шаблона, в котором используется функция hitch:
dojo.addOnLoad(function() {
//объект foo связан с контекстом этой анонимной функции
this.foo = "bar";
//связать контекст функции обратного вызова с текущим контекстом,
//чтобы иметь возможность обращаться к объекту foo
var callback = dojo.hitch(this, function(response, ioArgs) {
console.log("foo (in context) is", this.foo);
//и в вашем распоряжении все еще имеются response и ioArgs...
});
dojo.xhrGet({
url : "./data",
load : callback,
error : function(response, ioArgs) {
console.log("error", response, ioArgs);
}
});
});


И не забывайте, что функция hitch может принимать дополнительные аргументы, благодаря чему вы легко и просто можете передавать ей любые параметры, которые должны быть доступны в функции обратного вызова, например так:
dojo.addOnLoad(function() {
//объект foo связан с контекстом этой анонимной функции
this.foo = "bar";
//связать контекст функции обратного вызова с текущим контекстом,
//чтобы иметь возможность обращаться к объекту foo
var callback = dojo.hitch(
this,
function(extraParam1, extraParam2, response, ioArgs) {
console.log("foo (in context) is", this.foo);
//и в вашем распоряжении все еще имеются response и ioArgs...
},
"extra", "params"
);
dojo.xhrGet({
url : "./data",
load : callback,
error : function(response, ioArgs) {
console.log("error", response, ioArgs);
}
});
});


Если число параметров может изменяться, вы можете использовать для доступа к ним свойство arguments, не забывая, что два последних значения – это аргументы response и ioArgs.

Добавлено: 24 Июля 2018 20:43:07 Добавил: Андрей Ковальчук

Многоцелевая функция выполнения запросов через XMLHttpRequest

В Dojo версии 1.1 появилась более универсальная функция dojo.xhr со следующей сигнатурой:

dojo.xhr(/*String*/ method, /*Object*/ args, /*Boolean?*/ hasBody)


Как оказывается, каждая из функций, работающих с XHR, представленных в этой главе, фактически является оберткой вокруг этой функции. Например, dojo.xhrGet в действительности реализована следующим образом:
dojo.xhrGet = function(args) {
return dojo.xhr("GET", args); //Имя метода всегда должно записываться
//заглавными символами!
}


В большинстве случаев на практике используются функцииобертки, представленные в этом разделе, тем не менее в некоторых ситуациях удобнее использовать многоцелевую функцию dojo.xhr: когда необходимо программно настраивать объект XHR или когда нужная функцияобертка отсутствует. Например, следующий фрагмент выполняет запрос HEAD, для которого не предусмотрена функцияобертка:
dojo.xhr("HEAD", {
url : "/foo/bar/baz",
load : function(response, ioArgs) { /*...*/},
error : function(response, ioArgs) { /*...*/}
});

Добавлено: 24 Июля 2018 20:42:30 Добавил: Андрей Ковальчук

JSON

Перед тем как перейти к обсуждению технологии AJAX, необходимо вкратце рассмотреть формат JSON, потому что он стал практически стандартным форматом для организации обмена простыми данными в AJAXприложениях. С формальным описанием JSON можно познакомиться по адресу http://json.org, но по сути JSON – это просто строковое представление объектов JavaScript. В библиотеке Base имеются две простые функции, выполняющие преобразования объектов JavaScript в строковое представление и обратно. Эти функции берут на себя все хлопоты по экранированию специальных символов, таких как табуляция или перевод строки, и могут даже форматировать получаемый результат, если вам это требуется:

dojo.fromJson(/*String*/ json) //Возвращает Object
dojo.toJson(/*Object*/ json, /*Boolean?*/ prettyPrint) //Возвращает String


Ниже приводится короткий пример, иллюстрирующий процесс преобразования объекта в строку JSON, удобную для восприятия человеком:
var o = {a:1, b:2, c:3, d:4};
dojo.toJson(o, true); //форматирование результата
/* получится ...
'{
"a": 1,
"b": 2,
"c":3,
"d":4
}'

Добавлено: 24 Июля 2018 20:41:45 Добавил: Андрей Ковальчук

Организация взаимодействий по подписке

Существует масса ситуаций, когда прямой «цепочечный» стиль организации взаимодействий посредством функции dojo.connect является именно тем средством, которое необходимо для решения проблем. Однако существует не меньше ситуаций, когда требуется более опосредованный «широковещательный» стиль организации взаимодействий, когда различные виджеты взаимодействуют друг с другом анонимно.
В таких случаях можно использовать функции dojo.publish и dojo.sub-scribe.

Классическим примером является ситуация, когда необходимо организовать взаимодействие между одним объектом JavaScript и несколькими другими объектами по типу отношений «один ко многим». Вместо того чтобы создавать и управлять множественными соединениями, создаваемыми с помощью функции dojo.connect, которые больше напоминают одно связное действие, значительно проще организовать передачу уведомления о событии, произошедшем в одном виджете (наряду с данными, сопутствующими этому событию) , чтобы другие виджеты могли подписаться на получение этих извещений и автоматически принимать соответствующие меры. Вся прелесть такого подхода состоит в том, что объект, осуществляющий отправку уведомлений, ничего не должен знать о других объектах и даже не должен делать никаких предположений об их существовании. Другим классическим примером такого рода взаимодействий являются портлеты – подключаемые компоненты интерфейса (http://en.wikipedia.org/wiki/Portlet1), которые обслуживаются вебпорталом, иногда через панель управления.

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

Для начала, чтобы определить, какой стиль организации взаимодействий лучше подходит для решения проблемы, попробуйте ответить на следующие вопросы:
• Предполагаете ли вы (и насколько это правильно) публиковать прикладной интерфейс виджета, который вы разрабатываете? Если нет, тогда вам определенно следует использовать взаимодействия по подписке, потому что это позволит вам свободно менять архитектуру виджета, не опасаясь вступить в конфликт с его опубликованным API.
• Предполагается ли одновременное использование нескольких однотипных виджетов, которые порождают один и тот же тип событий?

Если да, тогда вам определенно следует использовать взаимодействия, основанные на прямых соединениях, потому что в противном случае вам придется разрабатывать дополнительную логику, которая будет определять, какой виджет и на какое событие должен реагировать.
• Разрабатываете ли вы виджет, содержащий другие виджеты в отношении «имеет»? Если да, тогда вам следует использовать взаимодействия, основанные на прямых соединениях.
• Предполагается ли возможность организации взаимодействий по принципу «один ко многим» или «многие ко многим»? Если да, то гда определенно следует использовать взаимодействия по подписке, чтобы минимизировать тяжесть организации взаимодействий, основанных на прямых соединениях.
• Предполагается ли, что взаимодействия должны быть полностью анонимными и должна ли быть обеспечена свобода в выборе взаимодействующих сторон? Если да, тогда следует использовать взаимодействия по подписке.

Не будем долго задерживаться и рассмотрим интерфейс организации взаимодействий по подписке. Обратите внимание, что в случае функции dojo.subscribe можно опустить параметр context, а функция выполнит необходимую нормализацию аргументов (точно так же, как и в случае с функцией dojo.connect):

dojo.publish(/*String*/topic, /*Array*/args)
dojo.subscribe(/*String*/topic, /*Object|null*/context,
/*String|Function*/method) //Возвращает Handle
dojo.unsubscribe(/*Handle*/handle)


Давайте рассмотрим простой пример, основанный на использовании функций dojo.subscribe и
dojo.publish:
function Foo(topic) {
this.topic = topic;
this.greet = function() {
console.log("Hi, I'm Foo");
/* Объект Foo публикует информацию, но не для конкретного получателя */
dojo.publish(this.topic);
}
}
function Bar(topic) {
this.topic = topic;
this.greet = function() {
console.log("Hi, I'm Bar");
}
/ * Объект Bar подписывается на информацию, но не от конкретного источника */
dojo.subscribe(this.topic, this, "greet");
}
var foo = new Foo("/dtdg/salutation");
var bar = new Bar("/dtdg/salutation");
foo.greet(); //Hi, I'm Foo...Hi, I'm Bar


Как видите, функция connect связывает конкретный источник с конкретным адресатом, а функции publish/subscribe позволяют работать с широковещательными посылками, которые могут исходить из любого источника и обрабатываться любыми получателями, заинтересованными в получении таких посылок. Отчасти удивительная мощь, заключенная в архитектуре с гибко связанными компонентами, обусловлена тем, что при минимальных усилиях и большой простоте она позволяет получать приложения, которые концептуально представляют собой совокупность связанных компонентов.

Давайте посмотрим, как можно отписаться от получения уведомлений, внеся изменения в реализацию объекта Bar. Пусть теперь объект Bar отвечает на сообщение, публикуемое объектом Foo, только один раз:
function Bar(topic) {
this.topic = topic;
this.greet = function() {
console.log("Hi, I'm bar");
dojo.unsubscribe(this.handle);
//не тревожь ты меня, я тебе не отвечу
}
this.handle = dojo.subscribe(this.topic, this, "greet");
}


Обратите внимание, что существует возможность вместе с сообщением передать как второй аргумент функции publish массив значений в виде именованных параметров, которые будут переданы обработчику, подписанному с помощью функции subscribe.
И, в заключение, предположим, что у нас отсутствует возможность изменить метод greet объекта Foo, чтобы включить в него вызов dojo.pub-lish, изза наличия какихто внешних обстоятельств, которые препятствуют этому, например когда программный код не является вашей собственностью или не должен изменяться. Это не причина для беспокойств – мы можем использовать другую функцию, dojo.connectPublisher, которая будет выполнять публикацию события всякий раз, когда оно происходит:
function Foo() {
this.greet = function() {
console.log("Hi, I'm foo");
}
}
function Bar() {
this.greet = function() {
console.log("Hi, I'm bar");
}
}
var foo = new Foo;
var bar = new Bar;
var topic = "/dtdg/salutation";
dojo.subscribe(topic, bar, "greet");
dojo.connectPublisher(topic, foo, "greet");
foo.greet();

Суть этого заключительного примера состоит в том, что функция dojo.connectPublisher позволила добиться тех же результатов, что и добавление вызова dojo.publish в метод greet, но без изменения исходного программного кода. В этом смысле объект foo является косвенным источником уведомлений и даже не подозревает, что вообще осуществляется какоето взаимодействие. С другой стороны, объект Bar, как подписчик на получение уведомления, требует явного знания схемы взаимодействий. По существу, это противоположный случай типично го использования функции dojo.connect, когда объект, предоставляющий информацию для обмена, должен явно знать о других объектах или функциях, которые играют роль «целей» соединения.

Добавлено: 24 Июля 2018 20:40:52 Добавил: Андрей Ковальчук

Использование замыканий с функцией dojo.connect

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

Однократные соединения
Рассмотрим ситуацию, когда необходимо установить соединение, которое должно быть разорвано после первого же срабатывания. Следующий пример показывает, как выполнить эту работу с минимальными усилиями:

var handle = dojo.connect(
dojo.byId("foo"), //некоторый элемент div
"onmouseover",
function(evt) {
//здесь находится тело некоторого обработчика...
dojo.disconnect(handle);
}
);


Если вы еще недостаточно уверенно чувствуете себя при работе с замыканиями, вашей первой реакцией может быть: «То, что мы только что сделали, попросту невозможно». В конце концов, переменная handle получает значение, возвращаемое функцией dojo.connect, и при этом ссылка на нее используется внутри функции, которая передается в dojo.connect в качестве параметра. Чтобы лучше понять ситуацию, разберем все происходящее в деталях:
1. Вызывается функция dojo.connect, и, хотя одним из ее параметров является анонимная функция, она в этот момент еще не выполняется.
2. Любые переменные внутри анонимной функции (такие как handle) связаны с ее областью видимости, и хотя они присутствуют в телефункции, фактическое обращение к ним происходит, только когда функция действительно будет вызвана, поэтому в этом программном коде не возникает никакой ошибки.
3. Функция dojo.connect возвращает значение переменной handle еще до того, как анонимная функция будет вызвана. Поэтому к моменту вызова анонимной функции значение переменной будет определено и может передаваться функции dojo.disconnect.

Установка соединений в цикле
Еще одна ситуация, часто встречающаяся на практике, – необходимость устанавливать соединения в теле цикла. Предположим, что у нас в странице имеется несколько элементов – foo0, foo1, ..., foo9, и нам необходимо при перемещении указателя мыши над этими элементами выводить уникальное для каждого из них число. При первой попытке вы могли бы прийти к следующему фрагменту программного кода, который, впрочем, не даст ожидаемого результата:
/* Следующий фрагмент работает не так, как ожидается! */
for (var i=0; i < 10; i++) {
var foo = dojo.byId("foo"+i);
var handle = dojo.connect(foo, "onmouseover", function(evt) {
console.log(i);
dojo.disconnect(handle);
});
}


Если вы запустите этот фрагмент в Firebug на странице с серией указанных элементов, вы быстро обнаружите, что тут имеется проблема. А именно, в консоли всегда будет выводиться число 10, то есть всеми функциямиобработчиками будет использоваться последнее значение переменной i, а это означает, что все десять обработчиков по ошибке будут пытаться разорвать одно и то же соединение. Остановимся на минутку, чтобы обдумать ситуацию. Однако такому неожиданному для вас поведению имеется разумное объяснение: внутри замыкания, образованного анонимной функцией, переданной функции dojo.connect, не выполняется разрешение имени i, пока функция действительно не будет вызвана, но к этому моменту переменная i будет иметь последнее полученное в цикле значение.

Следующие изменения устраняют проблему, захватывая значение переменной i в ловушку цепочки областей видимости, чтобы при после дующем обращении к переменной возвращалось значение, которое было на момент вызова функции dojo.connect:
for (var i=0; i < 10; i++) {
(function() {
var foo = dojo.byId("foo"+i);
var current_i = i; //поймать в ловушку замыкания
var handle = dojo.connect(foo, "onmouseover",
function(evt) {
console.log(current_i);
dojo.disconnect(handle);
}
);
})(); //выполнить анонимную функцию немедленно
}


Поначалу этот фрагмент программного кода может показаться замысловатым, но на самом деле здесь нет ничего сложного. Все тело цикла оформлено в виде анонимной функции, которая тут же и выполняется, а поскольку анонимные функции образуют замыкание для всего, что находится внутри, значение переменной i попадает в «ловушку» переменной current_i, которая может быть разрешена во время выполнения обработчика события. Точно так же правильно будет разрешаться и переменная handle, потому что она тоже существует в пределах замыкания, образованного встроенной анонимной функцией.

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

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

Соединения в тексте разметки
Стоит заметить, что точно так же можно создавать соединения для дид житов вообще без (или с минимальным использованием) программного кода JavaScript при использовании специальных тегов SCRIPT с функцией dojo.connect в тексте разметки.

Добавлено: 24 Июля 2018 20:40:12 Добавил: Андрей Ковальчук

Распространение событий

Бывают моменты, когда необходимо переопределить обработку некоторых событий DOM, встроенную в броузер, и с помощью функции dojo.connect подставить свои функции, которые будут обрабатывать эти события. В качестве двух наиболее типичных примеров можно привести необходимость предотвратить автоматический переход броузера после щелчка на гиперссылке и необходимость предотвратить автоматическую отправку формы по нажатию на клавишу Enter или в результате щелчка на кнопке Submit.

К счастью, помешать броузеру выполнить действие по умолчанию, заданное для этих событий DOM, очень просто – достаточно лишь вызвать функцию dojo.stopEvent или метод preventDefault объекта DOM-Event, и событие прекратит свое распространение по броузеру. Функция stopEvent принимает в качестве параметра объект DOMEvent:

dojo.stopEvent(/*DOMEvent*/evt)


Следующий пример демонстрирует применение функции stopEvent:
var foo = dojo.byId("foo"); //некоторый якорный элемент
dojo.connect(foo, "onclick", function(evt) {
console.log("anchor clicked");
dojo.stopEvent(evt); //предотвратит переход по ссылке и дальнейшее
//всплытие события
});


Точно так же просто отменяется автоматическая отправка формы, для этого достаточно передать контекст соединения и связать его с событием submit. Однако на этот раз, чтобы отменить действие события по умолчанию, мы воспользуемся методом preventDefault объекта DOM-Event, который при этом не предотвращает дальнейшее всплытие события в дереве DOM:
var bar = dojo.byId("bar"); //some form element
dojo.connect(bar, "onsubmit", function(evt) {
console.log("form submitted");
evt.preventDefault();//предотвратит отправку формы, но не запретит
//дальнейшее всплытие события
});

Добавлено: 24 Июля 2018 20:39:36 Добавил: Андрей Ковальчук

Обработчики событий

Прямые каналы взаимодействий конструируются путем явного соединения функций и/или событий DOM так, что выполнение одной функции автоматически влечет за собой вызов другой. Например, вы можете захотеть, чтобы при каждом изменении какоголибо объекта посредством метода «записи» автоматически производилось изменение в визуальном интерфейсе приложения. Или чтобы каждое изменение одного объекта автоматически вызывало обновление свойств другого объекта. Варианты могут быть самые разные.

В схему прямого взаимодействия вовлечены два основных метода – dojo.connect и dojo.disconnect. Проще говоря, метод dojo.connect используется для объединения в цепь последовательности событий. При каждом обращении к методу dojo.connect возвращается дескриптор, который необходимо сохранять и явно передавать методу dojo.disconnect, когда требуется разорвать связь. Обычно при выгрузке страницы все соединения разрываются автоматически, однако ручное управление дескрипторами может потребоваться, чтобы предотвратить утечки памяти в долгоживущих приложениях, которые устанавливают множество временных соединений. (Это особенно справедливо в отношении IE.)

Установка и разрыв соединения выполняются достаточно просто. Ниже приводятся сигнатуры используемых функций:

/* Устанавливает соединение */
dojo.connect(/*Object|null*/ obj,
/*String*/ event,
/*Object|null*/ context,
/*String|Function*/ method) // Возвращает дескриптор Handle
/* Разрывает соединение */
dojo.disconnect(/*Handle*/handle);


Давайте рассмотрим пример, иллюстрирующий своего рода проблему, которую можно решить с помощью функции dojo.connect:
function Foo() {
this.greet = function() { console.log("Hi, I'm Foo"); }
}
function Bar() {
this.greet = function() { console.log("Hi, I'm Bar"); }
}
foo = new Foo;
bar = new Bar;
foo.greet();
//объект bar должен отвечать на приветствие объекта foo
//всегда, когда объект bar существует.


Оказывается, что решить эту маленькую проблему можно всего одной строкой программного кода. Измените предыдущий листинг, как показано ниже, и проверьте его в Firebug:
function Foo() {
this.greet = function() { console.log("Hi, I'm foo"); }
}

function Bar() {
this.greet = function() { console.log("Hi, I'm bar"); }
}
foo = new Foo;
bar = new Bar;
//При каждом вызове foo.greet автоматически будет вызываться bar.greet ...
var handle = dojo.connect(foo, "greet", bar, "greet"); //устанавливается
//соединение
foo.greet(); //теперь объект bar автоматически ответит на приветствие!


Вы написали всего одну строчку, а получили такой приятный результат – неплохо, не правда ли? Обратите внимание, что второй и четвертый параметры, переданные функции dojo.connect, – строковые литералы для соответствующих им контекстов и что функция возвращает дескриптор, который позднее позволит разорвать это соединение. Вообще говоря, всегда наступает некоторый момент, когда следует разорвать соединение – либо приложение достигает некоторого функционального состояния, либо вы выполняете некоторые завершающие действия, например при уничтожении некоторого объекта или при закрытии страницы, примерно так, как показано ниже:
var handle = dojo.connect(foo, "greet", bar, "greet");
foo.greet();
dojo.disconnect(handle);
foo.greet(); //на сей раз ответного приветствия не последует


Кроме того что dojo.connect позволяет добиться такого значительного эффекта такими малыми усилиями, обратите внимание, насколько опрятным и понятным остался программный код. Никаких шаблонных заготовок, никакой путаницы, никаких накрученных решений и никакого кошмара для того, кто будет это сопровождать. Запуск методов в ответ на события, происходящие в странице, – действительно очень удобная возможность, но помимо этого рано или поздно появится необходимость передавать таким методам какие-нибудь аргументы. Оказывается, функция connect обладает возможностью передавать аргументы из контекста одной функции в контекст другой функции. Ниже приводится пример, как это делается:
function Foo() {
this.greet = function(greeting) { console.log("Hi, I'm Foo.",greeting); };
}
function Bar() {
this.greet = function(greeting) { console.log("Hi, I'm Bar.",greeting); };
}
foo = new Foo;
bar = new Bar;
var handle= dojo.connect(foo, "greet", bar, "greet");
foo.greet("Nice to meet you");


Вы наверняка можете оценить, насколько удобно, когда передача аргументов происходит автоматически, и это особенно верно для функций, связанных с событиями DOM, например щелчок мышью, потому что такая возможность обеспечивает для функции моментальный доступ ко всем важным сведениям о событии, таким как целевой элемент, координаты указателя мыши и т. д. Давайте рассмотрим еще один пример:
//Обратите внимание, что третий аргумент опущен, так как обработчик -
//это анонимная функция. Если в третьем аргументе передать значение null,
//мы получим тот же самый эффект.
dojo.connect(
dojo.byId("foo"), //Некоторый элемент DOM
"onmouseover",
function(evt) {
console.log(evt);
});

Если создать пробную страницу, установить в ней соединение и наблюдать за происходящим в консоли Firebug, можно заметить, что функцииобработчику объект события, содержащий практически все сведения, какие только может потребоваться знать о только что произошедшем событии доступен целиком.

Возникает резонный вопрос: «Если настолько просто определять обработчики событий DOM, зачем тогда заниматься изучением другой библиотечной функции?». Да, возможно, не требуется быть нейрохирургом, чтобы собрать вместе несколько простых обработчиков событий, но как быть, когда имеется сложное приложение, основанное на обработке большого числа сложных событий с учетом предпочтений пользователя, на обработке нестандартных событий или обладающее иным поведением, управляемым событиями? Несомненно, всю необходимую работу можно выполнить вручную, но сможете ли вы устанавливать и разрывать соединение однострочными командами, с единым непротиворечивым интерфейсом, уже написанными и всесторонне проверенными?

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

Добавлено: 24 Июля 2018 20:39:00 Добавил: Андрей Ковальчук

Утилиты для работы с броузером

В этом разделе представлен краткий обзор вспомогательных функций из инструментального набора, предназначенных для работы с cookies и с кнопкой «Назад» – двух возможностей, поддержка которых является обычной для любого современного вебприложения. Поскольку все необходимые функции находится в библиотеке Core, прежде чем воспользоваться ими, следует загрузить их с помощью dojo.require.

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

Концепция cookies впервые была разработана в компании Netscape с целью смягчить эту проблему и дать броузерам ограниченную форму краткосрочной памяти. Проще говоря, разработчики вебстраниц могут с помощью JavaScript или с помощью сценариев на стороне сервера создать cookie с информацией о посещении страницы в виде пар имя/значение. При очередном посещении страницы сценарии могут извлечь информацию из cookie и динамически подстроить страницу под вас. Вообще cookies имеют конечный срок действия и всегда связаны с определенным доменом, откуда они были получены.

Одна из проблем, возникающих при работе с cookie из JavaScript, заключается в том, что вам приходится помнить некий строгий требуемый синтаксис и самостоятельно конструировать строки. Например, чтобы создать cookie для домена по умолчанию, содержащий пару имя/значение foo=bar, с определенным сроком действия, необходимо выполнить примерно следующее действие:
document.cookie ='foo=bar; expires=Sun, 15 Jun 2008 12:00:00 UTC; path=/'

Безусловно, в этом нет ничего сложного. Но когда потребуется прочитать значения из cookie, вам придется самостоятельно проанализировать строку, которая может содержать большое число пар имя/значение.

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

Например, установить и прочитать значение cookie можно следующим образом:

dojo.cookie("foo","bar", {expires : 30});
//установит пару ключ/значение foo/bar,
//со сроком действия 30 дней от текущей даты
dojo.cookie("foo"); //вернет значение bar для имени foo

Работа с кнопкой «Назад»

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

Пример 2.8, как хотелось бы надеяться, поясняет основную идею; в нем иллюстрируется тривиальное использование функции back для определения функции обратного вызова, которая должна обеспечить необходимое поведение. Обратите внимание: строки, выделенные в листинге, необходимы для обеспечения нормальной работы в IE.

Пример 2.8. Пример обработки нажатия кнопки «Назад»
<html>
<head>
<title>Fun with Back!</title>
<link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
<script
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
djConfig="dojoIframeHistoryUrl:'iframe_history.html',isDebug:true"
></script>
<script type="text/javascript">
dojo.addOnLoad(function() {
initialState = {
back: function() { console.log("Back to initial state"); }
};
state1 = {
back:function() { console.log("Back to state 1"); },
forward:function() { console.log("Forward to state 1"); },
changeUrl:true //можно было бы использовать идентификатор,
//такой как "state1"
};
state2 = {
back: function() { console.log("Back to state 2"); },
forward: function() {console.log("Forward to state 2"); },
changeUrl:true //можно было бы использовать идентификатор,
//такой как "state2"
};
//установить начальное состояние и переместиться вперед
//на два шага в истории
dojo.back.setInitialState(initialState);
dojo.back.addToHistory(state1);
dojo.back.addToHistory(state2);
});
</script>
<head>
(body)
<script type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/back.js"></script>
<script type="text/javascript">dojo.back.init( );</script>
Press the back button and have a look at the console.
(/body)
</html>

Добавлено: 24 Июля 2018 20:37:41 Добавил: Андрей Ковальчук

Изменение стилей узлов

Функция dojo.style из библиотеки Base обеспечивает полную возможность получения или изменения значений отдельных атрибутов стиля для конкретного узла. Чтобы получить значение определенного атрибута стиля, достаточно просто передать функции узел и имя атрибута стиля в формате DOM (то есть borderWidth, а не border-width). Если в третьем необязательном аргументе функции передать значение атрибута стиля, она превратится из метода чтения в метод записи. Например, вызов dojo.style("foo", "height") вернет высоту элемента со значением атрибута id, равным "foo", а вызов dojo.style("foo", "height", "100px") установит высоту элемента равной 100 пикселям. Существует также возможность одним обращением к функции изменить значения сразу нескольких атрибутов, используя значение типа Object в качестве второго аргумента, например, так:

dojo.style("foo", {
height : "100px",
width : "100px",
border : "1px green"
});


Во многих приложениях помимо возможности манипулировать определенными атрибутами стиля с помощью dojo.style возникает необходимость добавлять, удалять, переключать и проверять существование определенных классов стилей. Все это можно реализовать с помощью набора функций манипулирования классами, которые к тому же имеют одинаковые сигнатуры. Первым параметром в эти функции переда ется интересующий узел DOM, а вторым – имя класса, которым требуется манипулировать. Например, чтобы добавить в узел стилевой класс, достаточно выполнить такой вызов: dojo.addClass("foo", "some-ClassName"). Обратите внимание, что в имени класса отсутствует ведущий символ точки, которая необходима при определении класса в таблицах стилей.

Добавлено: 24 Июля 2018 08:37:32 Добавил: Андрей Ковальчук

Селективность

Необходимость предотвратить возможность выделения текста на странице с помощью мыши возникает не так уж редко, и это иногда действительно способствует увеличению простоты и удобства использования. Фактически в каждом броузере имеется свой собственный способ решить эту задачу, но это не повод для беспокойства, когда у вас имеется Dojo. Всякий раз, когда в этом возникает необходимость, достаточно просто вызвать функцию dojo.setSelectable. Ниже приводится сигнатура этой функции:

dojo.setSelectable(/*String | DomNode*/node, /*Boolean*/selectable)

Добавлено: 24 Июля 2018 08:37:06 Добавил: Андрей Ковальчук

Манипулирование контекстом объекта

Глобальный объект window обеспечивает в вебприложениях самый внешний уровень контекста, но иногда бывает необходимо изменить контекст по умолчанию на какойто другой. Например, у вас может возникнуть потребность сохранять состояние сеанса, когда пользователь завершает работу с приложением. Или у вас, может быть, какая то особенная среда выполнения, которая уже настроена под определенные обстоятельства. Тогда вместо того чтобы каждый раз заставлять программный код выполнять проверку всех условий для настройки среды, вы могли бы средствами библиотеки Base поменять один существующий контекст на другой.

Следующая функция позволяет изменять объект dojo.global и dojo.doc по своему усмотрению. Обратите внимание: по умолчанию dojo.doc – это простая ссылка на window.document, но несмотря на это, она обеспечивает единообразный способ идентификации контекста текущего документа, что опять же может быть весьма удобно в ситуациях, когда вопрос касается управления контекстами объектов. Функция dojo.body() –
это сокращенный способ получить ссылку на тело документа.

Вам необходимо знать о существовании как минимум трех функций из библиотеки Base, чтобы успешно управлять контекстом:

dojo.doc //Возвращает Document
dojo.body() //Возвращает DomNode
dojo.setContext(/*Object*/globalObject, /*Document*/globalDocument)


Наконец, для большей гибкости библиотека Base предоставляет две функции, позволяющие вызывать функцию либо в контексте другого окружения dojo.global, либо в контексте другого документа dojo.doc,
отличных от текущих.
dojo.withGlobal(/*Object*/globalObject, /*Function*/callback,
/*Object*/thisObject, /*Array*/callbackArgs)
dojo.withDoc(/*Object*/documentObject, /*Function*/callback,
/*Object*/thisObject, /*Array*/callbackArgs)


Следует заметить, что применение функций Dojo для активной работы в другом объекте document или window еще не достаточно хорошо проверено, поэтому вы можете столкнуться с некоторыми проблемами, если пойдете этим путем. Стандартное использование предполагает загрузку инструментария Dojo в каждый документ, где он, как планируется, будет использоваться. Однако, в случае выполнения несложных операций обсуждаемые здесь функции управления контекстом работают замечательно.

Передача параметров по частям
Функция partial из библиотеки Base позволяет передавать параметры функции частями по мере их готовности и производить фактический вызов функции позднее. Или можно определить значения всех параметров сразу и передать ссылку на функцию, которую можно будет вызвать позднее. Этот прием выглядит проще, чем передача функции вместе с параметрами, и широко используется по всему инструментальному набору. Ниже приводится сигнатура функции partial:
dojo.partial(*/Function|String*/func /*, arg1, ..., argN*/) //Возвращает Any

Для иллюстрации ниже приводится простой пример использования функции partial для передачи параметров функции, вычисляющей сумму последовательности чисел:
function addThree(x,y,z) { console.log(x+y+z);}
//передать два первых аргумента
f = dojo.partial(addThree, 100, 10);
//передать последний аргумент
f = dojo.partial(f, 1);
//а теперь вызвать функцию
f(); //111


Связывание объекта с определенным контекстом
Функция hitch из библиотеки Base очень напоминает функцию par-tial, – в том смысле, что тоже позволяет передавать параметры частями. Но у нее есть одна интересная особенность, которая заключается в том, что она позволяет связывать функцию с определенным контекстом выполнения, который будет установлен независимо от того, в каком контексте будет произведен вызов функции. Это может быть особенно удобно в ситуациях, когда имеется функция обратного вызова, и заранее никогда точно не известно, каков будет контекст (то есть значение ссылки this) при вызове функции. Сигнатура функции hitch имеет следующий вид:
dojo.hitch(/*Object*/scope, /*Function||String*/method /*, arg1, ... , argN*/)
//Возвращает Any

А для иллюстрации ниже приводится простой пример, где переопределяется контекст метода объекта:
var foo = {
name : "Foo",
greet : function() {
console.log("Hi, I'm", this.name);
}
}
var bar = {
name : "Bar",
greet : function() {
console.log("Hi, I'm", this.name);
}
}
foo.greet(); //Hi, I'm Foo
bar.greet(); //Hi, I'm Bar
/* Связать метод greet объекта bar с другим контекстом */
bar.greet = dojo.hitch(foo, "greet");
/ * Теперь объект bar будет выдавать себя за другой объект */
bar.greet(); // Hi, I'm Foo


Дополню: поскольку функция greet явно ссылается на контекст с помощью ссылки this, следующий фрагмент программного кода не переопределяет контекст метода greet:
bar.greet = foo.greet;
bar.greet();


Делегирование и наследование
Делегирование – это шаблон программирования, когда при выполнении действия один объект опирается на другой объект, а не предусматривает собственную реализацию этого действия. Делегирование – это самое сердце JavaScript как языка, основанного на прототипах, потому что делегирование – это именно тот прием, благодаря которому отыскиваются свойства объекта в цепочке прототипов. Несмотря на то, что делегирование составляет самую основу реализации наследования в JavaScript, которая опирается на поиск в цепочке прототипов во время выполнения, тем не менее делегирование по своей сути совершенно отличается от наследования в настоящих объектноориентированных языках программирования, таких как Java и C++, в которых часто (но далеко не всегда) разрешение имен в иерархии классов выполняется на этапе компиляции, а не во время выполнения. В этом смысле особенно примечательно, что, будучи особенностью времени выполнения, делегирование в обязательном порядке опирается на механизм динамического связывания как на особенность языка.

Функция delegate инструментального набора Dojo реализует механизм делегирования функции объекта и имеет следующую сигнатуру:
dojo.delegate(/*Object*/delegate, properties) //Возвращает Object


Основываясь на предыдущем примере, следующий отрывок демонстрирует, как можно с помощью делегирования получить объект, который делегирует вызов функции другому объекту:
function Foo() {
this.talk = function() {console.log("Hello, my name is", this.name);}
}
// Получить объект типа Function, имеющий свойство name,
// но передает или делегирует обязательство вызова функции talk
// экземпляру Foo, который передается функции delegate.
var bar = dojo.delegate(new Foo, {name : "Bar"});
// Вызывается метод делегата Foo
bar.talk();

Добавлено: 24 Июля 2018 08:36:48 Добавил: Андрей Ковальчук

Утилиты для работы с объектами JavaScript

В библиотеке Base имеются три утилиты, упрощающие выполнение операций, которые обычно выполняются над объектами: mixin, extend и clone.

Классы-смеси
Язык JavaScript позволяет создавать нечто, напоминающее классы, помещая совокупности свойств и методов внутрь функцииконструктора. После этого вы можете создавать экземпляры таких классов, вызывая функциюконструктор с оператором new. Как можно предположить, иногда бывает очень удобно иметь возможность добавлять к объектам дополнительные свойства – либо в ответ на происходящие события, либо как часть хорошо продуманного дизайна, с целью максимально повысить возможность многократного использования программного кода. В любом из этих случаев функция mixin принимает на себя основные сложности реализации.

Функция mixin принимает неопределенное число объектов, первый из которых служит объектом, к которому примешиваются остальные объекты:

dojo.mixin(/*Object*/ o, /*Object*/ o, ...) //Возвращает Object

Ниже приводится пример использования функции mixin:
function Man() {
this.x = 10;
}
function Myth() {
this.y = 20;
}
function Legend() {
this.z = 30;
}
var theMan = new Man;
var theMyth = new Myth;
var theLegend = new Legend;
function ManMythLegend() {}
var theManTheMythTheLegend = new ManMythLegend;
//видоизменить объект theManTheMythTheLegend, смешав в нем три других объекта
dojo.mixin(theManTheMythTheLegend, theMan, theMyth, theLegend);


Расширение прототипов объектов
Функция extend из библиотеки Base работает точно так же, как функция mixin, но, в отличие от последней, она добавляет все свойства и методы смеси к прототипу функции!конструктора. Вследствие этого все последующие экземпляры, созданные с помощью такого конструктора, автоматически будут приобретать все эти новые свойства и методы:
dojo.extend(/*Function*/constructor, /*Object*/props, ... )
//Возвращает Function

Ниже приводится пример использования функции extend:
function Man() {
this.x = 10;
}
function Myth() {
this.y = 20;
}
function Legend() {
this.z = 30;
}
var theMan = new Man;
var theMyth = new Myth;
var theLegend = new Legend;
function ManMythLegend() {}
var theManTheMythTheLegend = new ManMythLegend;
dojo.extend(ManMythLegend, theMan, theMyth, theLegend);
var theTheManTheMythTheLegend = new ManMythLegend;


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

Еще одна важная область применения функции extend – обеспечение более простой возможности создавать классы, чем обычно; когда все, что необходимо, реализуется через свойство prototype объекта. Использование функции extend таким способом – это, скорее, вопрос вкуса, хотя конечный результат при этом обычно получается более компактным. Ниже с целью демонстрации приводится переделанная версия нашего волшебного сценария из примера 2.6:
dojo.provide("dtdg.Genie");
//определить объект
dtdg.Genie = function() {}
//и расширить его
dojo.extend(dtdg.Genie, {
_predictions : [
"As I see it, yet",
"Ask again later",
"Better not tell you now",
"Cannot predict now",
"Concentrate and ask again",
"Don't count on it",
"It is certain",
"It is decidedly so",
"Most likely",
"My reply is no",
"My sources say no",
"Outlook good",
"Outlook not so good",
"Reply hazy, try again",
"Signs point to yes",
"Very doubtful",
"Without a doubt",
"Yes",
"Yes - definitely",
"You may rely on it"
],
initialize : function() {
var label = document.createElement("p");
label.innerHTML = "Ask a question. The genie knows the answer...";
var question = document.createElement("input");
question.size = 50;
var button = document.createElement("button");
button.innerHTML = "Ask!";
button.onclick = function() {
alert(dtdg.Genie.prototype._getPrediction());
question.value = "";
}
var container = document.createElement("div");
container.appendChild(label);
container.appendChild(question);
container.appendChild(button);
dojo.body().appendChild(container);
},
getPrediction : function() {
//получить псевдослучайное число в диапазоне от 0 до 19,
//которое будет играть роль индекса предсказания
var idx = Math.round(Math.random()*19)
return this._predictions[idx];
}


Клонирование объектов
Язык JavaScript предусматривает создание лишь поверхностных копий, поэтому, когда в операциях присваивания участвуют объекты JavaScript и узлы DOM, часто возникает необходимость в клонировании, или создании полных копий иерархий объектов. В библиотеке Base имеется функция clone, которая обеспечивает высокоэффективный способ копирования. Взгляните на следующий простой пример:
function foo() {
this.bar = "baz";
}
var foo1 = new foo;
var foo2 = foo1; //shallow copy
console.log(foo1.bar);
console.log(foo2.bar);
foo1.bar = "qux"; //изменение foo1 приводит к изменению foo2
console.log(foo1.bar); // qux
console.log(foo2.bar); // qux
foo3 = new foo
foo4 = dojo.clone(foo3); //полная копия
foo3.bar = "qux";
console.log(foo3.bar); // qux
console.log(foo4.bar); // baz

Добавлено: 24 Июля 2018 08:35:29 Добавил: Андрей Ковальчук

Создание примера модуля волшебного джинна

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

Прежде чем приступить к созданию модуля, вспомните, что всегда рекомендуется использовать пространства имен. Пример 2.6 помещает программный код ресурса Genie в пространство имен dtdg, которое мы уже использовали в этой книге. Если вы еще не создали локальный каталог с именем dtdg, то сделайте это прямо сейчас. Внутри этого каталога создайте файл с именем Genie.js, куда скопируйте волшебный программный код, представленный в примере 2.6.

Пример 2.6. Реализация модуля волшебного джинна

//всегда начинайте модуль с инструкции dojo.provide
dojo.provide("dtdg.Genie");
//создание пространства имен для джинна
dtdg.Genie = function() {}
//набор предсказаний чемто напоминает 8 волшебных шаров
dtdg.Genie.prototype._predictions = [
"As I see it, yes",
"Ask again later",
"Better not tell you now",
"Cannot predict now",
"Concentrate and ask again",
"Don't count on it",
"It is certain",
"It is decidedly so",
"Most likely",
"My reply is no",
"My sources say no",
"Outlook good",
"Outlook not so good",
"Reply hazy, try again",
"Signs point to yes",
"Very doubtful",
"Without a doubt",
"Yes",
"Yes - definitely",
"You may rely on it"
];
//функция инициализации, конструирующая интерфейс
dtdg.Genie.prototype.initialize = function() {
var label = document.createElement("p");
label.innerHTML = "Ask a question. The genie knows the answer...";
var question = document.createElement("input");
question.size = 50;
var button = document.createElement("button");
button.innerHTML = "Ask!";
button.onclick = function() {
alert(dtdg.Genie.prototype._getPrediction());
question.value = "";
}
var container = document.createElement("div");
container.appendChild(label);
container.appendChild(question);
container.appendChild(button);
dojo.body().appendChild(container);
}
//основная функция реализации взаимодействия
dtdg.Genie.prototype._getPrediction = function() {
//получить псевдослучайное число в диапазоне от 0 до 19,
//которое будет играть роль индекса предсказания
var idx = Math.round(Math.random()*19)
return this._predictions[idx];
}


Фактически этот пример не делает ничего, кроме как создает объект типа Function с именем dtdg.Genie и определяет одну «общедоступ ную» функцию initialize.

Пример содержит комментарии, и, с точки зрения вебразработчика, его логика должна быть достаточно проста для понимания. (Если это не так, то, прежде чем продолжить чтение, вам стоит ознакомиться с основами HTML и JavaScript с помощью какойнибудь другой книги.) Чтобы ввести джинна в действие, необходимо изменить базовый шаблон вебстраницы, как показано в примере 2.7.
Пример 2.7. Веб!страница, предназначенная для общения
с волшебным джинном
<html>
<head>
<title>Fun With the Genie!</title>
<script
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
djConfig="modulePaths:{dtdg:'./dtdg',baseUrl:'./'}">
</script>
<script type="text/javascript">
// загрузить модуль
dojo.require("dtdg.Genie");
// обеспечить безопасное обращение к dtdg.Genie внутри addOnLoad
dojo.addOnLoad(function() {
//создать экземпляр
var g = new dtdg.Genie;
//инициализировать, а все остальное он сделает сам
g.initialize();
});
</script>
</head>
(body)
(/body)
</html>


Этот пример демонстрирует возможность многократного использова ния и переносимость модуля dtdg.Genie. Вам нужно просто загрузить модуль в странице, и после инициализации он будет «просто работать». (И пока пользователь не увидит исходный текст, он будет считать его понастоящему волшебным.) Наиболее сложная часть сценария, тре бующая дополнительного пояснения, – это использование djConfig для определения параметров настройки Dojo перед загрузкой инструмента рия: параметр modulePaths определяет местоположение модуля относи тельно параметра baseUrl, значение которого соответствует текущему рабочему каталогу.

Добавлено: 24 Июля 2018 08:34:31 Добавил: Андрей Ковальчук

Пример с числами Фибоначчи при использовании локальной версии инструментария

Для сравнения в примере 2.5 приводится тот же самый пример, но на этот раз используется локальная версия инструментария Dojo, а модуль dtdg находится в корневом каталоге инструментария рядом с каталогом dojo, содержащим библиотеку Core, поэтому нет необходимо сти изменять параметр настройки baseUrl или вызывать функцию re-gisterModulePath. Это возможно потому, что Dojo автоматически пытается искать модули в каталоге с библиотекой Core, который является логичным и удобным местоположением для них.
Пример 2.5. Использование модуля dtdg.foo совместно с локальной инсталляцией инструментария

<html>
<head>
<title>Fun With Fibonacci!</title>
<script
type="text/javascript"
src="your/relative/path/from/this/page/to/dojo/dojo.js" >
</script>
<script type="text/javascript">
dojo.require("dtdg.foo");
/* мы по привычке применяем блок addOnLoad, даже
при использовании локальной версии */
dojo.addOnLoad(function() {
dojo.body().innerHTML = "guess what? fibonacci(5) = " +
dtdg.foo.fibonacci(5);
});
</script>
</head>
(body)
(/body)
</html>

Добавлено: 24 Июля 2018 08:33:54 Добавил: Андрей Ковальчук

Пример создания модуля для применения в составе версии XDomain

Рассмотрим небольшой конкретный пример использования инструкций dojo.require и dojo.provide. Для начала рассмотрим простой модуль, реализующий такую тривиальную функцию, как функцию вычисления членов последовательности Фибоначчи. В примере 2.3 ресурс связан с модулем. И хотя группировка ресурсов в модули не является строго обязательной, тем не менее практически всегда это целесообразно. В этой книге вам часто будет встречаться имя dtdg (от «Dojo: The Definitive Guide1»), используемое в качестве общего пространства имен для модулей.

Пример 2.3. Определение простого модуля (dtdg.foo)

/*
Инструкция dojo.provide указывает, что файл .js с исходным программным кодом образует модуль dtdg.foo. Семантически модуль dtdg.foo также образует пространство имен для функций, входящих в состав модуля. На диске этот файл мог бы иметь имя foo.js и находиться в каталоге dtdg.
*/
dojo.provide("dtdg.foo");
//Обратите внимание: функции объявляются относительно пространства имен модуля
dtdg.foo.fibonacci = function(x) {
if (x < 0)
throw Exception("Illegal argument");
if (x <= 1)
return x;
return dtdg.foo.fibonacci(x-1) + dtdg.foo.fibonacci(x-2);
}


Предположим, когданибудь, при создании другой страницы, вы решили использовать свой модуль dtdg.foo, чтобы удивить юных математиков из начальной школы. Вместо того чтобы заново переписывать хорошо протестированную функцию и тем самым повышать вероят
ность появления ошибок, вы можете снова использовать свой модуль, подключив его с помощью dojo.require. В примере 2.4 показано, как вы можете использовать локальный модуль совместно с инструментальным набором, загружаемым из CDN. В этом примере предполагается, что этот файл HTML сохранен в каталоге, содержащем каталог dtdg, в котором находится модуль из примера 2.3.
Пример 2.4. Использование локального модуля в процедуре самонастройки XDomain
<html>
<head>
<title>Fun With Fibonacci!</title>
<script
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
djConfig="baseUrl:'./'">
</script>
<script type="text/javascript">
dojo.registerModulePath("dtdg", "./dtdg");
dojo.require("dtdg.foo");
/* с этого момента dojo.require выполняется асинхронно, потому что используется Dojo в сборке Xdomain. Будет лучше, если все обращения к dtdg.foo будут происходить в addOnLoad */
dojo.addOnLoad(function() {
dojo.body().innerHTML = "guess what? fibonacci(5) = " +
dtdg.foo.fibonacci(5);
});
</script>
</head>
(body)
(/body)
</html>


Этот пример наглядно демонстрирует, насколько просто с помощью dojo.require подключить ресурс к странице и затем использовать его. Кроме того, здесь имеется несколько интересных моментов, которые следует отметить особо.

В случае использования локальной копии инструментарий Dojo с точки зрения модулей выглядит как корневой каталог, но при использовании версии XDomain «настоящий» корневой каталог инструмента рия находится гдето на одном из серверов компании AOL. Поэтому, чтобы определить начальную точку поиска локальных модулей, в данном случае – dtdg.foo, в массиве djConfig явно задается значение параметра baseUrl.

Функция dojo.registerModulePath просто связывает пространство имен верхнего уровня (первый аргумент функции) с именем каталога относительно baseUrl (второй аргумент).

Все, что определяется в вашем модуле, станет доступно для использования в результате вызова dojo.require. Например, если предположить, что модуль dtdg.foo содержит дополнительные функции или переменные, они будут доступны после выполнения инструкции dojo.re-quire("dtdg.foo"). Как обычно, мы нигде не обращаемся к содержимому dtdg.foo за пределами блока addOnLoad.

Возможно, в предыдущем примере вы также заметили вызов функции dojo.body(). По сути, это упрощенный способ получить доступ к телу текущего документа в противоположность менее удобному методу do-cument.body.

Добавлено: 24 Июля 2018 08:29:16 Добавил: Андрей Ковальчук