В этой главе я остановлюсь на создании интерактивных Web-документов. Хотя сегодня подобные документы не столь широко распространены, их значение и роль в общем потоке офисных документов будет только возрастать. Причина этого в том, что, как я уже неоднократно говорил, интрасети все в большей мере становятся средой, в которой ведется совместная работа над документами. Большинство документов, циркулирующих в сети, по своей сути являются интерактивными. В главе 7, где рассматривались вопросы создания интерактивных документов в Excel, отмечалось, что для таких документов характерно:
Эти же свойства характерны и для интерактивных Web-документов.
Различные инструментальные средства используются для построения таких документов, - CGI, Perl, PHP и другие подобные средства. Но, продолжая тему программирования в среде Microsoft Office , я ограничусь рассмотрением инструментальных средств от Microsoft, - вот краткий перечень того, что так или иначе будет затронуто в этой главе: FrontPage 2000, IIS (Internet Information Server), Internet Explorer, ASP (Active Server Pages), VBScript, OWC (Office Web Components), объекты ADO. Из названного подробный разговор шел, пожалуй, только об объектах ADO и OWC, другие же темы затрагивались лишь вскользь. Понятно, что рассмотреть хоть с какой либо степенью подробности затрагиваемые темы мне не удастся, да я и не ставлю такой цели. Я ограничусь примером создания подобного документа. Этот пример и комментарии в процессе создания документа могут служить введением в WEB-программирование. Так что эта последняя глава могла бы служить первой главой новой книги с условным названием "Основы Web-программирования". Главная цель этой главы - показать, что для тех, кто умеет программировать на VBA и создавать интерактивные документы в среде Office 2000, создание интерактивных Web-документов не представляет особых трудностей. Переход от офисного программирования к Web-программированию вполне естественен.
Вместе с тем Web-программирование имеет ряд принципиальных особенностей, затрудняющих как написание, так и отладку соответствующего кода. Прежде чем обсудить эти трудности, сделаю еще одно замечание. Когда я говорил об офисных документах, то всегда отмечал, что документы в среде Microsoft Office могут создаваться без всякого программирования. Офисное программирование начинается тогда, когда документам необходимо придать интерактивность, специфичную для данного документа. Аналогичная ситуация имеет место и при создании Web-документов. Web-мастера и Web-дизайнеры вполне могут создавать Web-документы без всякого программирования. Web-программирование начинается тогда, когда в документ встраиваются различные элементы управления, которые должны реагировать на действия пользователя специфическим для этого документа образом. Если рассматривать Web-документ как книгу, которую читает пользователь, то нет нужды в программировании, даже если страницы просматриваются в произвольном порядке. Если же пользователь определяет не только, какой будет следующая страница, но и влияет на ее содержание, например, требуя, чтобы на странице были размещены нужные ему данные, то тут уже без программирования не обойтись.
Назову три момента, характерные для Web-программирования:
Что я имею в виду, когда говорю о "многомерности" кода? Дело в том, что код, создаваемый программистом, всегда представляет собой линейный текст. Однако исполнение написанного кода в Web-документе, чаще всего, будет разнесено в пространстве и времени. Как правило, у кода будет два исполнителя - Web-сервер на серверной стороне и Обозреватель на клиентской. Прежде чем страница документа будет послана обозревателю, ее текст читается Web-сервером, который находит относящиеся к нему части кода и выполняет его. Страница с результатами этой работы передается по сети клиенту, и там уже обозреватель, установленный на клиентском компьютере, выполняет свою часть работы, исполняя предназначенный ему код и отображая страницу на экране. После этого за дело берется пользователь, работающий за клиентским компьютером, - он выбирает нужные ему данные из списков, заносит требуемую информацию в поля ввода, нажимает командные кнопки и выполняет доступные команды меню. В ходе обработки возникающих событий обозреватель, как правило, обращается к серверу за получением очередной страницы документа, передавая ему одновременно информацию о том, какой должна быть эта страница в соответствии с запросами пользователя. Сервер находит нужную страницу, выполняет относящийся к нему код с учетом полученной им информации. Далее процесс повторяется. Напомню, что в роли Web-сервера я рассматриваю IIS, в роли обозревателя - Internet Explorer, а страницы, выполняемые на серверной стороне, являются ASP-страницами.
Сделаю еще несколько замечаний по поводу деталей этого процесса:
"Многомерный" характер программного кода, конечно, усложняет написание и понимание соответствующего текста. Другим фактором, усложняющим жизнь Web-программистов, является слабая структурированность программного кода. В языке VBScript нет модульности, присущей развитым языкам программирования. Программный код сервера не отделен от программного кода клиентской части. Теги "script" идут вперемешку с другими тегами. Объявления не отделены от исполняемого кода. Ну, уж конечно, нет, как в проектах на VBA, стандартных модулей, модулей - обработчиков событий. Поэтому ответственность за придание нужной структуры в полной мере ложится на программиста, который сам должен придать структуру своему проекту, чтобы ему самому легче было в нем ориентироваться и вносить необходимые изменения, которые не заставляют себя долго ждать. Чуть позже я приведу некоторый набор правил, которыми я пользуюсь в своей работе для придания коду определенной структуры.
Следствием первых двух факторов является и сложность отладки. ASP-страницы, содержащие серверный код отлаживать сложнее, чем HTML-страницы, содержащие только клиентский код. Скажу несколько слов о тех средствах отладки, которые предоставляет редактор VBScript Editor.
Инструментальная среда редактора, во многом, напоминает среду редактора VBA - похожие окна, похожая идеология. Взгляните, как выглядит эта среда в один из моментов работы:
(рис 11.1) Инструментальная среда редактора VBScript в процессе работыКак видите, практически тот же вид, что при работе с редактором VBA . Те же многочисленные панели с командами меню, многие кнопки на которых совпадают по виду и по функциям. Знакомое окно Project Explorer, в котором теперь показаны файлы, содержащие страницы, открытые в редакторе. Заметьте, эти страницы могут принадлежать разным документам - разным Web-узлам.
В основном окне среды отображена сама страница, выбранная в окне Project Explorer. Заметьте, у окна имеется три вкладки - Design, Source, QuickView, позволяющие отобразить различные облики страницы. Открытая на рисунке вкладка Source отображает HTML-код страницы. Страница показана в тот момент, когда процесс отладки находится в режиме прерывания. Процесс вычислений при отладке был прерван, поскольку была достигнута точка прерывания. Замечу, что аппарат точек прерывания здесь даже более мощный, чем в редакторе VBA. В частности для показанной точки прерывания задано условие прерывания, позволяющее прервать вычисление в точке только в том случае, когда выполняется определенное условие.
Обратите внимание и на знакомое окно отладки - окно Immediate. В момент прерывания в этом окне я проводил некоторые вычисления - посмотрел, а затем изменил значение свойства одного из объектов, доступных в контексте прерывания. Мои действия отображены в этом окне.
Еще одна знакомая деталь среды - панель инструментов Toolbox, позволяющая в режиме проектирования добавлять элементы управления на страницу.
Но можно видеть и одно новое окно - HTML Outline, в котором отображаются элементы управления, имеющиеся на странице.
Этот беглый взгляд, надеюсь, дает первое представление о среде редактора VBScript. Если попытаться вкратце охарактеризовать возможности этого редактора, то они следующие:
Как видите, среда Редактора VBScript по многим своим потенциальным возможностям не уступает среде редактора VBA. К сожалению, при отладке серверного кода большая часть из перечисленных средств являются недоступными. Для выполнения серверного кода, то есть при отладке ASP-страниц необходим Web-сервер. Поэтому отладка интерактивных Web-документов, в которых без ASP-страниц не обойтись требует установки IIS или Personal WEB Server. Для узла на сервере должен быть создан виртуальный каталог и страницы этого узла должны вызываться в Internet Explorer с указанием адреса этого каталога.
В примере, о котором я собираюсь рассказать, речь пойдет главным образом об ASP-страницах. Когда возникала ошибка в серверном коде, а без ошибок, естественно, не обошлось, то сообщение об ошибке выдается в Internet Explorer всегда с указанием номера строки, в которой произошла ошибка, а иногда и с указанием причины ошибки. В этом случае я, как правило, возвращался в среду редактора VBScript и использовал такое "мощное" средство отладки как команду "Go To", вызываемую из меню Edit. Открывающееся окно позволяет указать номер строки и перейти на строку, являющуюся причиной ошибки. Поскольку других подсказок в этой ситуации нет, то исправление ошибок в гораздо большей степени зависит от искусства и опыта программиста.
Вот то минимально необходимое, что я считаю должным сказать, перед тем как перейти к рассмотрению примера.
Вернемся снова к нашему Офису РР, который неоднократно встречался в наших примерах, и посмотрим, как он использует интернет и Web-программирование при разработке своих документов. Конечно же, Офис РР давно уже имеет интернет-магазин для продажи своих книг, более того, он дает возможность своим читателям присылать отзывы и выставлять оценки приобретенным книгам. Чтобы связь с читателями была двусторонней, на страницах узла нашего офиса, читатели могут задавать вопросы авторам, переводчикам и редакторам выпускаемых книг, получать ответы на свои вопросы. Читатели могут также ознакомиться с отзывами, присланными на книги и просмотреть весь спектр выставленных оценок.
Давайте рассмотрим, как можно организовать обратную связь с читателями с помощью упомянутых мной средств. При этом я основное внимание сосредоточу на вопросах программирования, оставляя в стороне вопросы дизайна и многие другие вопросы, которые возникают в процессе создания коммерческого решения.
Для тех, кто хочет ознакомиться с реально работающим подобным узлом, советую зайти на узел издательства "Русская Редакция", - http://www.rusedit.ru. Там, правда, нет возможности в настоящее время задавать вопросы и получать ответы, но возможность заказать нужную книгу и написать отзыв имеется.
Давайте займемся проектированием нашего решения, обеспечивающего обратную связь с читателями. Я создам небольшой Web-узел, который буду называть узлом "Читатели". Он и будет представлять наш интерактивный Web-документ, а его страницы и будут являться страницами нашего документа. Основная интерактивность будет связана со страницей, озаглавленной "Страница читателей". Взгляните, как она выглядит, и тогда, надеюсь, станет понятно, что могут делать пользователи, работающие с этим документом:
(рис 11.2) "Страница читателей" интерактивного Web-документаПопадая на эту страницу, пользователь может:
0, установленное по умолчанию, означает, что пользователь не собирается оценивать книгу. По нажатию командной кнопки с надписью "Послать вопрос и/или отзыв и/или оценку" вопрос, отзыв и оценка, если они заданы, передаются в базу данных. Одновременно в базу данных передается информация о книге, с которой связаны эти характеристики, и о пользователе, задавшем эти характеристики.Теперь, когда цели ясны, давайте посмотрим, как спроектировать подобный узел с такой страницей, и как заставить, чтобы все это работало.
Приложение 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".
Теперь, когда сами страницы созданы, можно приступать к их наполнению. Давайте начнем с основной страницы и, прежде всего, поговорим о том, как создавался ее интерфейс, и какие проблемы могут возникать на этом этапе.
Вопрос о том, каким должен быть интерфейс этой страницы, решался еще на этапе проектирования. На рис. 11.2 показано, как выглядит эта страница, отображаемая в Internet Explorer. Поговорим сейчас о реализации этого интерфейса.
Но вначале пару слов о том, какие возможности предоставляет среда FrontPage 2000 для работы с документом. Основное окно среды, в котором отображается документ, имеет три вкладки - Normal, HTML, Preview - каждая из которых показывает тот или иной облик документа. Создавать документ, в особенности его интерфейс, удобнее всего, пожалуй, на вкладке Normal, где все вставляемые объекты отображаются в обычном виде. Вкладка HTML показывает другой облик этого же документа - его HTML-код. Наконец, вкладка Preview позволяет показать документ в том виде, каким он будет отображаться в Internet Explorer, следовательно, с исполнением программного кода. По этой причине попытка просмотреть ASP-страницы на этой вкладке приводит к возникновению ошибки, поскольку код, предназначенный для сервера, выполниться во FrontPage не может.
В среде FrontPage 2000 есть три варианта, где можно выполнить работу по созданию интерфейса документа:
Вернемся к нашему документу. На рис. 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. Это позволяет при отсылке формы серверу, наряду с другой информацией передать данные о той книге,
которую выбрал пользователь.qomToBase будет вызываться тогда, когда данные из формы - вопрос, отзыв, оценка - записываются в базу данных. Эта страница будет вызываться при нажатии кнопки со статусом Submit. Другая ASP-страница - qomFromBase - будет вызываться тогда, когда требуется вывести на экран пользователя информацию, хранящуюся в базе, - вопросы и ответы, имеющиеся отзывы и оценки. В отличие от предопределенных действий, связанных с кнопкой Submit, в этом случае вызов ASP-страницы и передачу ей информации придется организовать программно в обработчиках события Click соответствующих командных кнопок.С нетерпением ждал, когда же, наконец, можно перейти к описанию программного кода, связанного с этой страницей. Этот момент наступил. Что должен делать это код, уже говорилось. На серверной стороне он должен сформировать интерфейсный элемент - список, содержащий названия всех имеющихся книг. На клиентской стороне необходимо в скрытые поля формы передать информацию о выборе пользователем из списка той или иной книги. Ну и, конечно, нужно обеспечить передачу информации серверу и переход на другую страницу при нажатии командных кнопок.
Давайте, вначале рассмотрим код, исполняемый на стороне сервера. Это логично, поскольку он выполняется первым и от его результатов, зачастую, зависит содержание страницы, поступающей клиенту. Приведу вначале декларативную часть кода, содержащую описание глобальных переменных, описание используемых процедур и функций:
<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-код страницы, передаваемой клиенту, но я ограничусь только программным кодом.
И на клиентской стороне можно выделить декларативную и исполняемую часть кода. Заметьте, я предпочитаю, чтобы на серверной стороне исполняемая часть кода присутствовала, в то же время на клиентской стороне ее может не быть. Более того, при хорошем программировании, полагаю, ее не должно быть, как не будет ее и в нашем примере. Причины этого, надеюсь, понятны. Дело в том, что обработка событий на стороне сервера хотя и возможна, но не представляется столь желательной. Поэтому исполняемый участок кода и предназначен для инициирования вычислений. Конечно, достаточно одного оператора, вызывающего первую процедуру, чтобы начать сложные вычисления с многочисленными вызовами процедур, представленными в декларативной части кода. При хорошем стиле программирования так, обычно, и бывает.
Программирование на клиентской стороне в большей степени является программированием, управляемым событиями. Это и понятно - здесь расположены элементы управления, с которыми работает пользователь, а именно он, его действия при работе с документом являются источником возникновения событий. Поэтому основу декларативной части клиентского кода составляют, как правило, процедуры-обработчики событий, возникающих с интерфейсными объектами и другими объектами, расположенными на странице. По этой причине нет необходимости в исполняемом фрагменте кода, хотя он и может присутствовать. Но более естественно для данного стиля программирования для инициирования вычислений иметь процедуру, обрабатывающую событие Load, которая автоматически будет вызываться при открытии страницы. Когда же пользователь начнет работать, то будут вызываться и другие обработчики событий. А теперь взгляните на декларативный код клиентской части нашей страницы. Конечно же, он включает процедуры-обработчики различных событий, возникающих с объектами страницы. Вот этот код:
<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-программирования в нынешних условиях, является слабая структурированность программного кода, отсутствие столь привычного модульного построения программных проектов. Чтобы как-то справиться с этой проблемой, я для себя создал определенные правила. Приведу их, может быть, они будут полезны и другим:
<head>. Декларативной части предшествует специальный комментарий. Вы могли видеть его в тексте приведенного кода.<body>.Эти нехитрые правила хоть как-то облегчают понимание программного текста, столь важное при отладке многомерного кода. А теперь вернемся к рассмотрению того, что собой представляют две другие ASP-страницы нашего узла.
Напомню, эта страница вызывается после нажатия кнопки с заголовком "Послать вопрос и/или отзыв и/или оценку". Кнопка имеет статус Submit. Одновременно странице передаются данные о заданном пользователем вопросе, сделанном отзыве, проставленной оценке, также как и данные о книге, с которой связаны эти характеристики. Серверный код на этой странице должен обеспечить прием параметров и запись полученной информации в таблицы базы данных. Должен быть сформирован HTML-код, уведомляющий пользователя о результатах выполнения его запроса.
Я решил на этапе проектирования оставить эту страницу пустой - ни интерфейсных объектов, ни даже простого текста на этой странице нет. Все, что на ней появится, когда она откроется на клиентском компьютере, будет определяться результатами выполнения серверного кода. Поэтому, если открыть эту страницу во FrontPage на вкладке Normal, то все будет пусто. Но серверный код в ней, конечно же, присутствует.
Напомню, следуя собственным правилам структурирования кода, я отделяю декларативную часть кода, содержащую описание переменных и процедур. С ее рассмотрения и начнем:
<SCRIPT LANGUAGE=vbscript RUNAT=Server>
'**********************************************************
'** Объявления глобальных переменных, процедур и функций **
'************ на серверной стороне ************************
'Задание объектов ADODB - Connection, Command, Recordset
Dim Con1 'Объект ADODB, задающий соединение
Dim Cmd1 'Объект ADODB, задающий команду
Dim Rst1 'Объект ADODB, задающий набор записей
'Переменные для приема полей формы frmMain страницы Qpage
Dim pAuthor 'Автор книги
Dim pTitle 'Название книги
Dim pQ 'Вопрос
Dim pOp 'Отзыв
Dim pM 'Отметка
Dim pUser 'Автор вопроса, отзыва, отметки
Sub InitVars()
'Инициализация глобальных переменных
Set Con1=Server.CreateObject("ADODB.Connection")
'Получение информации, переданной при пересылке формы
pAuthor =Request.form("Author")
pTitle =Request.form("Title")
pQ =Request.form("Question")
pOp =Request.form("Opinion")
pM =Request.form("Mark")
'pM="Excellent"
pUser=Session("UserId")
End Sub 'InitVars
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 SelectAndExec()
'Анализ запроса и его выполнение
Dim res 'результат выполнения запроса
Dim strMark 'Уведомление о сохранении отметки
Dim strOpinion 'Уведомление о сохранении отзыва
Dim strQuestion 'Уведомление о сохранении вопроса
Dim str1 'строковая константа
On Error Resume Next
str1 = "Сохранение в базе данных прошло успешно"
If Len(pQ)> 2 Then 'Вопрос задан
res = False
res = SaveQuestion
If res = True Then
strQuestion="Ваш Вопрос - " str1
Else strQuestion = "Ваш Вопрос - " Err.Description
End If
End If
If Len(pOp)> 2 Then 'Отзыв задан
res = False
res = SaveOpinion
If res = True Then
strOpinion="Ваш отзыв - " str1
Else strOpinion = "Ваш отзыв - " Err.Description
End If
End if
If pM > 0 Then 'Оценка задана
res = False
res = SaveMark
If res = True Then
strMark="Ваша оценка - " str1
Else strMark = "Ваша оценка - " Err.Description
End If
End If
'Формирование HTML-кода страницы с уведомлением
'о результатах выполнения запроса
Response.Write "<p>" strMark
Response.Write "<p>" strQuestion
Response.Write "<p>" strOpinion
Response.Write "<p><A href=""qpage.asp"">" _
" <font size=7 color = ""#FF6666""> Вернуться </font> </a>"
Response.Write "на страницу Читатели"
End Sub 'SelectAndExec
Function SaveMark
'Сохранение отметки
Dim par 'параметр для формирования запроса на вставку
Set Cmd1=Server.CreateObject("ADODB.Command")
With Cmd1
'Конфигурирование объекта Command
.ActiveConnection = Con1
.CommandText = "InsertMark"
.CommandType = 4 'adCmdStoredProc
For i = 0 to .Parameters.Count -1
.Parameters.Delete(i) 'Чистка коллекции
Next 'i
Set par= .CreateParameter("repMark",2,1)
par.value=pM
.Parameters.Append par
Set par= .CreateParameter("repUID",3,1)
par.value=pUser
.Parameters.Append par
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
.Execute
SaveMark=True
End With
End Function 'SaveMark
Function SaveOpinion
'Сохранение отзыва
Dim par 'параметр для формирования запроса на вставку
Set Cmd1=Server.CreateObject("ADODB.Command")
With Cmd1
'Конфигурирование объекта Command
.ActiveConnection = Con1
.CommandText = "InsertOpinion"
.CommandType = 4 'adCmdStoredProc
For i = 0 to .Parameters.Count -1
.Parameters.Delete(i) 'Чистка коллекции
Next 'i
Set par= .CreateParameter("repOpinion",202,1,255)
par.value=pOp
.Parameters.Append par
Response.Write par
Set par= .CreateParameter("repUID",3,1)
par.value=pUser
.Parameters.Append par
Response.Write par
Set par= .CreateParameter("repAuthor",202,1,255)
par.value=pAuthor
.Parameters.Append par
Response.Write par
Set par= .CreateParameter("repTitle",202,1,255)
par.value=pTitle
.Parameters.Append par
Response.Write par
.Execute
SaveOpinion=True
End With
End Function 'SaveOpinion
Function SaveQuestion
'Сохранение вопроса
Dim par 'параметр для формирования запроса на вставку
Set Cmd1=Server.CreateObject("ADODB.Command")
With Cmd1
'Конфигурирование объекта Command
.ActiveConnection = Con1
.CommandText = "InsertQuestion"
.CommandType = 4 'adCmdStoredProc
For i = 0 to .Parameters.Count -1
.Parameters.Delete(i) 'Чистка коллекции
Next 'i
Set par= .CreateParameter("repQuestion",202,1,255)
par.value=pQ
.Parameters.Append par
Set par= .CreateParameter("repQDate",133,1)
par.value = Date
.Parameters.Append par
Set par= .CreateParameter("repUID",3,1)
par.value=pUser
.Parameters.Append par
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
.Execute
SaveQuestion = True
End With
End Function 'SaveQuestion
</SCRIPT>
Декларативная часть кода достаточно большая - в ней около десятка процедур, которые и решают все задачи, возложенные на эту страницу. Зато исполняемый фрагмент серверного кода невелик - он сводится к вызову тех или иных процедур. Приведу сразу и его, а уже потом приступлю к обсуждению деталей работы по выполнению запросов пользователя.
<body> <SCRIPT LANGUAGE=vbscript RUNAT=Server> '********************************************************** '** Исполняемый код на серверной стороне ** '************ на серверной стороне ************************ 'Инициализация переменных InitVars 'Создать соединение с базой данных CreateConnection 'Анализ запроса и выполнение действия, заданного пользователем SelectAndExec </SCRIPT> </body>
Исполнение серверного кода начинается с инициализации глобальных переменных, которое производится в процедуре InitVars. Помимо привычной инициализации объекта ADODB.Connection именно здесь происходит извлечение информации, переданной в полях формы. Метод (свойство) Form Submit. Присвоив в начале работы эти значения специально созданным глобальным переменным, в дальнейшем уже можно не испытывать никаких затруднений - вся нужная информация, переданная серверу, будет под рукой. Обратите внимание на еще один способ получения информации на стороне сервера. Для получения значения переменной pUser, которая хранит идентификатор пользователя, используется другой Session. Обычно этот объект создается по данным регистрации пользователя, но я упростил задачу и задал его явно на предыдущей странице. Таким образом, после работы процедуры InitVars вся нужная серверу информация получена и хранится в обычных переменных.
Обратите внимание на закомментированный оператор, присваивающий переменной pMark некорректное значение. Я использовал его, чтобы смоделировать ситуацию, приводящую к ошибке при записи в базу данных. Это позволило мне убедиться в том, что предусмотренная кодом обработка ошибок периода выполнения работает должным образом.
Следующим шагом работы является установление соединения с базой данных. Оно типично, и я на нем останавливаться не буду.
Основная работа на серверной стороне выполняется в процедуре SelectAndExec. Прежде всего следует понять, какую информацию прислал пользователь, - послал ли пользователь вопрос, написал ли отзыв, проставил оценку. С этой целью анализируется содержимое переменных, хранящих значение вопроса, отзыва, оценки. Если соответствующее поле пусто (оценка равна 0 ), то ничего записывать в базу данных не нужно, в противном случае вызывается соответствующая функция - SaveQuestion, SaveOpinion, SaveMark, которая и добавляет новую запись в одну из таблиц базы данных.
Обратите внимание, если запись в базу данных пройдет успешно, то переменная res получит значение True. Если же при выполнении функции возникнет ошибка, то предусмотренный обработчик исключительной ситуации On Error позволит продолжить выполнение программы, но переменная res сохранит значение False. В зависимости от значения res формируется то или иное сообщение для уведомления пользователя о результатах выполненной работы.
Последний фрагмент в процедуре SelectAndExec формирует HTML-код страницы с уведомлениями о результатах работы, а также ссылку для возврата на основную страницу. Для этого используется, как обычно, метод Write объекта Response.
Три процедуры - SaveQuestion, SaveOpinion, SaveMark устроены одинаково - все они добавляют одну запись к той или иной таблице базы данных, используя для этого хранимый запрос на добавление записи. Давайте разберем более подробно первую из них. Начну с вида хранимого запроса. Вот его SQL-запись:
PARAMETERS repQuestion Text ( 255 ), repQDate DateTime, repUID Long, repAuthor Text ( 255 ), repTitle Text ( 255 ); INSERT INTO QA ( Question, QDate, UserId, Author, Title ) VALUES ([repQuestion], [repQDate], [repUID], [repAuthor], [repTitle]);
Как видите, это запрос с пятью параметрами. Через параметры полям записи передаются - сам вопрос, дата задания вопроса, название и автор книги, по которой был задан вопрос и идентификатор пользователя, задавшего вопрос.
Понятно, что в функции SaveQuestion, вызывающей этот запрос, при формировании объекта ADODB.Command главной задачей является корректное формирование коллекции Parameters этого объекта. Дальше остается только вызвать метод Execute, чтобы выполнить запрос. Поскольку в главах, посвященных объектам ADO, я подробно рассматривал все свойства и методы этих объектов, в том числе работу с коллекцией Parameters, то теперь останавливаться на деталях, думаю, нет необходимости.
На этом можно и закончить описание вопросов создания программного кода, да и самой страницы в целом. В заключение взгляните, как выглядит эта страница, после того как пользователь послал свою оценку книги на сервер, и эта операция была успешно выполнена.
(рис 11.6) Страница qomToBase с уведомлением об успешном завершении операции
Напомню, эта страница вызывается после нажатия любой из трех командных кнопок с заголовками: "Посмотреть мои вопросы и ответы на них", "Посмотреть все вопросы и ответы на них", "Посмотреть отзывы и оценки". При вызове странице ей передаются три параметра - имя нажатой кнопки, название книги и фамилия автора.
Теперь нам предстоит рассмотреть серверный код, позволяющий принять параметры, проанализировать и выполнить запрос пользователя на получение нужной ему информации. Ну и, конечно же, потребуется сформировать динамически 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) Страница с вопросами и ответами, выводимая по запросу пользователяНа этом я заканчиваю описание примера, главы, книги и всей серии "Офисное программирование".
В этой главе я остановлюсь на создании интерактивных Web-документов. Хотя сегодня подобные документы не столь широко распространены, их значение и роль в общем потоке офисных документов будет только возрастать. Причина этого в том, что, как я уже неоднократно говорил, интрасети все в большей мере становятся средой, в которой ведется совместная работа над документами. Большинство документов, циркулирующих в сети, по своей сути являются интерактивными. В главе 7, где рассматривались вопросы создания интерактивных документов в Excel, отмечалось, что для таких документов характерно:
Эти же свойства характерны и для интерактивных Web-документов.
Различные инструментальные средства используются для построения таких документов, - CGI, Perl, PHP и другие подобные средства. Но, продолжая тему программирования в среде Microsoft Office , я ограничусь рассмотрением инструментальных средств от Microsoft, - вот краткий перечень того, что так или иначе будет затронуто в этой главе: FrontPage 2000, IIS (Internet Information Server), Internet Explorer, ASP (Active Server Pages), VBScript, OWC (Office Web Components), объекты ADO. Из названного подробный разговор шел, пожалуй, только об объектах ADO и OWC, другие же темы затрагивались лишь вскользь. Понятно, что рассмотреть хоть с какой либо степенью подробности затрагиваемые темы мне не удастся, да я и не ставлю такой цели. Я ограничусь примером создания подобного документа. Этот пример и комментарии в процессе создания документа могут служить введением в WEB-программирование. Так что эта последняя глава могла бы служить первой главой новой книги с условным названием "Основы Web-программирования". Главная цель этой главы - показать, что для тех, кто умеет программировать на VBA и создавать интерактивные документы в среде Office 2000, создание интерактивных Web-документов не представляет особых трудностей. Переход от офисного программирования к Web-программированию вполне естественен.
Вместе с тем Web-программирование имеет ряд принципиальных особенностей, затрудняющих как написание, так и отладку соответствующего кода. Прежде чем обсудить эти трудности, сделаю еще одно замечание. Когда я говорил об офисных документах, то всегда отмечал, что документы в среде Microsoft Office могут создаваться без всякого программирования. Офисное программирование начинается тогда, когда документам необходимо придать интерактивность, специфичную для данного документа. Аналогичная ситуация имеет место и при создании Web-документов. Web-мастера и Web-дизайнеры вполне могут создавать Web-документы без всякого программирования. Web-программирование начинается тогда, когда в документ встраиваются различные элементы управления, которые должны реагировать на действия пользователя специфическим для этого документа образом. Если рассматривать Web-документ как книгу, которую читает пользователь, то нет нужды в программировании, даже если страницы просматриваются в произвольном порядке. Если же пользователь определяет не только, какой будет следующая страница, но и влияет на ее содержание, например, требуя, чтобы на странице были размещены нужные ему данные, то тут уже без программирования не обойтись.
Назову три момента, характерные для Web-программирования:
Что я имею в виду, когда говорю о "многомерности" кода? Дело в том, что код, создаваемый программистом, всегда представляет собой линейный текст. Однако исполнение написанного кода в Web-документе, чаще всего, будет разнесено в пространстве и времени. Как правило, у кода будет два исполнителя - Web-сервер на серверной стороне и Обозреватель на клиентской. Прежде чем страница документа будет послана обозревателю, ее текст читается Web-сервером, который находит относящиеся к нему части кода и выполняет его. Страница с результатами этой работы передается по сети клиенту, и там уже обозреватель, установленный на клиентском компьютере, выполняет свою часть работы, исполняя предназначенный ему код и отображая страницу на экране. После этого за дело берется пользователь, работающий за клиентским компьютером, - он выбирает нужные ему данные из списков, заносит требуемую информацию в поля ввода, нажимает командные кнопки и выполняет доступные команды меню. В ходе обработки возникающих событий обозреватель, как правило, обращается к серверу за получением очередной страницы документа, передавая ему одновременно информацию о том, какой должна быть эта страница в соответствии с запросами пользователя. Сервер находит нужную страницу, выполняет относящийся к нему код с учетом полученной им информации. Далее процесс повторяется. Напомню, что в роли Web-сервера я рассматриваю IIS, в роли обозревателя - Internet Explorer, а страницы, выполняемые на серверной стороне, являются ASP-страницами.
Сделаю еще несколько замечаний по поводу деталей этого процесса:
"Многомерный" характер программного кода, конечно, усложняет написание и понимание соответствующего текста. Другим фактором, усложняющим жизнь Web-программистов, является слабая структурированность программного кода. В языке VBScript нет модульности, присущей развитым языкам программирования. Программный код сервера не отделен от программного кода клиентской части. Теги "script" идут вперемешку с другими тегами. Объявления не отделены от исполняемого кода. Ну, уж конечно, нет, как в проектах на VBA, стандартных модулей, модулей - обработчиков событий. Поэтому ответственность за придание нужной структуры в полной мере ложится на программиста, который сам должен придать структуру своему проекту, чтобы ему самому легче было в нем ориентироваться и вносить необходимые изменения, которые не заставляют себя долго ждать. Чуть позже я приведу некоторый набор правил, которыми я пользуюсь в своей работе для придания коду определенной структуры.
Следствием первых двух факторов является и сложность отладки. ASP-страницы, содержащие серверный код отлаживать сложнее, чем HTML-страницы, содержащие только клиентский код. Скажу несколько слов о тех средствах отладки, которые предоставляет редактор VBScript Editor.
Инструментальная среда редактора, во многом, напоминает среду редактора VBA - похожие окна, похожая идеология. Взгляните, как выглядит эта среда в один из моментов работы:
(рис 11.1) Инструментальная среда редактора VBScript в процессе работыКак видите, практически тот же вид, что при работе с редактором VBA . Те же многочисленные панели с командами меню, многие кнопки на которых совпадают по виду и по функциям. Знакомое окно Project Explorer, в котором теперь показаны файлы, содержащие страницы, открытые в редакторе. Заметьте, эти страницы могут принадлежать разным документам - разным Web-узлам.
В основном окне среды отображена сама страница, выбранная в окне Project Explorer. Заметьте, у окна имеется три вкладки - Design, Source, QuickView, позволяющие отобразить различные облики страницы. Открытая на рисунке вкладка Source отображает HTML-код страницы. Страница показана в тот момент, когда процесс отладки находится в режиме прерывания. Процесс вычислений при отладке был прерван, поскольку была достигнута точка прерывания. Замечу, что аппарат точек прерывания здесь даже более мощный, чем в редакторе VBA. В частности для показанной точки прерывания задано условие прерывания, позволяющее прервать вычисление в точке только в том случае, когда выполняется определенное условие.
Обратите внимание и на знакомое окно отладки - окно Immediate. В момент прерывания в этом окне я проводил некоторые вычисления - посмотрел, а затем изменил значение свойства одного из объектов, доступных в контексте прерывания. Мои действия отображены в этом окне.
Еще одна знакомая деталь среды - панель инструментов Toolbox, позволяющая в режиме проектирования добавлять элементы управления на страницу.
Но можно видеть и одно новое окно - HTML Outline, в котором отображаются элементы управления, имеющиеся на странице.
Этот беглый взгляд, надеюсь, дает первое представление о среде редактора VBScript. Если попытаться вкратце охарактеризовать возможности этого редактора, то они следующие:
Как видите, среда Редактора VBScript по многим своим потенциальным возможностям не уступает среде редактора VBA. К сожалению, при отладке серверного кода большая часть из перечисленных средств являются недоступными. Для выполнения серверного кода, то есть при отладке ASP-страниц необходим Web-сервер. Поэтому отладка интерактивных Web-документов, в которых без ASP-страниц не обойтись требует установки IIS или Personal WEB Server. Для узла на сервере должен быть создан виртуальный каталог и страницы этого узла должны вызываться в Internet Explorer с указанием адреса этого каталога.
В примере, о котором я собираюсь рассказать, речь пойдет главным образом об ASP-страницах. Когда возникала ошибка в серверном коде, а без ошибок, естественно, не обошлось, то сообщение об ошибке выдается в Internet Explorer всегда с указанием номера строки, в которой произошла ошибка, а иногда и с указанием причины ошибки. В этом случае я, как правило, возвращался в среду редактора VBScript и использовал такое "мощное" средство отладки как команду "Go To", вызываемую из меню Edit. Открывающееся окно позволяет указать номер строки и перейти на строку, являющуюся причиной ошибки. Поскольку других подсказок в этой ситуации нет, то исправление ошибок в гораздо большей степени зависит от искусства и опыта программиста.
Вот то минимально необходимое, что я считаю должным сказать, перед тем как перейти к рассмотрению примера.
Вернемся снова к нашему Офису РР, который неоднократно встречался в наших примерах, и посмотрим, как он использует интернет и Web-программирование при разработке своих документов. Конечно же, Офис РР давно уже имеет интернет-магазин для продажи своих книг, более того, он дает возможность своим читателям присылать отзывы и выставлять оценки приобретенным книгам. Чтобы связь с читателями была двусторонней, на страницах узла нашего офиса, читатели могут задавать вопросы авторам, переводчикам и редакторам выпускаемых книг, получать ответы на свои вопросы. Читатели могут также ознакомиться с отзывами, присланными на книги и просмотреть весь спектр выставленных оценок.
Давайте рассмотрим, как можно организовать обратную связь с читателями с помощью упомянутых мной средств. При этом я основное внимание сосредоточу на вопросах программирования, оставляя в стороне вопросы дизайна и многие другие вопросы, которые возникают в процессе создания коммерческого решения.
Для тех, кто хочет ознакомиться с реально работающим подобным узлом, советую зайти на узел издательства "Русская Редакция", - http://www.rusedit.ru. Там, правда, нет возможности в настоящее время задавать вопросы и получать ответы, но возможность заказать нужную книгу и написать отзыв имеется.
Давайте займемся проектированием нашего решения, обеспечивающего обратную связь с читателями. Я создам небольшой Web-узел, который буду называть узлом "Читатели". Он и будет представлять наш интерактивный Web-документ, а его страницы и будут являться страницами нашего документа. Основная интерактивность будет связана со страницей, озаглавленной "Страница читателей". Взгляните, как она выглядит, и тогда, надеюсь, станет понятно, что могут делать пользователи, работающие с этим документом:
(рис 11.2) "Страница читателей" интерактивного Web-документаПопадая на эту страницу, пользователь может:
0, установленное по умолчанию, означает, что пользователь не собирается оценивать книгу. По нажатию командной кнопки с надписью "Послать вопрос и/или отзыв и/или оценку" вопрос, отзыв и оценка, если они заданы, передаются в базу данных. Одновременно в базу данных передается информация о книге, с которой связаны эти характеристики, и о пользователе, задавшем эти характеристики.Теперь, когда цели ясны, давайте посмотрим, как спроектировать подобный узел с такой страницей, и как заставить, чтобы все это работало.
Приложение 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".
Теперь, когда сами страницы созданы, можно приступать к их наполнению. Давайте начнем с основной страницы и, прежде всего, поговорим о том, как создавался ее интерфейс, и какие проблемы могут возникать на этом этапе.
Вопрос о том, каким должен быть интерфейс этой страницы, решался еще на этапе проектирования. На рис. 11.2 показано, как выглядит эта страница, отображаемая в Internet Explorer. Поговорим сейчас о реализации этого интерфейса.
Но вначале пару слов о том, какие возможности предоставляет среда FrontPage 2000 для работы с документом. Основное окно среды, в котором отображается документ, имеет три вкладки - Normal, HTML, Preview - каждая из которых показывает тот или иной облик документа. Создавать документ, в особенности его интерфейс, удобнее всего, пожалуй, на вкладке Normal, где все вставляемые объекты отображаются в обычном виде. Вкладка HTML показывает другой облик этого же документа - его HTML-код. Наконец, вкладка Preview позволяет показать документ в том виде, каким он будет отображаться в Internet Explorer, следовательно, с исполнением программного кода. По этой причине попытка просмотреть ASP-страницы на этой вкладке приводит к возникновению ошибки, поскольку код, предназначенный для сервера, выполниться во FrontPage не может.
В среде FrontPage 2000 есть три варианта, где можно выполнить работу по созданию интерфейса документа:
Вернемся к нашему документу. На рис. 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. Это позволяет при отсылке формы серверу, наряду с другой информацией передать данные о той книге,
которую выбрал пользователь.qomToBase будет вызываться тогда, когда данные из формы - вопрос, отзыв, оценка - записываются в базу данных. Эта страница будет вызываться при нажатии кнопки со статусом Submit. Другая ASP-страница - qomFromBase - будет вызываться тогда, когда требуется вывести на экран пользователя информацию, хранящуюся в базе, - вопросы и ответы, имеющиеся отзывы и оценки. В отличие от предопределенных действий, связанных с кнопкой Submit, в этом случае вызов ASP-страницы и передачу ей информации придется организовать программно в обработчиках события Click соответствующих командных кнопок.С нетерпением ждал, когда же, наконец, можно перейти к описанию программного кода, связанного с этой страницей. Этот момент наступил. Что должен делать это код, уже говорилось. На серверной стороне он должен сформировать интерфейсный элемент - список, содержащий названия всех имеющихся книг. На клиентской стороне необходимо в скрытые поля формы передать информацию о выборе пользователем из списка той или иной книги. Ну и, конечно, нужно обеспечить передачу информации серверу и переход на другую страницу при нажатии командных кнопок.
Давайте, вначале рассмотрим код, исполняемый на стороне сервера. Это логично, поскольку он выполняется первым и от его результатов, зачастую, зависит содержание страницы, поступающей клиенту. Приведу вначале декларативную часть кода, содержащую описание глобальных переменных, описание используемых процедур и функций:
<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-код страницы, передаваемой клиенту, но я ограничусь только программным кодом.
И на клиентской стороне можно выделить декларативную и исполняемую часть кода. Заметьте, я предпочитаю, чтобы на серверной стороне исполняемая часть кода присутствовала, в то же время на клиентской стороне ее может не быть. Более того, при хорошем программировании, полагаю, ее не должно быть, как не будет ее и в нашем примере. Причины этого, надеюсь, понятны. Дело в том, что обработка событий на стороне сервера хотя и возможна, но не представляется столь желательной. Поэтому исполняемый участок кода и предназначен для инициирования вычислений. Конечно, достаточно одного оператора, вызывающего первую процедуру, чтобы начать сложные вычисления с многочисленными вызовами процедур, представленными в декларативной части кода. При хорошем стиле программирования так, обычно, и бывает.
Программирование на клиентской стороне в большей степени является программированием, управляемым событиями. Это и понятно - здесь расположены элементы управления, с которыми работает пользователь, а именно он, его действия при работе с документом являются источником возникновения событий. Поэтому основу декларативной части клиентского кода составляют, как правило, процедуры-обработчики событий, возникающих с интерфейсными объектами и другими объектами, расположенными на странице. По этой причине нет необходимости в исполняемом фрагменте кода, хотя он и может присутствовать. Но более естественно для данного стиля программирования для инициирования вычислений иметь процедуру, обрабатывающую событие Load, которая автоматически будет вызываться при открытии страницы. Когда же пользователь начнет работать, то будут вызываться и другие обработчики событий. А теперь взгляните на декларативный код клиентской части нашей страницы. Конечно же, он включает процедуры-обработчики различных событий, возникающих с объектами страницы. Вот этот код:
<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-программирования в нынешних условиях, является слабая структурированность программного кода, отсутствие столь привычного модульного построения программных проектов. Чтобы как-то справиться с этой проблемой, я для себя создал определенные правила. Приведу их, может быть, они будут полезны и другим:
<head>. Декларативной части предшествует специальный комментарий. Вы могли видеть его в тексте приведенного кода.<body>.Эти нехитрые правила хоть как-то облегчают понимание программного текста, столь важное при отладке многомерного кода. А теперь вернемся к рассмотрению того, что собой представляют две другие ASP-страницы нашего узла.
Напомню, эта страница вызывается после нажатия кнопки с заголовком "Послать вопрос и/или отзыв и/или оценку". Кнопка имеет статус Submit. Одновременно странице передаются данные о заданном пользователем вопросе, сделанном отзыве, проставленной оценке, также как и данные о книге, с которой связаны эти характеристики. Серверный код на этой странице должен обеспечить прием параметров и запись полученной информации в таблицы базы данных. Должен быть сформирован HTML-код, уведомляющий пользователя о результатах выполнения его запроса.
Я решил на этапе проектирования оставить эту страницу пустой - ни интерфейсных объектов, ни даже простого текста на этой странице нет. Все, что на ней появится, когда она откроется на клиентском компьютере, будет определяться результатами выполнения серверного кода. Поэтому, если открыть эту страницу во FrontPage на вкладке Normal, то все будет пусто. Но серверный код в ней, конечно же, присутствует.
Напомню, следуя собственным правилам структурирования кода, я отделяю декларативную часть кода, содержащую описание переменных и процедур. С ее рассмотрения и начнем:
<SCRIPT LANGUAGE=vbscript RUNAT=Server>
'**********************************************************
'** Объявления глобальных переменных, процедур и функций **
'************ на серверной стороне ************************
'Задание объектов ADODB - Connection, Command, Recordset
Dim Con1 'Объект ADODB, задающий соединение
Dim Cmd1 'Объект ADODB, задающий команду
Dim Rst1 'Объект ADODB, задающий набор записей
'Переменные для приема полей формы frmMain страницы Qpage
Dim pAuthor 'Автор книги
Dim pTitle 'Название книги
Dim pQ 'Вопрос
Dim pOp 'Отзыв
Dim pM 'Отметка
Dim pUser 'Автор вопроса, отзыва, отметки
Sub InitVars()
'Инициализация глобальных переменных
Set Con1=Server.CreateObject("ADODB.Connection")
'Получение информации, переданной при пересылке формы
pAuthor =Request.form("Author")
pTitle =Request.form("Title")
pQ =Request.form("Question")
pOp =Request.form("Opinion")
pM =Request.form("Mark")
'pM="Excellent"
pUser=Session("UserId")
End Sub 'InitVars
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 SelectAndExec()
'Анализ запроса и его выполнение
Dim res 'результат выполнения запроса
Dim strMark 'Уведомление о сохранении отметки
Dim strOpinion 'Уведомление о сохранении отзыва
Dim strQuestion 'Уведомление о сохранении вопроса
Dim str1 'строковая константа
On Error Resume Next
str1 = "Сохранение в базе данных прошло успешно"
If Len(pQ)> 2 Then 'Вопрос задан
res = False
res = SaveQuestion
If res = True Then
strQuestion="Ваш Вопрос - " str1
Else strQuestion = "Ваш Вопрос - " Err.Description
End If
End If
If Len(pOp)> 2 Then 'Отзыв задан
res = False
res = SaveOpinion
If res = True Then
strOpinion="Ваш отзыв - " str1
Else strOpinion = "Ваш отзыв - " Err.Description
End If
End if
If pM > 0 Then 'Оценка задана
res = False
res = SaveMark
If res = True Then
strMark="Ваша оценка - " str1
Else strMark = "Ваша оценка - " Err.Description
End If
End If
'Формирование HTML-кода страницы с уведомлением
'о результатах выполнения запроса
Response.Write "<p>" strMark
Response.Write "<p>" strQuestion
Response.Write "<p>" strOpinion
Response.Write "<p><A href=""qpage.asp"">" _
" <font size=7 color = ""#FF6666""> Вернуться </font> </a>"
Response.Write "на страницу Читатели"
End Sub 'SelectAndExec
Function SaveMark
'Сохранение отметки
Dim par 'параметр для формирования запроса на вставку
Set Cmd1=Server.CreateObject("ADODB.Command")
With Cmd1
'Конфигурирование объекта Command
.ActiveConnection = Con1
.CommandText = "InsertMark"
.CommandType = 4 'adCmdStoredProc
For i = 0 to .Parameters.Count -1
.Parameters.Delete(i) 'Чистка коллекции
Next 'i
Set par= .CreateParameter("repMark",2,1)
par.value=pM
.Parameters.Append par
Set par= .CreateParameter("repUID",3,1)
par.value=pUser
.Parameters.Append par
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
.Execute
SaveMark=True
End With
End Function 'SaveMark
Function SaveOpinion
'Сохранение отзыва
Dim par 'параметр для формирования запроса на вставку
Set Cmd1=Server.CreateObject("ADODB.Command")
With Cmd1
'Конфигурирование объекта Command
.ActiveConnection = Con1
.CommandText = "InsertOpinion"
.CommandType = 4 'adCmdStoredProc
For i = 0 to .Parameters.Count -1
.Parameters.Delete(i) 'Чистка коллекции
Next 'i
Set par= .CreateParameter("repOpinion",202,1,255)
par.value=pOp
.Parameters.Append par
Response.Write par
Set par= .CreateParameter("repUID",3,1)
par.value=pUser
.Parameters.Append par
Response.Write par
Set par= .CreateParameter("repAuthor",202,1,255)
par.value=pAuthor
.Parameters.Append par
Response.Write par
Set par= .CreateParameter("repTitle",202,1,255)
par.value=pTitle
.Parameters.Append par
Response.Write par
.Execute
SaveOpinion=True
End With
End Function 'SaveOpinion
Function SaveQuestion
'Сохранение вопроса
Dim par 'параметр для формирования запроса на вставку
Set Cmd1=Server.CreateObject("ADODB.Command")
With Cmd1
'Конфигурирование объекта Command
.ActiveConnection = Con1
.CommandText = "InsertQuestion"
.CommandType = 4 'adCmdStoredProc
For i = 0 to .Parameters.Count -1
.Parameters.Delete(i) 'Чистка коллекции
Next 'i
Set par= .CreateParameter("repQuestion",202,1,255)
par.value=pQ
.Parameters.Append par
Set par= .CreateParameter("repQDate",133,1)
par.value = Date
.Parameters.Append par
Set par= .CreateParameter("repUID",3,1)
par.value=pUser
.Parameters.Append par
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
.Execute
SaveQuestion = True
End With
End Function 'SaveQuestion
</SCRIPT>
Декларативная часть кода достаточно большая - в ней около десятка процедур, которые и решают все задачи, возложенные на эту страницу. Зато исполняемый фрагмент серверного кода невелик - он сводится к вызову тех или иных процедур. Приведу сразу и его, а уже потом приступлю к обсуждению деталей работы по выполнению запросов пользователя.
<body> <SCRIPT LANGUAGE=vbscript RUNAT=Server> '********************************************************** '** Исполняемый код на серверной стороне ** '************ на серверной стороне ************************ 'Инициализация переменных InitVars 'Создать соединение с базой данных CreateConnection 'Анализ запроса и выполнение действия, заданного пользователем SelectAndExec </SCRIPT> </body>
Исполнение серверного кода начинается с инициализации глобальных переменных, которое производится в процедуре InitVars. Помимо привычной инициализации объекта ADODB.Connection именно здесь происходит извлечение информации, переданной в полях формы. Метод (свойство) Form Submit. Присвоив в начале работы эти значения специально созданным глобальным переменным, в дальнейшем уже можно не испытывать никаких затруднений - вся нужная информация, переданная серверу, будет под рукой. Обратите внимание на еще один способ получения информации на стороне сервера. Для получения значения переменной pUser, которая хранит идентификатор пользователя, используется другой Session. Обычно этот объект создается по данным регистрации пользователя, но я упростил задачу и задал его явно на предыдущей странице. Таким образом, после работы процедуры InitVars вся нужная серверу информация получена и хранится в обычных переменных.
Обратите внимание на закомментированный оператор, присваивающий переменной pMark некорректное значение. Я использовал его, чтобы смоделировать ситуацию, приводящую к ошибке при записи в базу данных. Это позволило мне убедиться в том, что предусмотренная кодом обработка ошибок периода выполнения работает должным образом.
Следующим шагом работы является установление соединения с базой данных. Оно типично, и я на нем останавливаться не буду.
Основная работа на серверной стороне выполняется в процедуре SelectAndExec. Прежде всего следует понять, какую информацию прислал пользователь, - послал ли пользователь вопрос, написал ли отзыв, проставил оценку. С этой целью анализируется содержимое переменных, хранящих значение вопроса, отзыва, оценки. Если соответствующее поле пусто (оценка равна 0 ), то ничего записывать в базу данных не нужно, в противном случае вызывается соответствующая функция - SaveQuestion, SaveOpinion, SaveMark, которая и добавляет новую запись в одну из таблиц базы данных.
Обратите внимание, если запись в базу данных пройдет успешно, то переменная res получит значение True. Если же при выполнении функции возникнет ошибка, то предусмотренный обработчик исключительной ситуации On Error позволит продолжить выполнение программы, но переменная res сохранит значение False. В зависимости от значения res формируется то или иное сообщение для уведомления пользователя о результатах выполненной работы.
Последний фрагмент в процедуре SelectAndExec формирует HTML-код страницы с уведомлениями о результатах работы, а также ссылку для возврата на основную страницу. Для этого используется, как обычно, метод Write объекта Response.
Три процедуры - SaveQuestion, SaveOpinion, SaveMark устроены одинаково - все они добавляют одну запись к той или иной таблице базы данных, используя для этого хранимый запрос на добавление записи. Давайте разберем более подробно первую из них. Начну с вида хранимого запроса. Вот его SQL-запись:
PARAMETERS repQuestion Text ( 255 ), repQDate DateTime, repUID Long, repAuthor Text ( 255 ), repTitle Text ( 255 ); INSERT INTO QA ( Question, QDate, UserId, Author, Title ) VALUES ([repQuestion], [repQDate], [repUID], [repAuthor], [repTitle]);
Как видите, это запрос с пятью параметрами. Через параметры полям записи передаются - сам вопрос, дата задания вопроса, название и автор книги, по которой был задан вопрос и идентификатор пользователя, задавшего вопрос.
Понятно, что в функции SaveQuestion, вызывающей этот запрос, при формировании объекта ADODB.Command главной задачей является корректное формирование коллекции Parameters этого объекта. Дальше остается только вызвать метод Execute, чтобы выполнить запрос. Поскольку в главах, посвященных объектам ADO, я подробно рассматривал все свойства и методы этих объектов, в том числе работу с коллекцией Parameters, то теперь останавливаться на деталях, думаю, нет необходимости.
На этом можно и закончить описание вопросов создания программного кода, да и самой страницы в целом. В заключение взгляните, как выглядит эта страница, после того как пользователь послал свою оценку книги на сервер, и эта операция была успешно выполнена.
(рис 11.6) Страница qomToBase с уведомлением об успешном завершении операции
Напомню, эта страница вызывается после нажатия любой из трех командных кнопок с заголовками: "Посмотреть мои вопросы и ответы на них", "Посмотреть все вопросы и ответы на них", "Посмотреть отзывы и оценки". При вызове странице ей передаются три параметра - имя нажатой кнопки, название книги и фамилия автора.
Теперь нам предстоит рассмотреть серверный код, позволяющий принять параметры, проанализировать и выполнить запрос пользователя на получение нужной ему информации. Ну и, конечно же, потребуется сформировать динамически 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) Страница с вопросами и ответами, выводимая по запросу пользователяНа этом я заканчиваю описание примера, главы, книги и всей серии "Офисное программирование".
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.