Как Офис РР организовал обратную связь с читателями
Вернемся снова к нашему Офису РР, который неоднократно встречался в наших примерах, и посмотрим, как он использует интернет и Web-программирование при разработке своих документов. Конечно же, Офис РР давно уже имеет интернет-магазин для продажи своих книг, более того, он дает возможность своим читателям присылать отзывы и выставлять оценки приобретенным книгам. Чтобы связь с читателями была двусторонней, на страницах узла нашего офиса, читатели могут задавать вопросы авторам, переводчикам и редакторам выпускаемых книг, получать ответы на свои вопросы. Читатели могут также ознакомиться с отзывами, присланными на книги и просмотреть весь спектр выставленных оценок.
Давайте рассмотрим, как можно организовать обратную связь с читателями с помощью упомянутых мной средств. При этом я основное внимание сосредоточу на вопросах программирования, оставляя в стороне вопросы дизайна и многие другие вопросы, которые возникают в процессе создания коммерческого решения.
Для тех, кто хочет ознакомиться с реально работающим подобным узлом, советую зайти на узел издательства "Русская Редакция", - www.rusedit.ru. Там, правда, нет возможности в настоящее время задавать вопросы и получать ответы, но возможность заказать нужную книгу и написать отзыв имеется.
Как придать структуру программному коду
Я говорил о том, что одной из трудностей Web-программирования в нынешних условиях, является слабая структурированность программного кода, отсутствие столь привычного модульного построения программных проектов. Чтобы как-то справиться с этой проблемой, я для себя создал определенные правила. Приведу их, может быть, они будут полезны и другим:
Декларативная часть кода, исполняемого на сервере, записывается в заголовочной части HTML-текста страницы - в конец тега <head>. Декларативной части предшествует специальный комментарий. Вы могли видеть его в тексте приведенного кода.Декларативная часть кода, исполняемого на клиентской стороне, также записывается в заголовочной части сразу после серверного кода, если таковой присутствует. Этому фрагменту кода также предшествует специальный комментарий.Исполняемая часть серверного кода, снабженная комментарием, записывается в начале тега <body>.Исполняемая часть клиентского кода отсутствует. Инициируют выполнение программного кода на стороне клиента процедуры - обработчики событий.
Эти нехитрые правила хоть как-то облегчают понимание программного текста, столь важное при отладке многомерного кода. А теперь вернемся к рассмотрению того, что собой представляют две другие ASP-страницы нашего узла.
Код, исполняемый на сервере
Давайте, вначале рассмотрим код, исполняемый на стороне сервера. Это логично, поскольку он выполняется первым и от его результатов, зачастую, зависит содержание страницы, поступающей клиенту. Приведу вначале декларативную часть кода, содержащую описание глобальных переменных, описание используемых процедур и функций:
<SCRIPT LANGUAGE=vbscript RUNAT=Server> '********************************************************** '** Объявления глобальных переменных, процедур и функций ** '************ на серверной стороне ************************ 'Задание объектов ADODB - Connection, Command, Recordset Dim Con1 'Объект ADODB, задающий соединение Dim Cmd1 'Объект ADODB, задающий команду Dim Rst1 'Объект ADODB, задающий набор записей
Sub CreateConnection() 'Создание соединения с тестовой базой данных Access Dim strConnStr 'Строка соединения If Con1.State <> 1 Then strConnStr="Provider=Microsoft.jet.oledb.4.0; " & _ "Data Source =c:\Ch20\dbPP2000.mdb" Con1.Open strConnStr End If End Sub 'CreateConnection Sub CreateRstBooks() 'Создание набора записей по данным хранимого запроса With Cmd1 .ActiveConnection = Con1 .CommandText = "Select * From [список книг]" .CommandType = 1 'adCmdText Set Rst1 =.Execute End With End Sub 'CreateRstBooks Sub CreateOptions() 'Програмное создание списка SelectBook 'Заполнение элементов этого списка по данным набора записей Dim str1 'Строковая переменная - автор и название книги Dim str2,str3,str4 'Строковые константы str2 = "<OPTION value = """ str3= """>" str4="</OPTION>" With Rst1 'Проход по набору записей и формирование элементов списка .MoveFirst 'Создание элемента SelectBook Response.Write("<Select id=""SelectBook""" & _ "Style= ""position:absolute; top:100; left:170"" > ") Do While Not .EOF str1 = Rst1("Автор") & ": " & Rst1("Название") Response.Write(str2 & str1 & str3 & str1 & str4) .MoveNext Loop Response.Write("</SELECT>") End With End Sub 'CreateOptions </SCRIPT>
Ну а теперь, как всегда, некоторые комментарии к этому коду. Работа с базой данных будет вестись, как и положено, через хорошо знакомые объекты ADO. Глобальные переменные Con1, Cmd1, Rst1, задающие эти объекты, уже не один раз встречались в наших примерах. Это же касается и процедуры CreateConnection, устанавливающей соединение с базой данных. В процедуре CreateRstBooks создается набор записей, содержащий данные о книгах. Подобные процедуры также встречались неоднократно при рассмотрении объектов ADO. На одно обстоятельство хочу обратить внимание. Для получения информации из базы данных используется хранимый запрос с именем "список книг". Вот его SQL-текст:
SELECT Книги.Автор, Книги.Название FROM Книги ORDER BY Книги.Автор;
При работе с базами данных на Web-страницах рекомендую использовать всюду, где можно, хранимые процедуры. В базе данных Microsoft Access, с которой я работаю во всех примерах данной книги, роль хранимых процедур играют хранимые запросы. Помимо многих других преимуществ такой способ хорош еще и тем, что надежно работает. В то же время при использовании других приемов работы с объектами ADO зачастую возникали ошибки там, где их не должно было бы быть.
Процедура CreateOptions по данным полученного набора записей программно создает интерфейсный объект - раскрывающийся список и определяет элементы этого списка. Если говорить более точно, то процедура динамически дописывает к имеющемуся HTML-коду страницы новый фрагмент HTML-кода, определяющий тег Select. Метод Write серверного объекта Response выполняет эту задачу. Когда браузер получит страницу от сервера и начнет выполнять присланный HTML-код страницы, то он создаст в дополнение к элементам определенным при проектировании страницы, и динамически созданный сервером элемент SelectBook.
Мы рассмотрели декларативную часть серверного кода. Теперь давайте взглянем на исполняемый код. Он небольшой и сводится к инициализации переменных и вызову рассмотренных нами процедур. Вот он:
<body> <SCRIPT LANGUAGE=vbscript RUNAT=Server> '********************************************************** '** Исполняемый код на серверной стороне ** '************ на серверной стороне ************************ 'Формирование глобальных объектов Set Con1=Server.CreateObject("ADODB.Connection") Set Cmd1=Server.CreateObject("ADODB.Command") Set Rst1=Server.CreateObject("ADODB.Recordset")
' Этот оператор моделирует регистрацию пользователя. Session("UserId")=6 'Получение информации о книгах из базы данных 'и формирование списка - элемента SelectBook 'Создать соединение с базой данных CreateConnection() 'Получить набор записей CreateRstBooks() 'Создать интерфейсный элемент - список SelectBook CreateOptions
</SCRIPT>
Из этого кода хочу обратить внимание на оператор, создающий объект Session("UserId"). Реально этот объект создается на начальной индексной странице при регистрации пользователя. Поскольку я не собираюсь описывать индексную страницу, то решил промоделировать ее работу, введением этого оператора, явным образом создавая объект Session и присваивая конкретное значение Id пользователю, работающему со страницей.
Разбор кода, выполняемого на серверной стороне, закончен, и можно переходить к коду, выполняемому на стороне клиента. Возможно, следовало бы привести полный HTML-код страницы, передаваемой клиенту, но я ограничусь только программным кодом.
Как видите, это запрос с пятью параметрами. Через параметры полям записи передаются - сам вопрос, дата задания вопроса, название и автор книги, по которой был задан вопрос и идентификатор пользователя, задавшего вопрос.
Понятно, что в функции SaveQuestion, вызывающей этот запрос, при формировании объекта ADODB.Command главной задачей является корректное формирование коллекции Parameters этого объекта. Дальше остается только вызвать метод Execute, чтобы выполнить запрос. Поскольку в главах, посвященных объектам ADO, я подробно рассматривал все свойства и методы этих объектов, в том числе работу с коллекцией Parameters, то теперь останавливаться на деталях, думаю, нет необходимости.
На этом можно и закончить описание вопросов создания программного кода, да и самой страницы в целом. В заключение взгляните, как выглядит эта страница, после того как пользователь послал свою оценку книги на сервер, и эта операция была успешно выполнена.
Рис. 11.6. Страница qomToBase с уведомлением об успешном завершении операции
Код, исполняемый на стороне клиента
И на клиентской стороне можно выделить декларативную и исполняемую часть кода. Заметьте, я предпочитаю, чтобы на серверной стороне исполняемая часть кода присутствовала, в то же время на клиентской стороне ее может не быть. Более того, при хорошем программировании, полагаю, ее не должно быть, как не будет ее и в нашем примере. Причины этого, надеюсь, понятны. Дело в том, что обработка событий на стороне сервера хотя и возможна, но не представляется столь желательной. Поэтому исполняемый участок кода и предназначен для инициирования вычислений. Конечно, достаточно одного оператора, вызывающего первую процедуру, чтобы начать сложные вычисления с многочисленными вызовами процедур, представленными в декларативной части кода. При хорошем стиле программирования так, обычно, и бывает.
Программирование на клиентской стороне в большей степени является программированием, управляемым событиями. Это и понятно - здесь расположены элементы управления, с которыми работает пользователь, а именно он, его действия при работе с документом являются источником возникновения событий. Поэтому основу декларативной части клиентского кода составляют, как правило, процедуры-обработчики событий, возникающих с интерфейсными объектами и другими объектами, расположенными на странице. По этой причине нет необходимости в исполняемом фрагменте кода, хотя он и может присутствовать. Но более естественно для данного стиля программирования для инициирования вычислений иметь процедуру, обрабатывающую событие Load, которая автоматически будет вызываться при открытии страницы. Когда же пользователь начнет работать, то будут вызываться и другие обработчики событий. А теперь взгляните на декларативный код клиентской части нашей страницы. Конечно же, он включает процедуры-обработчики различных событий, возни
m° кающих с объектами страницы. Вот этот код:
<SCRIPT LANGUAGE=vbscript>
<!-- '********************************************************** '** Объявления глобальных переменных, процедур и функций ** '************ на стороне клиента ************************** Sub Window_onLoad() 'Обработчик события Load - вызывается при открытии страницы 'Заполняем скрытые поля формы - автор и название книги FromSelectToHiddenFields End Sub 'Window_onLoad
Sub SelectBook_onChange 'Обработчик события Change - вызывается при выборе книги 'в списке SelectBook 'Заполняем скрытые поля формы - автор и название книги FromSelectToHiddenFields End Sub 'SelectBook_onChange
Sub btnLookmyQ_onClick 'Обработчик события Click - вызывается при нажатии 'командной кнопки "Посмотреть мои вопросы и ответы на них" 'Вызов страницы qomFromBase с показом вопросов и ответов CallNextPage("LookmyQ") End Sub 'btnLookmyQ
Sub btnLookAllQ_onClick 'Обработчик события Click - вызывается при нажатии 'командной кнопки "Посмотреть все вопросы и ответы на них" 'Вызов страницы qomFromBase с показом всех вопросов и ответов CallNextPage("LookAllQ") End Sub 'btnLookAllQ
Sub btnLookOM_onClick 'Обработчик события Click - вызывается при нажатии 'командной кнопки "Посмотреть отзывы и оценки" 'Вызов страницы qomFromBase с показом отзывов и оценок CallNextPage("LookOM") End Sub 'btnLookOM
Sub FromSelectToHiddenFields() 'Передача данных от выбранного элемента списка книг 'к скрытым полям формы Dim str1,str2,str3 'строковые переменные Dim Ind1 'индекс вхождения 'Разбор строки с названием книги и фамилией автора str1=SelectBook.Value Ind1= InStr(str1,":")-1 str2= Left(str1,Ind1) str3 = Mid(str1,Ind1+2) 'Запись данных в поля формы frmMain.Author.value = str2 frmMain.Title.value = str3 'MsgBox(frmMain.Author.value & frmMain.Title.value) End Sub 'FromSelectToHiddenFields
Sub CallNextPage(NameOfButton) 'NameOfButton - Имя нажатой кнопки 'Вызов страницы qomFromBase с передачей параметров: 'имени нажатой кнопки, названия и автора книги Dim str1, str2 'строковые пременные str1=frmMain.Author.value str2=frmMain.Title.value Window.navigate "qomFromBase.asp" & _ "?btnName=" & NameOfButton & "&Author=" & str1 & "&Title=" & str2 End Sub 'CallNextPage --> </SCRIPT>
Как можно видеть, данный код содержит 5 процедур, обрабатывающих различные события, и две процедуры, вызываемые в обработчиках событий.
Когда на клиентской стороне открывается страница, возникает событие Load объекта Window и вызывается соответствующая процедура. Действие ее достаточно просто - она обращается к процедуре FromSelectToHiddenFields, которая значение элемента SelectBook преобразует в название книги и фамилию автора и заполняет этими данными скрытые поля формы.
Эта же процедура вызывается и в обработчике события Change объекта SelectBook. Само же событие возникает тогда, когда пользователь выберет новую книгу из списка. Понятно, что в этот момент следует и обновить значения полей формы.
Комментировать работу процедуры FromSelectToHiddenFields вряд ли имеет смысл - обычная работа с объектами и строковыми переменными.
Следующие три процедуры в этом тексте - это обработчики события Click для трех объектов, задающих разные командные кнопки. Напомню, что ожидаемое действие при нажатии этих кнопок аналогично действию при нажатии кнопки Submit - должна быть вызвана некоторая страница узла и этой странице должны быть переданы параметры, необходимые для выполнения на сервере запрашиваемого действия. Все три обработчика устроены одинаково - вызывают процедуру CallNextPage, передавая ей в качестве параметра имя нажатой кнопки.
Процедура CallNextPage обеспечивает связь с сервером. Вызов метода Navigate объекта Window позволяет достаточно просто решить требуемую задачу. Метод позволяет задать вызываемую страницу сервера и передать ей параметры. Заметьте, вызывается страница qomFromBase, которая предназначена для того, чтобы по запросу пользователя читать данные из базы данных и отображать их в нужном виде на клиентском компьютере. Три параметра передаются этой странице - имя нажатой командной кнопки, фамилия автора и название книги. Первый параметр определяет, по сути, запрос пользователя - хочет ли он просмотреть по выбранной им книге ответы на его вопросы, все вопросы и ответы или все отзывы и оценки.
Вот и весь программный код, обеспечивающий интерактивность нашей странице. Я рассмотрел создание интерфейса страницы и создание кода, поддерживающего этот интерфейс. На этом я заканчиваю рассмотрение вопросов создания основной страницы нашего узла. Думаю, что она является достаточно типичным примером интерактивной Web-страницы - примером страницы, в которой пользователь запрашивает необходимую ему информацию. Чтобы пример был законченным, нужно еще рассмотреть, как устроены страницы qomToBase и qomFromBase, организующие реакцию на запросы пользователя. Но прежде, чем перейти к их рассмотрению, хочу сказать несколько слов о том, как я придаю структуру программному коду.
Принципиальные особенности Web-программирования
Назову три момента, характерные для Web-программирования:
"Многомерный" код.Слабая структурированность.Специфика отладки.
Что я имею в виду, когда говорю о "многомерности" кода? Дело в том, что код, создаваемый программистом, всегда представляет собой линейный текст. Однако исполнение написанного кода в Web-документе, чаще всего, будет разнесено в пространстве и времени. Как правило, у кода будет два исполнителя - Web-сервер на серверной стороне и Обозреватель на клиентской. Прежде чем страница документа будет послана обозревателю, ее текст читается Web-сервером, который находит относящиеся к нему части кода и выполняет его. Страница с результатами этой работы передается по сети клиенту, и там уже обозреватель, установленный на клиентском компьютере, выполняет свою часть работы, исполняя предназначенный ему код и отображая страницу на экране. После этого за дело берется пользователь, работающий за клиентским компьютером, - он выбирает нужные ему данные из списков, заносит требуемую информацию в поля ввода, нажимает командные кнопки и выполняет доступные команды меню. В ходе обработки возникающих событий обозреватель, как правило, обращается к серверу за получением очередной страницы документа, передавая ему одновременно информацию о том, какой должна быть эта страница в соответствии с запросами пользователя. Сервер находит нужную страницу, выполняет относящийся к нему код с учетом полученной им информации. Далее процесс повторяется. Напомню, что в роли Web-сервера я рассматриваю IIS, в роли обозревателя - Internet Explorer, а страницы, выполняемые на серверной стороне, являются ASP-страницами.
Сделаю еще несколько замечаний по поводу деталей этого процесса:
В ходе работы, выполняемой на серверной стороне, зачастую создается код, который должен выполнять обозреватель по получению страницы. Так что программирование на стороне сервера часто напоминает "программирование на лету". Хотя IIS и IE оба понимают VBScript, но объекты, доступные им, разные. У сервера свой набор объектов, обозревателю же доступна объектная модель отображаемой страницы с центральными объектами Window и Document.Важным моментом во всем этом процессе является организация передачи нужной информации от обозревателя к серверу и корректный анализ полученной информации на серверной стороне."Многомерность" кода связана не только с тем, что его текст выполняется двумя различными исполнителями. Когда код выполняется на клиентской стороне, то и здесь не все так просто. Дело в том, что код, исполняемый обозревателем, представляет собой смесь HTML-кода и VBScript-кода. Если быть более точным, то родным языком для обозревателя является HTML-код. Возможность выполнять сценарии, то есть исполнять VBScript-код, появилась позже. Для Internet Explorer такой код формально является HTML-кодом, представленным тегом <script> специального вида, который Internet Explorer умеет распознать и выполнить. В этой модели исполняемый код может быть разбросан по странице вперемешку с другими тегами. К этому следует добавить, что процедуры и функции, которые встречаются в таких тегах, рассматриваются обозревателем, естественно, как описания и не выполняются при просмотре. Они выполняются только при их вызове из выполняемых участков программного кода. Особое место в VBScript-коде занимают процедуры - обработчики событий, связанные с элементами управления и другими объектами, размещенными на странице. Такие процедуры вызываются в момент возникновения события. Они то и обеспечивают интерактивность Web-документов, делая VBScript языком, управляемым событиями.Наряду с HTML все большее распространение получает язык XML, позволяющий определять новые теги, задавая с их помощью определение объектов, используемых в документе. Фактически XML становится основой описания страниц Web-документов. Я, однако, в своих примерах не буду приводить тегов на XML, ограничившись лишь стандартными тегами HTML.
"Многомерный" характер программного кода, конечно, усложняет написание и понимание соответствующего текста. Другим фактором, усложняющим жизнь Web-программистов, является слабая структурированность программного кода. В языке VBScript нет модульности, присущей развитым языкам программирования. Программный код сервера не отделен от программного кода клиентской части. Теги "script" идут вперемешку с другими тегами. Объявления не отделены от исполняемого кода. Ну, уж конечно, нет, как в проектах на VBA, стандартных модулей, модулей - обработчиков событий. Поэтому ответственность за придание нужной структуры в полной мере ложится на программиста, который сам должен придать структуру своему проекту, чтобы ему самому легче было в нем ориентироваться и вносить необходимые изменения, которые не заставляют себя долго ждать. Чуть позже я приведу некоторый набор правил, которыми я пользуюсь в своей работе для придания коду определенной структуры.
Следствием первых двух факторов является и сложность отладки. ASP-страницы, содержащие серверный код отлаживать сложнее, чем HTML-страницы, содержащие только клиентский код. Скажу несколько слов о тех средствах отладки, которые предоставляет редактор VBScript Editor.
"Многомерный" характер программного кода, конечно, усложняет написание и понимание соответствующего текста. Другим фактором, усложняющим жизнь Web-программистов, является слабая структурированность программного кода. В языке VBScript нет модульности, присущей развитым языкам программирования. Программный код сервера не отделен от программного кода клиентской части. Теги "script" идут вперемешку с другими тегами. Объявления не отделены от исполняемого кода. Ну, уж конечно, нет, как в проектах на VBA, стандартных модулей, модулей - обработчиков событий. Поэтому ответственность за придание нужной структуры в полной мере ложится на программиста, который сам должен придать структуру своему проекту, чтобы ему самому легче было в нем ориентироваться и вносить необходимые изменения, которые не заставляют себя долго ждать. Чуть позже я приведу некоторый набор правил, которыми я пользуюсь в своей работе для придания коду определенной структуры.
Следствием первых двух факторов является и сложность отладки. ASP-страницы, содержащие серверный код отлаживать сложнее, чем HTML-страницы, содержащие только клиентский код. Скажу несколько слов о тех средствах отладки, которые предоставляет редактор VBScript Editor.
Проект узла "Читатели"
Давайте займемся проектированием нашего решения, обеспечивающего обратную связь с читателями. Я создам небольшой Web-узел, который буду называть узлом "Читатели". Он и будет представлять наш интерактивный Web-документ, а его страницы и будут являться страницами нашего документа. Основная интерактивность будет связана со страницей, озаглавленной "Страница читателей". Взгляните, как она выглядит, и тогда, надеюсь, станет понятно, что могут делать пользователи, работающие с этим документом:
Рис. 11.2. "Страница читателей" интерактивного Web-документа
Попадая на эту страницу, пользователь может:
Выбрать из списка книгу, которая его интересует в текущий момент, и по которой он хочет вести дискуссию, - задать вопрос, прочесть отзыв и прочее. Заметьте, для того чтобы обеспечить пользователю такую возможность, список книг должен быть автоматически сформирован на основе информации, хранящейся в базе данных офиса РР. Формирование списка будет обеспечено программным путем в момент открытия страницы. В двух окнах ввода, расположенных на странице, пользователь может задать вопрос и/или написать отзыв по выбранной им книге. В раскрывающемся списке он может задать численное значение оценки книги. Значение 0, установленное по умолчанию, означает, что пользователь не собирается оценивать книгу. По нажатию командной кнопки с надписью "Послать вопрос и/или отзыв и/или оценку" вопрос, отзыв и оценка, если они заданы, передаются в базу данных. Одновременно в базу данных передается информация о книге, с которой связаны эти характеристики, и о пользователе, задавшем эти характеристики. В разделе просмотра информации, расположенном во второй части страницы, пользователю доступны три командные кнопки, нажав каждую из которых он может: Посмотреть по выбранной книге ответы, на заданные им ранее вопросы.Посмотреть все вопросы и ответы, которые задавались всеми пользователями по данной книге.Посмотреть все отзывы и весь спектр поставленных оценок.
Теперь, когда цели ясны, давайте посмотрим, как спроектировать подобный узел с такой страницей, и как заставить, чтобы все это работало.
Приложение FrontPage 2000 позволяет создавать
Приложение FrontPage 2000 позволяет создавать как отдельные Web-страницы, так и Web-узлы. Как и во всех приложениях Office 2000, создание нового документа FrontPage - в данном случае Web-узла - начинается с выбора пункта меню File|New|Web, где в открывающемся окне можно выбрать шаблон создаваемого узла, а также задать папку, в которой будет храниться узел. По умолчанию создается узел с единственной страницей, которому затем можно придать нужную структуру.
Целесообразно начинать проектирование узла с задания его структуры в окне Navigation. Это окно открывается по нажатию одноименной кнопки, расположенной на левой панели приложения FrontPage. На деталях этого процесса останавливаться не буду, тем более что интерфейс FrontPage 2000 типичен для всех приложений Office 2000. Взгляните, как выглядит спроектированная мной структура узла:
Рис. 11.3. Структура узла "Читатели"
Наряду с начальной индексной страницей, на узле расположены три страницы. Одна из них, которая названа Qpage, представляет ту самую страницу читателей, создание которой является основной нашей целью. Две другие страницы - qomToBase, qomFromBase - это страницы с результатами выполненных действий, вызываемые в ответ на нажатие соответствующих командных кнопок основной страницы Qpage. Каждая из них после просмотра результатов позволяет вернуться на основную страницу читателей.
На следующем шаге я принял важное решение, определив тип создаваемых страниц. Все страницы, кроме индексной, я решил сделать ASP-страницами. Решение это определено тем, что все страницы будут получать или посылать данные в базу данных, расположенную на сервере, а потому, естественно, чтобы эта часть работы выполнялась на серверной стороне, а, следовательно, страница, выполняющая эту работу, была ASP-страницей.
Заметьте, когда я в окне навигации задал структуру узла, автоматически были созданы не только страницы, но и соответствующие папки, хранящие эти страницы. Страницы по умолчанию получили тип "html". Мне достаточно было перейти в окно Folders, отображающее все папки и все файлы, связанные с узлом, и там произвести изменение типа страниц, заменив его типом "asp".
Редактор VBScript и отладка
Инструментальная среда редактора, во многом, напоминает среду редактора VBA - похожие окна, похожая идеология. Взгляните, как выглядит эта среда в один из моментов работы:
увеличить изображение
Рис. 11.1. Инструментальная среда редактора VBScript в процессе работы
Как видите, практически тот же вид, что при работе с редактором VBA . Те же многочисленные панели с командами меню, многие кнопки на которых совпадают по виду и по функциям. Знакомое окно Project Explorer, в котором теперь показаны файлы, содержащие страницы, открытые в редакторе. Заметьте, эти страницы могут принадлежать разным документам - разным Web-узлам.
В основном окне среды отображена сама страница, выбранная в окне Project Explorer. Заметьте, у окна имеется три вкладки - Design, Source, QuickView, позволяющие отобразить различные облики страницы. Открытая на рисунке вкладка Source отображает HTML-код страницы. Страница показана в тот момент, когда процесс отладки находится в режиме прерывания. Процесс вычислений при отладке был прерван, поскольку была достигнута точка прерывания. Замечу, что аппарат точек прерывания здесь даже более мощный, чем в редакторе VBA. В частности для показанной точки прерывания задано условие прерывания, позволяющее прервать вычисление в точке только в том случае, когда выполняется определенное условие.
Обратите внимание и на знакомое окно отладки - окно Immediate. В момент прерывания в этом окне я проводил некоторые вычисления - посмотрел, а затем изменил значение свойства одного из объектов, доступных в контексте прерывания. Мои действия отображены в этом окне.
Еще одна знакомая деталь среды - панель инструментов Toolbox, позволяющая в режиме проектирования добавлять элементы управления на страницу.
Но можно видеть и одно новое окно - HTML Outline, в котором отображаются элементы управления, имеющиеся на странице.
Этот беглый взгляд, надеюсь, дает первое представление о среде редактора VBScript. Если попытаться вкратце охарактеризовать возможности этого редактора, то они следующие:
При написании кода - Подсказка при работе с объектами - раскрывающиеся списки свойств и методов объекта, появляющиеся в тот момент, когда поставлена точка.Выделение цветом различных участков кода.Вставка заготовок для тега script с учетом различий серверной и клиентской части кода.Управление процессом вычислений по ходу отладки - Пошаговое выполнение операторов с заходом в тело процедуры или без оного по нажатию клавиш F11 и F10.Прерывание вычислений по достижении оператора, отмеченного курсором.Прерывание вычислений по достижении очередной точки прерывания. Я уже говорил, что точки прерывания можно не только вставлять и удалять, но и связывать с ними условие прерывания.Изменение порядка выполнения операторов.Наблюдение за переменными и объектами по ходу отладки - Об окне отладки Immediate я уже сказал. Кроме того, имеется возможность включить показ таких знакомых по VBA окон отладки - Locals, Watch, Call Stack.Другие возможности инструментальной среды - Окно Object Browser, вызываемое также как и VBA по нажатию клавиши F2 позволяет изучать устройство классов объектов, используемых в документе.Появился ряд новых отладочных окон - Autos, Running Documents, Threads. Появился и ряд других окон, отражающих специфику документа, как, например, показанное на рисунке окно HTML Outline.
Как видите, среда Редактора VBScript по многим своим потенциальным возможностям не уступает среде редактора VBA. К сожалению, при отладке серверного кода большая часть из перечисленных средств являются недоступными. Для выполнения серверного кода, то есть при отладке ASP-страниц необходим Web-сервер. Поэтому отладка интерактивных Web-документов, в которых без ASP-страниц не обойтись требует установки IIS или Personal WEB Server. Для узла на сервере должен быть создан виртуальный каталог и страницы этого узла должны вызываться в Internet Explorer с указанием адреса этого каталога.
В примере, о котором я собираюсь рассказать, речь пойдет главным образом об ASP-страницах. Когда возникала ошибка в серверном коде, а без ошибок, естественно, не обошлось, то сообщение об ошибке выдается в Internet Explorer всегда с указанием номера строки, в которой произошла ошибка, а иногда и с указанием причины ошибки. В этом случае я, как правило, возвращался в среду редактора VBScript и использовал такое "мощное" средство отладки как команду "Go To", вызываемую из меню Edit. Открывающееся окно позволяет указать номер строки и перейти на строку, являющуюся причиной ошибки. Поскольку других подсказок в этой ситуации нет, то исправление ошибок в гораздо большей степени зависит от искусства и опыта программиста.
Вот то минимально необходимое, что я считаю должным сказать, перед тем как перейти к рассмотрению примера.
Создание интерфейса
Вопрос о том, каким должен быть интерфейс этой страницы, решался еще на этапе проектирования. На рис. 11.2 показано, как выглядит эта страница, отображаемая в Internet Explorer. Поговорим сейчас о реализации этого интерфейса.
Но вначале пару слов о том, какие возможности предоставляет среда FrontPage 2000 для работы с документом. Основное окно среды, в котором отображается документ, имеет три вкладки - Normal, HTML, Preview - каждая из которых показывает тот или иной облик документа. Создавать документ, в особенности его интерфейс, удобнее всего, пожалуй, на вкладке Normal, где все вставляемые объекты отображаются в обычном виде. Вкладка HTML показывает другой облик этого же документа - его HTML-код. Наконец, вкладка Preview позволяет показать документ в том виде, каким он будет отображаться в Internet Explorer, следовательно, с исполнением программного кода. По этой причине попытка просмотреть ASP-страницы на этой вкладке приводит к возникновению ошибки, поскольку код, предназначенный для сервера, выполниться во FrontPage не может.
В среде FrontPage 2000 есть три варианта, где можно выполнить работу по созданию интерфейса документа:
На вкладке Normal. Работая с этим видом документа, для вставки интерфейсного объекта достаточно выбрать пункт меню Insert|Form, а затем выбрать из списка нужный интерфейсный объект. Заметьте, при таком способе все интерфейсные элементы автоматически связываются с формой. При желании их можно из формы вынести, а саму форму удалить. После вставки элемента щелчок правой кнопки позволяет открыть окно свойств данного элемента и установить нужные значения свойств. На вкладке HTML. Для тех, кто хорошо знает HTML, чаще проще вставить тег, задающий тот или иной объект, и тут же определить его свойства.В редакторе VBScript. Программисты чаще всего предпочитают этот вариант. Здесь, конечно же, удобнее работать с программным кодом, но и удобно заниматься проектированием интерфейса, поскольку вставка интерфейсных объектов ведется более привычным способом, - набор этих элементов вынесен, как обычно на инструментальную панель.
Вернемся к нашему документу. На рис. 11.2 можно видеть, что проектируемая страница имеет 9 интерфейсных объектов - 5 командных кнопок, список с названиями книг, список с оценками и два текстовых окна для ввода отзывов и вопросов. Однако это не все интерфейсные объекты, которые в действительности расположены на странице, поскольку некоторые из реально существующих объектов невидимы в Internet Explorer. С другой стороны, список с названиями книг создается сервером программно и потому он виден в Internet Explorer, но не создается и не отображается при работе с документом в среде FrontPage.
Какие же еще интерфейсные объекты следует разместить на нашей странице, помимо 9 вышеупомянутых? Прежде всего, это объект, задающий форму, в которую и вставляются все остальные интерфейсные элементы. Кроме того, мне понадобилось, чтобы у формы были еще два текстовых поля, хранящих название выбранной пользователем книги и фамилию автора. Почему эти поля необходимы, скажу чуть позже, сейчас же отмечу, что, поскольку значения этих полей заполняются не пользователем, работающим с документом, а программным путем, то поля имеют статус "hidden", что делает их невидимыми.
Несколько общих слов о формах. Каждая форма является модулем, в который могут быть встроены различные интерфейсные элементы. Вся информация, введенная пользователем внутри формы, воспринимается, как один блок, и может быть передана получателю при отсылке форме. Тем самым, формы облегчают передачу информации от обозревателя к получателю, в роли которого, чаще всего, выступает сервер. Заметьте, при вставке формы в документ вместе с ней автоматически вставляются и две командные кнопки, имеющие особый статус и предопределенные функции. Кнопка со статусом Reset позволяет производить очистку содержания полей формы - текстовых окон, в которые пользователь мог ввести ту или иную информацию. Кнопка со статусом Submit позволяет отправить информацию, заданную пользователем, получателю.
При задании свойств моей формы я дал ей имя "frmMain", указал, что получателем данных будет ASP-страница узла "qomToBase.asp", задал метод "Post", как метод пересылки данных. Вот как выглядит тег, задающий форму:
<form method="POST" action="qomToBase.asp" name="frmMain" id="frmMain">
Затем я вставил в форму 10 уже упоминавшихся интерфейсных объектов - 2 скрытых и 8 видимых. На деталях процесса останавливаться не буду, а описание соответствующих тегов все-таки приведу:
<input type="hidden" name="Author" id="Author" size="20" > <input type="hidden" name="Title" size="20" id="Title"> <TEXTAREA rows=4 cols=30 id=Question name=Question> </TEXTAREA> <TEXTAREA rows=4 cols=30 id=Opinion name=Opinion> </TEXTAREA> <select size="1" name="Mark" id="Mark"> <option selected value="0">0</option> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> </select> <input type="submit" value="Послать вопрос и/или отзыв и/или оценку" name="btnSendQOM" id="btnSendQOM"> <input type="reset" value="Очистить поле вопроса, отзыва" name="B2"> <INPUT type="button" value="Посмотреть мои вопросы и ответы на них" id=btnLookmyQ name=btnLookmyQ> <INPUT type="button" value="Посмотреть все вопросы и ответы на них" id=btnLookAllQ name=btnLookAllQ> <INPUT type="button" value="Посмотреть отзывы и оценки" id=btnLookOM name=btnLookOM>
Надеюсь, Вы понимаете, какой из упоминавшихся интерфейсных объектов описывает каждый из этих тегов. Взгляните, как выглядит работа с документом на вкладке Normal в процессе создания интерфейса. На рисунке 11.4 показан процесс установки свойств формы.
увеличить изображение
Рис. 11.4. Вкладка Normal. Установка свойств объекта frmMain
На рис. 11.5 показана работа с тем же документом, но в среде редактора VBScript. Обратите внимание на панель инструментов - Toolbox, содержащую интерфейсные элементы. В окне Source показан HTML-код данной страницы.
увеличить изображение
Рис. 11.5. Создание интерфейса в среде редактора VBScript
Приведу теперь несколько важных комментариев, поясняющих некоторые особенности создаваемого мной интерфейса:
Прежде всего, поясню, почему список с названиями книг не строится на этапе проектирования интерфейса. Дело в том, что я хочу, чтобы при каждом открытии страницы этот список отражал текущее состояние базы данных, которое по понятным причинам динамически изменяется. Поэтому на этапе проектирования задать элементы этого списка, как это сделано, например, со списком оценок, невозможно. Список строится программно, по данным запроса к базе данных, и соответствующий код работает на стороне сервера. При создании этого интерфейсного элемента ему дается имя "SelectBook". В связи с тем, что список SelectBook строится программно, возникает некоторая проблема. Программно построенный сервером интерфейсный элемент не будет принадлежать форме frmMain, а, следовательно, информация, передаваемая при отсылке формы, не будет содержать необходимых сведений о выборе пользователем фиксированной книги, если только не принять специальных мер. Для решения проблемы и вводятся скрытые поля формы с именами Title и Author, содержащие название книги и фамилию автора. Хотя эти поля создаются на этапе проектирования документа, их значения заполняются программно. Соответствующий код работает уже на клиентской стороне и встроен в обработчик события Change элемента SelectBook. Как только пользователь сделает новый выбор в списке, соответствующая информация будет передана в поля Title и Author. Это позволяет при отсылке формы серверу, наряду с другой информацией передать данные о той книге, которую выбрал пользователь.Давайте теперь поговорим о командных кнопках, почему их пять, а не две - Reset и Submit. Прежде всего, заметьте, что эти две кнопки в форме присутствуют. Статус Reset имеет кнопка с заголовком "Очистить поле вопроса, отзыва", статус Submit - кнопка "Послать вопрос и/или отзыв и/или оценку". Можно ли было ограничиться только этими двумя кнопками? В принципе, да. Когда у пользователя, как в нашем случае, несколько вариантов возможных действий, - посмотреть отзывы, задать вопрос и так далее, то достаточно ввести в форму, например, флажки или переключатели, с помощью которых пользователь фиксировал бы желаемое действие. Анализ состояния флажков на странице, которой передается информация из формы, позволил бы выполнить нужное действие. Я решил, однако, иметь две разные страницы, которым передаются данные из формы. ASP-страница qomToBase будет вызываться тогда, когда данные из формы - вопрос, отзыв, оценка - записываются в базу данных. Эта страница будет вызываться при нажатии
кнопки со статусом Submit. Другая ASP-страница - qomFromBase - будет вызываться тогда, когда требуется вывести на экран пользователя информацию, хранящуюся в базе, - вопросы и ответы, имеющиеся отзывы и оценки. В отличие от предопределенных действий, связанных с кнопкой Submit, в этом случае вызов ASP-страницы и передачу ей информации придется организовать программно в обработчиках события Click соответствующих командных кнопок.
Создание программного кода
С нетерпением ждал, когда же, наконец, можно перейти к описанию программного кода, связанного с этой страницей. Этот момент наступил. Что должен делать это код, уже говорилось. На серверной стороне он должен сформировать интерфейсный элемент - список, содержащий названия всех имеющихся книг. На клиентской стороне необходимо в скрытые поля формы передать информацию о выборе пользователем из списка той или иной книги. Ну и, конечно, нужно обеспечить передачу информации серверу и переход на другую страницу при нажатии командных кнопок.
Страница qomFromBase - страница, где информация, необходимая пользователю, читается из базы данных
Напомню, эта страница вызывается после нажатия любой из трех командных кнопок с заголовками: "Посмотреть мои вопросы и ответы на них", "Посмотреть все вопросы и ответы на них", "Посмотреть отзывы и оценки". При вызове странице ей передаются три параметра - имя нажатой кнопки, название книги и фамилия автора.
Теперь нам предстоит рассмотреть серверный код, позволяющий принять параметры, проанализировать и выполнить запрос пользователя на получение нужной ему информации. Ну и, конечно же, потребуется сформировать динамически HTML-код, представляющий результаты запроса на странице в приемлемой форме.
На этапе проектирования эта страница, также как и предыдущая, пуста. Все, что на ней появится, когда она откроется на клиентском компьютере, будет определяться результатами выполнения серверного кода.
Действия, а, следовательно, и программный код этой страницы во многом аналогичен коду предыдущей страницы. По этой причине я не буду приводить весь серверный код, а ограничусь некоторыми фрагментами. В этот раз начну с исполняемой части кода:
<SCRIPT LANGUAGE=vbscript RUNAT=Server> '********************************************************** '** Исполняемый код на серверной стороне ** '************ на серверной стороне ************************ 'Инициализация переменных InitVars 'Анализ запроса и выполнение действия, заданного пользователем SelectAndExec </SCRIPT>
Как можно видеть, этот фрагмент практически совпадает с исполняемым кодом предыдущей страницы. Хотя вызываемые процедуры сохранили свои имена и назначение, но имеют, конечно, другое содержание. Поскольку информация на страницу передавалась не так как в предыдущем случае, то и процедура InitVars использует другие методы для извлечения этой информации. Вот код этой процедуры:
Sub InitVars() Set Con1=Server.CreateObject("ADODB.Connection")
pUser=Session("UserId") pUser =CLng(pUser) pAction = Request.QueryString("btnName") pAuthor =Request.QueryString("Author") pTitle =Request.QueryString("Title") End Sub 'InitVars
Основное отличие этой процедуры от своего аналога состоит в том, что используется метод QueryString объекта Request, а не метод Form, как ранее.
Одноименная процедура SelectAndExec, сохраняя свое назначение, также в ряде деталей отличается от своего аналога:
Sub SelectAndExec() Select Case LCase(pAction) Case "lookallq" LookQA_All Case "lookmyq" LookQA_my(pUser) Case "lookom" LookOM 'Case Else Response.Write "Else" End Select Response.Write "<p><A href=""qpage.asp"">" & _ "<font size=7 color = ""#FF6666""> Вернуться </font> </a>" Response.Write "на страницу Читатели" End Sub 'SelectAndExec
Здесь в операторе Select анализируется, какие данные о книге желает получить пользователь и в зависимости от этого вызывается одна из трех процедур - LookQA_All, LookQA_my, LookOM, которые соответственно позволяют просмотреть ответы на все вопросы, вопросы конкретного пользователя, отзывы и оценки по книге. Поскольку все эти процедуры принципиально устроены одинаково, то я ограничусь рассмотрением первой из них.
Процедура LookQA_All выполняет хранимый запрос на получение всех вопросов и ответов к заданной книге и формирует HTML-код, отображающий результаты запроса на экране. Вот как она выглядит:
Sub LookQA_All() 'Показ всех вопросов 'Получение данных из базы данных CreateConnection 'Получение набора записей CreateRSAllQ 'Перепись данных в таблицу на html-странице strTitle = "Вопросы и ответы к книге '" & pAuthor & _ ":" & pTitle & "'" CreateTHead(strTitle) 'Заголовок таблицы CreateTBody 'Тело таблицы End Sub
Как видите, она состоит из вызовов четырех процедур, решающих частные задачи. О процедуре CreateConnection говорить не буду, а другие давайте рассмотрим. Процедура CreateRSAllQ создает требуемый набор записей. Задача эта уже неоднократно встречалась, но все-таки посмотрим, как она решается в данной ситуации:
Sub CreateRSAllQ() 'Создание команды и набора записей с вопросами и ответами Dim par 'Параметр запроса Set Cmd1=Server.CreateObject("ADODB.Command") Set Rst1=Server.CreateObject("ADODB.Recordset") With Cmd1 'Конфигурирование объекта Command .ActiveConnection = Con1 .CommandText ="QAAll" .CommandType = 4 'adCmdStoredProc Set par= .CreateParameter("repAuthor",202,1,255) par.value=pAuthor .Parameters.Append par Set par= .CreateParameter("repTitle",202,1,255) par.value=pTitle .Parameters.Append par 'Формирование набора записей Rst1.Open Cmd1 End With End Sub 'CreateRSAllQ
И здесь используется хранимый запрос с параметрами, с помощью которого и создается искомый набор записей. Приведу сам запрос:
PARAMETERS repAuthor Text ( 255 ), repTitle Text ( 255 ); SELECT QA.Question, QA.QDate, QA.Answer, QA.ADate FROM QA WHERE (((QA.Author)=[repAuthor]) AND ((QA.Title)=[repTitle]));
Замечу, что таблица QA базы данных хранит вопросы пользователей и ответы на эти вопросы, сделанные авторами, редакторами, переводчиками. Я оставляю вне рассмотрения то, как даются ответы, для этого нужно было бы рассмотреть не только страницу читателей, но и страницу писателей.
Рассмотрим теперь, как результаты запроса, представленные полученным в процедуре CreateRSAllQ набором записей Rst1, преобразуются в таблицу, отображаемую на экране пользователя. Конечно, для "красивого" отображения необходим хороший дизайн, а, следовательно, и более глубокое знание языка HTML. Я приведу тексты процедур, решающих эту задачу, но особых комментариев давать не стану, хотя знание HTML и XML - это неотъемлемая часть знаний, необходимых Web-программисту.
Процедура CreateTHead строит заголовочную часть таблицы, в которой будут отображаться вопросы и ответы. Она используется в нескольких вызовах, ее параметр передает текст появляющегося заголовка. Вот ее код:
Sub CreateTHead(Tit) 'Организация заголовка таблицы вопросов и ответов Response.Write "<table border =1 id=tblQA width=""90%"">" Response.Write "<caption align = ""center"">" & _ "<font size = 5 color=""gray"">" & _ Tit & "</font></caption>" 'Организация заголовков столбцов таблицы Response.Write "<COLS=4>" Response.Write "<Col width=""40%"">" Response.Write "<Col width=""10%"">" Response.Write "<Col width=""40%"">" Response.Write "<Col width=""10%"">" Response.Write "<THead>" Response.Write "<tr bgcolor= ""lightblue"">" Response.Write "<td>Вопрос" Response.Write "<td>Дата вопроса" Response.Write "<td>Ответ" Response.Write "<td>Дата ответа" Response.Write "</THead>" End Sub 'CreateTHead
Текст ее достаточно понятен - строится таблица из четырех столбцов с заголовками, указанными для каждого поля. Таблица по ширине занимает 90% отводимого ей пространства экрана, задаются также пропорциональные размеры столбцов.
В следующей процедуре создаются записи этой таблицы. Число строк таблицы, естественно определяется числом записей в наборе, полученном по результатам запроса. Приведу код процедуры CreateTBody:
Sub CreateTBody() 'Организация тела таблицы вопросов и ответов While Not Rst1.EOF Response.Write "<tr>" Response.Write "<td>" & _ Rst1.Fields("Question").Value & "</td>" Response.Write "<td>" & _ Rst1.Fields("QDate").Value & "</td>" If Rst1.Fields("Answer").ActualSize=0 Then Response.Write "<td>" & _ " Пока ответа нет!</td>" Else Response.Write "<td>" & _ Rst1.Fields("Answer").Value & "</td>" End If If Rst1.Fields("ADate").ActualSize=0 Then Response.Write "<td>" & _ Date & "</td>" Else Response.Write "<td>" & _ Rst1.Fields("ADate").Value & "</td>" End If Response.Write "</tr>" Rst1.MoveNext Wend Response.Write "</table>" End Sub 'CreateTBody
Это более программистский текст - здесь реализован обычный проход по набору записей, и для каждой записи формируется соответствующая строка таблицы. Заметьте, поскольку ответа на некоторый вопрос может и не быть, а значит и дата ответа не будет указана, то эта ситуация анализируется и в случае отсутствия ответа выдается соответствующее сообщение. В заключение давайте взглянем, как выглядит таблица, формируемая в ответ на запрос по просмотру отзывов и оценок по одной из книг:
увеличить изображение
Рис. 11.7. Страница с вопросами и ответами, выводимая по запросу пользователя
На этом я заканчиваю описание примера, главы, книги и всей серии "Офисное программирование".
Страница qomToBase - страница, где информация пользователя записывается в базу данных
Напомню, эта страница вызывается после нажатия кнопки с заголовком "Послать вопрос и/или отзыв и/или оценку". Кнопка имеет статус Submit. Одновременно странице передаются данные о заданном пользователем вопросе, сделанном отзыве, проставленной оценке, также как и данные о книге, с которой связаны эти характеристики. Серверный код на этой странице должен обеспечить прием параметров и запись полученной информации в таблицы базы данных. Должен быть сформирован HTML-код, уведомляющий пользователя о результатах выполнения его запроса.
Я решил на этапе проектирования оставить эту страницу пустой - ни интерфейсных объектов, ни даже простого текста на этой странице нет. Все, что на ней появится, когда она откроется на клиентском компьютере, будет определяться результатами выполнения серверного кода. Поэтому, если открыть эту страницу во FrontPage на вкладке Normal, то все будет пусто. Но серверный код в ней, конечно же, присутствует.
Страница Qpage - страница читателей
Теперь, когда сами страницы созданы, можно приступать к их наполнению. Давайте начнем с основной страницы и, прежде всего, поговорим о том, как создавался ее интерфейс, и какие проблемы могут возникать на этом этапе.