Главная страница   /   14.1. Использование сокращенных методов Ajax (Pro jQuery

Pro jQuery

Pro jQuery

Адам Фриман

14.1. Использование сокращенных методов Ajax

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

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

Понимание асинхронных задач (вкратце)

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

Когда же вы выполняете асинхронную задачу, вы говорите браузеру, что вы хотите, чтобы что-то было сделано на заднем фоне. "На заднем фоне" звучит немного размыто, на самом деле вы говорите: "Сделай эту вещь, не прерывая пользователя от взаимодействия с документом, и скажи мне, когда ты это сделаешь". В случае с Ajax вы говорите браузеру, чтобы он связался с сервером и сказал вам, когда запрос будет выполнен. Это взаимодействие осуществляется через функции обратного вызова. Вы даете jQuery одну или более функций, которые будут вызваны, когда задача будет выполнена. Это будет функция для работы с удачным запросом, также могут быть другие функции для других результатов, например ошибок.

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

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

Выполнения Ajax запроса GET

Для начала мы собирается использовать Ajax, чтобы выполнить HTTP GET запрос для загрузки фрагмента HTML, который мы можем добавить к нашему документу. В листинге 14-1 представлен пример документа, с которым мы будем работать.

Листинг 14-1: Пример документа
<!DOCTYPE html>
<html>
<head>
	<title>Example</title>
	<script src="jquery-1.7.js" type="text/javascript"></script>
	<script src="jquery.tmpl.js" type="text/javascript"></script>
	<script src="jquery.validate.js" type="text/javascript"></script>
	<link rel="stylesheet" type="text/css" href="styles.css" />
	<script type="text/javascript">
		$(document).ready(function () {
			// скрипт будет здесь
		});
	</script>
</head>
<body>
	<h1>Jacqui's Flower Shop</h1>
	<form method="post" action="http://node.jacquisflowershop.com/order">
		<div id="oblock">
			<div class="dtable">
				<div id="row1" class="drow">
				</div>
				<div id="row2" class="drow">
				</div>
			</div>
		</div>
		<div id="buttonDiv">
			<button type="submit">Place Order</button></div>
	</form>
</body>
</html>

Он похож на примеры, которые вы видели раньше, но здесь нет элементов для описания продукции и нет данных или шаблонов для их генерирования. Вместо этого я создал отдельный файл с именем flowers.html, который я разместил рядом с примером документа (он называется example.html в исходном коде для этой книги). В листинге 14-2 показано содержание flowers.html.

Листинг 14-2: Файл flowers.html
<div>
	<img src="astor.png" /><label for="astor">Astor:</label>
	<input name="astor" value="0" required />
</div>
<div>
	<img src="daffodil.png" /><label for="daffodil">Daffodil:</label>
	<input name="daffodil" value="0" required />
</div>
<div>
	<img src="rose.png" /><label for="rose">Rose:</label>
	<input name="rose" value="0" required />
</div>
<div>
	<img src="peony.png" /><label for="peony">Peony:</label>
	<input name="peony" value="0" required />
</div>
<div>
	<img src="primula.png" /><label for="primula">Primula:</label>
	<input name="primula" value="0" required />
</div>
<div>
	<img src="snowdrop.png" /><label for="snowdrop">Snowdrop:</label>
	<input name="snowdrop" value="0" required />
</div>

Это те же самые элементы, которые мы использовали в предыдущих главах, за исключением того, что они не разбиты по рядам, и я удалил атрибут class из элементов div. Я сделал эти изменения только потому, что я могу показать вам, как работать с элементами, как только вы их подгрузите. Для этого нет никакой технической причины. Обратите внимание, что это не полный HTML документ, только лишь фрагмент.

Теперь вы можете использовать поддержку jQuery для Ajax, чтобы внести фрагмент HTML в основной HTML документ. Это действие может показаться немного странным, но сейчас мы симулируем общую ситуацию, когда различные куски контекста создаются различными системами, и их нужно объединить, чтобы создать сложный документ или веб приложение. В этом примере для простоты я использую только один сервер, но ведь можно себе представить, что информация о продукции приходит откуда-то еще. На самом деле, в следующих разделах я введу Node.js, чтобы показать вам, как работать с несколькими серверами. Это все впереди. На данный момент, давайте рассмотрим базовую jQuery поддержку для Ajax и будем использовать ее для работы с файлом flowers.html. В листинге 14-3 показано, как мы можем это сделать.

Листинг 14-3: Использование jQuery поддержки для Ajax с фрагментом HTML
<script type="text/javascript">
	$(document).ready(function () {
		$.get("flowers.html",
			function (data) {
				var elems = $(data).filter('div').addClass("dcell");
				elems.slice(0, 3).appendTo('#row1');
				elems.slice(3).appendTo("#row2");
			});
	});
</script>

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

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

Когда вы загружаете документ, содержащий этот скрипт, файл flowers.html подгружается с сервера, и HTML фрагмент, который он содержит, разбивается на элементы, и потом они добавляются в документ. На рисунке 14-1 показан результат.

Рисунок 14-1: Результат использования Ajax

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

Совет

Хотя я использовал метод get, чтобы загрузить HTML, он может быть использован, чтобы получить с сервера любой вид данных.

Обработка ответных данных

Аргументом, переданным функции (если запрос прошел удачно), являются данные, которые сервер отправил обратно в ответ на наш запрос. В этом примере вы получаете содержание файла flowers.html, который является HTML фрагментом.

Чтобы сделать это чем-то, что я могу использовать с jQuery, я передал данные в jQuery $ функцию. Таким образом они будут разбиты и сгенерированы в иерархии HTMLElement объектов, как показано в листинге 14-4.

Листинг 14-4: Обработка данных, полученных с сервера
<script type="text/javascript">
	$(document).ready(function () {
		$.get("flowers.html",
			function (data) {
				var elems = $(data).filter('div').addClass("dcell");
				elems.slice(0, 3).appendTo('#row1');
				elems.slice(3).appendTo("#row2");
			});
	});
</script>

Как я упоминал ранее, я опустил атрибуты class элементов div. Вы видите, что я добавил их обратно, используя стандартный jQuery метод addClass. Как только данные отправлены $ функции, вы можете использовать возвращенный объект jQuery, как и любой другой. Я продолжаю добавлять элементы в документ, используя методы slice и appendTo, как я делал в предыдущих главах.

Совет

Обратите внимание, что я использовал метод filter, чтобы выбрать только элементы div, сгенерированные из данных. При парсинге данных jQuery считает, что символы возврата каретки, которые я добавил для структуры между элементами div в файле flowers.html, является текстовым содержанием и создает для него текстовый элемент. Чтобы избежать этого, вам либо стоит убедиться, что в требуемом документе нет символов возврата каретки, либо использовать метод filter, чтобы удалить их.

Делаем результат более очевидным

Выражения, которые запускают Ajax запрос, выполняются в ответ на событие ready (о котором я рассказывал в главе 9). И поэтому сложно визуально представить, чем использование Ajax отличается от использования встроенных данных. Чтобы более наглядно это представить, я добавил в документ элемент button, и запрос Ajax будет выполнен только тогда, когда будет нажат этот элемент button (кнопка). Изменения вы можете увидеть в листинге 14-5.

Листинг 14-5: Выполнение Ajax запроса в ответ на нажатие кнопки
<script type="text/javascript">
	$(document).ready(function () {
		$('<button>Ajax</button>').appendTo('#buttonDiv').click(function (e) {
			$.get("flowers.html",
				function (data) {
					var elems = $(data).filter("div").addClass("dcell");
					elems.slice(0, 3).appendTo('#row1');
					elems.slice(3).appendTo("#row2");
				});
			e.preventDefault();
		});
	});
</script>

Теперь документ flowers.html не загружается, пока не нажата кнопка; и каждый раз, когда на нее нажимают, в документ добавляются новые элементы, как показано на рисунке 14-2. Обратите внимание, что я вызвал метод preventDefault для объекта Event, который передается в мою функцию обработки событий. Поскольку элемент button содержится в элементе form, действием по умолчанию является отправка формы на сервер.

Рисунок 14-2: Использование Ajax в ответ на нажатие кнопки

Получение других видов данных

Вы не ограничены использованием метода get только для HTML. Вы можете получать любые виды данных с сервера. Особенно нам интересен JSON из-за способа, которым jQuery успешно обрабатывает для вас данные. В те времена, когда Ajax начал широко применяться, XML был настолько популярным форматом данных, что X в Ajax обозначает XML. Я не собираюсь детально рассматривать XML, он излишне подробный, его тяжело читать, и он требует относительно много времени и ресурсов для генерирования и обработки.

В последнее время XML был в значительной степени заменен JavaScript Object Notation (JSON), который является простым форматом данных и исключительно прост в работе с кодом JavaScript (как и видно по его имени). Для этого примера я создал файл mydata.json и сохранил его на веб сервере вместе с файлом example.html. В листинге 14-6 показано содержание mydata.json.

Листинг 14-6: Содержание mydata.json
[{ "name": "Astor", "product": "astor", "stocklevel": "10", "price": "2.99" },
{ "name": "Daffodil", "product": "daffodil", "stocklevel": "12", "price": "1.99" },
{ "name": "Rose", "product": "rose", "stocklevel": "2", "price": "4.99" },
{ "name": "Peony", "product": "peony", "stocklevel": "0", "price": "1.50" },
{ "name": "Primula", "product": "primula", "stocklevel": "1", "price": "3.12" },
{ "name": "Snowdrop", "product": "snowdrop", "stocklevel": "15", "price": "0.99" }]

Файл содержит данные для продукции цветочного магазина, и как вы видите, данные JSON идентичны тому способу, которым вы представляли данные внутри JavaScript кода. Это одна из причин, почему JSON заменил в веб приложениях XML. Чтобы подгрузить и обработать эти данные с использованием Ajax, вы можете использовать метод get, как показано в листинге 14-7.

Листинг 14-7: Использование метода get для получения данных JSON
<script type="text/javascript">
	$(document).ready(function () {
		$('<button>Ajax</button>').appendTo('#buttonDiv').click(function (e) {
			$.get("mydata.json", function (data) {
				var template = $('#flowerTmpl');
				template.tmpl(data.slice(0, 3)).appendTo("#row1");
				template.tmpl(data.slice(3)).appendTo("#row2");
			});
			e.preventDefault();
		});
	});
</script>
<script id="flowerTmpl" type="text/x-jquery-tmpl">
	<div class="dcell">
		<img src="${product}.png" />
		<label for="${product}">${name}:</label>
		<input name="${product}" data-price="${price}" data-stock="${stocklevel}"
			value="0" required />
	</div>
</script>

В этом примере я запрашиваю файл с данными JSON в ответ на нажатие элемента button. Данные, полученные с сервера, добавляются в функцию, так же как и с фрагментом HTML. Я использовал плагин шаблона данных (описанный в главе 12), чтобы обработать данные и сгенерировать из них HTML элементы, а затем использовал методы slice и appendTo, чтобы вставить элементы в документ. Обратите внимание, что мне не нужно было ничего делать, чтобы конвертировать строку JSON в объект JavaScript. jQuery сделал это для меня автоматически.

Совет

Некоторые веб серверы (включая Microsoft IIS 7.5, который я использовал для данной книги) не вернут содержание браузеру, если они не узнают расширение файла или формат данных. Чтобы этот пример работал с IIS, я должен был создать новое соответствие между расширением файла (.json) и типом MIME для данных JSON (application/json). Пока я этого не сделал, IIS возвращал ошибки 404 – Not Found (не найдено), когда запрашивался mydata.json.

Предоставление данных по запросу GET

Данные можно пересылать на сервер как часть ваших запросов GET, которые являются видами запросов, сделанных методами get, load, getScript и getJSON. Этого можно добиться, если добавить объект данных сокращенному методу, который вы используете. В листинге 14-8 представлен пример.

Листинг 14-8: Отправка данных как часть запроса GET
<script type="text/javascript">
	$(document).ready(function () {
		var requestData = {
			country: "US",
			state: "New York"
		}

		$.get("flowers.html", requestData,
		function (responseData) {
			var elems = $(responseData).filter('div').addClass("dcell");
			elems.slice(0, 3).appendTo('#row1');
			elems.slice(3).appendTo("#row2");
		});
	});
</script>

Данные, которые вы предоставляете, прилагаются к указанному URL как строка параметров. Для этого примера это обозначает, что ваш запрос будет таким:

http://www.jacquisflowershop.com/jquery/flowers.html?country=US&state=New+York

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

GET и POST: как выбрать правильный

Если пытаться отправлять данные формы, используя запрос GET, будьте осторожны. Правило большого пальца гласит, что запросы GET могут быть использованы только для получения информация "только для чтения" (read-only), тогда как запросы POST должны быть использованы для любых действий, которые меняют статус приложения.

В стандартных условиях запросы GET предназначаются для безопасного взаимодействия (без каких-либо побочных эффектов, кроме получения информации), а запросы POST предназначаются для небезопасного взаимодействия (принятие решения или изменение чего-либо). Эти соглашения были установлены World Wide Web Consortium (W3C), на www.w3.org/Provider/Style/URI.

Таким образом, запросы GET можно использовать для отправки данных формы на сервер, но не для операций, которые меняют статус. Многие веб разработчики убедились в этом на собственном горьком опыте в 2005 году, когда вышел публичный релиз Google Web Accelerator. Это приложение переходит по всем ссылкам, представленным на странице, и это законно для HTTP, потому что запросы GET должны быть безопасными. К сожалению, многие разработчики игнорировали HTTP соглашения и размещали простые ссылки в своих приложениях для "удалить товар" или "добавить в корзину". Начался хаос.

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

Выполнение Ajax запроса POST

Теперь, когда мы знаем, как получать данные с сервера, мы обратим внимание на то, как их отправлять, иными словами, как оправлять данные формы на сервер. Еще раз, существует сокращенный метод post, который делает отправку очень простой. Прежде чем мы обратимся к этому методу, нам нужно настроить наш сервер, что означает, еще раз вернуться к Node.js и понять, как работать в рамках функций безопасности, которые браузер применяет к Ajax запросам POST.

Подготовка Node.js для получения данных из формы

Для этой части главы нам нужен серверный скрипт, который будет получать данные, отправленные из браузера при помощи HTTP метода POST, совершать с отправленными данными некоторые простые действия и генерировать ответ. В листинге 14-9 показан скрипт Node.js для этого раздела.

Листинг 14-9: Скрипт Node.js для отправки данных
var http = require('http');
var url = require('url');
var querystring = require('querystring');

http.createServer(function (req, res) {
	console.log("[200 OK] " + req.method + " to " + req.url);
	if (req.method == 'OPTIONS') {
		res.writeHead(200, "OK", {
			"Access-Control-Allow-Headers": "Content-Type",
			"Access-Control-Allow-Methods": "*",
			"Access-Control-Allow-Origin": "*"
		});
		res.end();

	} else if (req.method == 'POST') {
		var dataObj = new Object();
		var contentType = req.headers["content-type"];
		var fullBody = '';

		if (contentType) {
			if (contentType.indexOf("application/x-www-form-urlencoded") > -1) {
				req.on('data', function (chunk) { fullBody += chunk.toString(); });
				req.on('end', function () {
					var dBody = querystring.parse(fullBody);
					writeResponse(req, res, dBody,
url.parse(req.url, true).query["callback"])
				});
			} else {
				req.on('data', function (chunk) { fullBody += chunk.toString(); });
				req.on('end', function () {
					dataObj = JSON.parse(fullBody);
					var dprops = new Object();
					for (var i = 0; i < dataObj.length; i++) {
						dprops[dataObj[i].name] = dataObj[i].value;
					}
					writeResponse(req, res, dprops);
				});
			}
		}
	} else if (req.method == "GET") {
		var data = url.parse(req.url, true).query;
		writeResponse(req, res, data, data["callback"])
	}
	console.log("Ready on port 80");
}).listen(80);

function writeResponse(req, res, data, jsonp) {
	var total = 0;
	for (item in data) {
		if (item != "_" && data[item] > 0) {
			total += Number(data[item]);
		} else {
			delete data[item];
		}
	}
	data.total = total;
	jsonData = JSON.stringify(data);
	if (jsonp) {
		jsonData = jsonp + "(" + jsonData + ")";
	}
	res.writeHead(200, "OK", {
		"Content-Type": "application/json",
		"Access-Control-Allow-Origin": "*"
	});
	res.write(jsonData);
	res.end();
}

Я сохранил этот скрипт в файле formserver.js. Самый просто способ получить этот скрипт – это скачать исходный код, сопровождающий эту книгу, который можно получить бесплатно на Apress.com. Я запускаю этот скрипт, когда ввожу в командной строке следующее:

node.exe formserver.js

Этот скрипт обрабатывает данные, отправленные браузером, и создает JSON ответ. Я мог бы вернуть этим скриптом HTML, но JSON более компактный и с ним зачастую легче работать. Объект JSON, который я возвращаю, очень простой: объект, который содержит общее число единиц продукции, которые выбрал пользователь, и число каждой из них, для которой было указано значение. Например, если я выберу одну астру, два нарцисса и три розы, ответ JSON, отравленный обратно скриптом Node.js будет следующим:

{"astor":"1","daffodil":"2","rose":"2","total":5}

Предыдущий JSON, который я показал вам, представлял массив объектов, но этот серверный скрипт возвращает единственный объект, чьи свойства соответствуют выбранным цветам. Свойство total содержит сумму отдельных выборок. Я понимаю, что это едва ли не самые ценные возможности, которые может выполнять сервер, но я хочу уделять наше внимание использованию Ajax, а не разработке серверной части.

Понимание кросс-доменных Ajax запросов

Если вы посмотрите на скрипт Node.js, вы увидите, что когда я пишу ответ на сервер, указывая HTTP заголовок вот так:

Access-Control-Allow-Origin: http://www.jacquisflowershop.com

По умолчанию браузеры ограничивают скрипты выполнением Ajax запросов внутри одного и того же абсолютного интернет адреса (origin), где находится и документ, который их содержит. Абсолютный интернет адрес – это комбинация протокола, доменного имени и порта в URL. Если два URL имеют одинаковый протокол, доменное имя и порт, тогда они находятся внутри одного абсолютного интернет адреса. Если же один из трех компонентов иной, они находятся по разным абсолютным интернет адресам.

Совет

Эта политика направлена на снижение рисков ХSS (сross-site scripting, межсайтовый скриптинг) атак, когда браузер (или пользователь) обманом вовлечен в выполнение вредоносного скрипта. ХSS атаки выходят за рамки данной книги, но есть хорошая статья в Википедии на http://en.wikipedia.org/wiki/Cross-site_scripting, которая дает подробную информацию по этому вопросу.

В таблице 14-2 представлено сравнение некоторых URL с URL главного документа примера, то есть www.jacquisflowershop.com/jquery/example.html.

Таблица 14-2: Сравнение URL
URL Сравнение абсолютного интернет адреса
http://www.jacquisflowershop.com/apps/mydoc.html Тот же адрес
https://www.jacquisflowershop.com/apps/mydoc.html Другой адрес; различаются протоколы
http://www.jacquisflowershop.com:81/apps/mydoc.html Другой адрес; различаются порты
http://node.jacquisflowershop.com/order Другой адрес; различаются полные имена доменов

В моей конфигурации у меня есть два сервера. www.jacquisflowershop.com работает со статическим содержанием, а node.jacquisflowershop.com работает с Node.js. Как видно из таблицы, документ с первого сервера имеет другой абсолютный интернет адрес, нежели со второго. Запрос с одного абсолютного интернет адреса на второй известен как кросс-доменный запрос.

Проблема этой политики заключается в том, что она несет в себе полный запрет: кросс-доменных запросов нет. Это привело к использованию некоторых очень некрасивых уловок, чтобы обмануть браузер в создании запросов, которые противоречат политике. К счастью, сейчас есть законные средства обеспечения кросс-доменных запросов, определенных в спецификации CORS (Cross-Origin Resource Sharing). Я хочу кратко описать CORS. Для полной информации по CORS вам стоит обратиться к www.w3.org/TR/cors.

Совет

Спецификация CORS довольно молода. Она поддерживается текущим поколением браузеров, а старые браузеры просто будут игнорировать кросс-доменные запросы. Более признанным и упрочившимся подходом является использование JSONP, о чем я расскажу в разделе "Работа с JSONP".

Способ, которым работает CORS, заключается в том, что браузер устанавливает связь с другим сервером (для нас это сервер Node.js) и включает в запрос заголовок Origin. Значением этого заголовка является абсолютный интернет адрес того документа, который привел к тому, чтобы был сделан запрос.

Если сервер распознает абсолютный интернет адрес и хочет разрешить браузеру сделать кросс-доменный запрос, тогда он добавляет заголовок Access-Control-Allow-Origin, устанавливая значение, подходящее заголовку Origin из запроса. Если ответ не содержит этот заголовок, тогда браузер отвергает ответ.

Совет

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

В скрипте Node.js я вручную устанавливаю заголовок Access-Control-Allow-Origin для моего доверенного абсолютного интернет адреса www.jacquisflowershop.com, но вы просто можете использовать значение заголовка Origin в запросе, чтобы следовать более сложному процессу принятия решений. Также к заголовку Access-Control-Allow-Origin можно поставить звездочку (*), что обозначает, что кросс-доменные запросы будут разрешены с любого абсолютного интернет адреса. Это отлично работает в целях тестирования, но вы должны хорошенько подумать о последствиях для безопасности, прежде чем использовать такие настройки для публикации.

Использование метода post для отправки данных из формы

Теперь, когда мы настроили сервер и поняли, что такое CORS, мы можем использовать метод post, чтобы отправлять данные формы на сервер, как показано в листинге 14-10.

Листинг 14-10: Отправка данных методом post
<script type="text/javascript">
	$(document).ready(function () {
		$('button').get(0).disabled = true;

		$.getJSON("mydata.json", function (data) {
			var template = $('#flowerTmpl');
			template.tmpl(data.slice(0, 3)).appendTo("#row1");
			template.tmpl(data.slice(3)).appendTo("#row2");
			$('button').get(0).disabled = false;
		});

		$('button').click(function (e) {
			var formData = $('form').serialize();
			$.post("http://node.jacquisflowershop.com/order", formData,
				function (data) {
					processServerResponse(data);
				})
			e.preventDefault();
		})

		function processServerResponse(data) {
			var inputElems = $('div.dcell').hide();

			for (var prop in data) {
				var filtered = inputElems.has('input[name=' + prop + ']')
					.appendTo("#row1").show();
			}

			$('#buttonDiv, #totalDiv').remove();
			$('#totalTmpl').tmpl(data).appendTo('body');
		}
	});
</script>
<script id="totalTmpl" type="text/x-jquery-tmpl">
	<div id="totalDiv" style="clear: both; padding: 5px">
		<div style="text-align: center">Total Items: <span id="total">${total}</span></div>
		<div id="buttonDiv"><button type="submit">Place Order</button></div>
	</div>
</script>
<script id="flowerTmpl" type="text/x-jquery-tmpl">
	<div class="dcell">
		<img src="${product}.png" />
		<label for="${product}">${name}:</label>
		<input name="${product}" data-price="${price}" data-stock="${stocklevel}" value="0" required />
	</div>
</script>

Этот пример кажется более сложным, нежели он есть на самом деле. Я начал с использования метода getJSON, чтобы получить файл mydata.json, который содержит информацию о цветочной продукции, а затем использовал шаблон данных, чтобы сгенерировать элементы и добавить их в документ. Как видно по рисунку 14-3, это дает нам отправную точку, которую мы знаем и любим.

Рисунок 14-3: Начальная точка для отправки данных на сервер

Вы видите, что я добавил некоторые значения в элементы input: 12 астр, 20 нарциссов и 4 примулы. Я использую метод click, чтобы зарегистрировать функцию, которая будет вызвана, когда пользователь нажмет на элемент button, вот так:

<script type="text/javascript">
	$('button').click(function (e) {
		var formData = $('form').serialize();

		$.post("http://node.jacquisflowershop.com/order", formData,
			function (data) {
				processServerResponse(data);
			})

		e.preventDefault();
	})
</script>

Первая вещь, которую я делаю, это вызываю метод serialize для элемента form. Это очень полезный метод, который проходит по всем элементам form и создает URL-кодированную строку, которую вы можете отправить на сервер.

Совет

Обратите внимание, что я вызываю метод preventDefault для объекта Event, который передается моей функции обработки событий для события click. Мне нужно сделать это, чтобы не дать браузеру отправить форму обычным путем, то есть отправить данные и загрузить ответ как новый документ.

Для значений, которые я ввел, метод serialize создает строку, наподобие этой:

astor=12&daffodil=20&rose=0&peony=0&primula=4&snowdrop=0

Я использую метод serialize, потому что метод post отправляет данные в URL-кодированном формате (хотя это можно изменить, если использовать глобальный метод обработки событий ajaxSetup, который я опишу в главе 15). Поскольку у меня есть данные из элементов input, я вызываю метод post, чтобы инициировать запрос Ajax.

Аргументами метода post являются URL, на который я хочу отправить данные (он должен быть не тем же, что URL, указанный атрибутом action элемента form), данные, которые я хочу отправить, и функция, которая должна быть вызвана, если запрос прошел удачно. В этом примере я забираю ответ с сервера и передаю его функции processServerResponse, которая определена следующим образом:

function processServerResponse(data) {
	var inputElems = $('div.dcell').hide();
	for (var prop in data) {
		var filtered = inputElems.has('input[name=' + prop + ']')
			.appendTo("#row1").show();
	}
	$('#buttonDiv, #totalDiv').remove();
	$('#totalTmpl').tmpl(data).appendTo('body');
}

Я прячу все элементы div ячеек в верстке CSS (которые являются членами класса dcell), а затем отображаю те, которые соответствуют свойствам в объекте JSON с сервера. Я также использую шаблон данных, чтобы сгенерировать элемент для отображения общего числа выбранной продукции. Это те действия, которые вы могли совершить на стороне клиента, но суть заключается в том, что вы получили данные через Ajax запрос POST. Результат можно увидеть на рисунке 14-4.

Рисунок 14-4: Результат обработки данных, возвращенных Ajax запросом POST

Теперь вы видите, как просто отправлять данные из формы на сервер (и, естественно, как просто обрабатывать ответ, особенно если это JSON).

Совет

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

Отправка других данных при помощи метода post

Хотя метод post обычно используется для отправки данных из формы, на самом деле, можно отправлять любые данные, которые вы хотите. Вам просто нужно создать объект, который содержит ваши данные, вызвать метод serialize для надлежащего форматирования данных и затем передать их методу post. Это может быть полезным техническим приемом, если вы собираете данные от пользователей, не используя форму, или если вы хотите избирательно выбирать элементы form, которые вы включаете в POST запрос. В листинге 14-11 показано, как можно использовать метод post таким образом.

Листинг 14-11: Использование метода post для отправки данных не из формы на сервер
<script type="text/javascript">
	$(document).ready(function () {
		$('button').click(function (e) {
			var requestData = {
				apples: 2,
				oranges: 10
			};

			$.post("http://node.jacquisflowershop.com/order", requestData,
				function (responseData) {
					alert(JSON.stringify(responseData));
				})
			e.preventDefault();
		})
	});
</script>

В этом скрипте я создаю объект и явно определяю свойства. Я передаю объект методу post и использую метод alert, чтобы отобразить ответ сервера. (На самом деле сервер не волнует, какой вид данных он получает от браузера, он просто попытается сложить значения и сгенерировать сумму). Окно диалога изображено на рисунке 14-5.

Рисунок 14-5: Ответ сервера для некоторых данных не из формы

Совет

Ответ сервера в формате JSON автоматически трансформируется jQuery в объект JavaScript. Я использовал метод JSON.stringify (который поддерживается большинством браузеров) и вернул его в виде строки, таким образом, я смог отобразить его в окне диалога.

Указание ожидаемого типа данных

Когда вы используете методы get и post, jQuery должен выяснить, какой вид данных отправит сервер в ответ на ваш запрос. Это может быть что угодно, начиная от HTML и заканчивая JavaScript. Чтобы сделать это, jQuery полагается на информацию, которую сервер предоставляет в своем ответе, в частности, это заголовок Content-Type. В большинстве случаев это очень хорошо работает, но иногда jQuery нужно немножко помочь. Обычно это встречается потому, что сервер в ответе указывает неправильный тип MIME для данных.

Вы можете переписать информацию, которую предоставляет сервер, и сказать jQuery, какой тип данных вы ожидаете, если добавите дополнительный аргумент методам get или post. Этим аргументом может быть одно из следующих значений:

  • xml
  • json
  • jsonp
  • script
  • html
  • text

В листинге 14-12 показано, как можно указать ожидаемый тип данных для метода get.

Листинг 14-12: Указание ожидаемого типа данных
<script type="text/javascript">
	$(document).ready(function () {
		$.get("mydata.json",
			function (responseData) {
				console.log(JSON.stringify(responseData));
			}, "json");
	});
</script>

Вы указываете ожидаемый тип данных в качестве последнего аргумента сокращенного метода. В этом примере я сказал jQuery, что я ожидаю данные в формате JSON. Не имеете значения, что говорит сервер о типе данных. jQuery будет обрабатывать ответ как JSON. Для этого примера я вывожу результат на консоль, который выглядит следующим образом:

[{ "name": "Astor", "product": "astor", "stocklevel": "10", "price": "2.99" },
{ "name": "Daffodil", "product": "daffodil", "stocklevel": "12", "price": "1.99" },
{ "name": "Rose", "product": "rose", "stocklevel": "2", "price": "4.99" },
{ "name": "Peony", "product": "peony", "stocklevel": "0", "price": "1.50" },
{ "name": "Primula", "product": "primula", "stocklevel": "1", "price": "3.12" },
{ "name": "Snowdrop", "product": "snowdrop", "stocklevel": "15", "price": "0.99" }]

Это то же содержание, которые я поместил в файл mydata.json, и это, конечно же то, что вы надеялись увидеть. Проблема с указанием типа данных заключается в том, что вам обязательно нужно быть правыми. Если данные находятся в другом формате, это может вызвать определенные проблемы, как показано в листинге 14-13.

Листинг 14-13: Указание неправильного типа данных
<script type="text/javascript">
	$(document).ready(function () {
		$.get("flowers.html",
			function (responseData) {
				console.log(JSON.stringify(responseData));
			}, "json");
	});
</script>

В этом примере я запросил файл, который содержит HTML, но сказал jQuery, что тот должен обращаться с ним, как с JSON. Проблема заключается в том, что при работе с JSON, jQuery автоматически создает из данных JavaScript объект, чего он не может сделать с HTML. Ajax запрос заканчивается следующей ошибкой:

SyntaxError: Unexpected token <

Совет

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