Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Вопросы по Spring MVC


Автор: garbuz 8.5.2009, 18:14
Изучаю SpringMVC, пишу некое подобие блога.

1) Есть PostController, который выбирает из базы пост и комменты к нему, затем все это отображается на страничке. Там же есть поле для добавления комментария. Как лучше организовать этот процесс? а именно:

- Должен ли быть контроллер, который обрабатывает комментарии обязательно FormController, так как сам комментарий мы получаем из формы. Или же это может быть обычный AbstractController, а текст комментария просто получать из запроса. (Валидацию можно возложить на javascript)

- Когда сабмичу комментарий нужно как-то указать к какому посту этот комментарий принадлежит, хотя мы явно понимаем какому, но это же надо как-то указать и приложению. Тут я вижу два пути -  передавать сам объект типа Post с jsp странички, который мы раньше получили и отобразили, или же передавать id этого поста и уже в контроллере по id выбирать пост и назначать его комментарию. Если втрой вариант, то id можно передать hidden полем в форме, а если первый, как то как передать объект Post? Просто в скриплете положить в запрос или в сессию? Может есть более красивое решение? Может есть какие теги спринговые полезные или еще есть вариант?

2) Добавление комментариев лучше производить в отдельном контроллере или же в PostControlelr?

3) Предположим мы добавили комментарий, если это произошло в отдельном контроллере (CommentController), то как нам потом форварднуться на PostController, чтобы заново выбрать и отобразить уже пост с обновленными комментариями?      

4) Если форварднемся на PostController, то опять будет происходить выборка того же поста и тех же комментариев + 1 новый. Не есть хорошо. Можно конечно же деалть через аджакс, но аджакс пока прикручивать я не тороплюсь.

Короче вот такая цепочка вопросов.  smile 

Автор: Vasay 8.5.2009, 18:35
garbuz

Цитата

(Валидацию можно возложить на javascript)


ИМХО Плохая идея. Валидация ВСЕГДА должна быть на стороне сервера и опционально на клиенте, поскольку, js на клинте может работать не корректно, или просто не работать - откуда Вы знаете с какого устройства будут смотреть Ваш блог.

Кроме того, любой "кулхацкер" сможет подправив код страницы, обойти Вашу JS валидацию.

Цитата

2) Добавление комментариев лучше производить в отдельном контроллере или же в PostControlelr?


ИМХО - в отдельном.

Цитата

- Когда сабмичу комментарий нужно как-то указать к какому посту этот комментарий принадлежит, хотя мы явно понимаем какому, но это же надо как-то указать и приложению. Тут я вижу два пути -  передавать сам объект типа Post с jsp странички, который мы раньше получили и отобразили, или же передавать id этого поста и уже в контроллере по id выбирать пост и назначать его комментарию. Если втрой вариант, то id можно передать hidden полем в форме, а если первый, как то как передать объект Post? Просто в скриплете положить в запрос или в сессию? Может есть более красивое решение? Может есть какие теги спринговые полезные или еще есть вариант?


Думаю, id в хидден поле. 


Цитата

4) Если форварднемся на PostController, то опять будет происходить выборка того же поста и тех же комментариев + 1 новый. Не есть хорошо. Можно конечно же деалть через аджакс, но аджакс пока прикручивать я не тороплюсь.


ИМХО, это тот случай, когда грамотная реализация ajax будет лучшим выбором, а пока - "выборка того же поста и тех же комментариев + 1 новый"

Автор: garbuz 8.5.2009, 18:57
Vasay, спасибо за ответы!

Цитата(Vasay @  8.5.2009,  18:35 Найти цитируемый пост)
ИМХО Плохая идея. Валидация ВСЕГДА должна быть на стороне сервера и опционально на клиенте,

Полностью согласен, но на первое время думал обойтись без серверной валидации.


Цитата(Vasay @  8.5.2009,  18:35 Найти цитируемый пост)
Думаю, id в хидден поле. 

Смотрите, если же передавать не id а объект Post, то этот объект можно потом передать в Post контроллер и тогда нам останется подтацить только комменты. Т.е. что лучше будет? Потаскать объект через контроллеры или сделать + 1 запрос к базе?  Отсюда вытекает вопрос, который есть в первом моем посте - как передать объект правильнее и как потом с этим объектом форварднуться на другой контроллер?

Автор: garbuz 13.5.2009, 15:53
Следующий вопрос.
Создание нового поста лучше производить в отдельном контроллере или же в этом же? Большинство небольших примеров что я видел, работали по правилу один запрос - один контроллер. Правильно ли это? Я думаю сейчас делать все в одном контроллере, например в PostController будет работа с потами (типа просмотр, создание, удаление, редактирование и т.п.) Рулить собираюся всем через параметр action, в контроллере перебираю его через if и в зависимости от значения выполняю действия. Такой подход нормальный? Или же лепить много контроллеров на каждое действие?

Автор: goodday1941 13.5.2009, 16:44
id в хиден поля можно не класть

использую к примеру SimpleFormController
и добавив атрибут в настройке контроллера sessionForm = true (в таком случае контроллер в formBackingObject будет заходить только один раз, но там есть подводные камни)

и еще пытайтесь всегда использовать для форм SimpleFormController (ну и еже с ними Wizard и тд, но не AbstractController и MultiactionController) в конечном итоге это очень сильно сэкономит время smile

Добавлено через 2 минуты и 36 секунд
валидацию форм тоже проводите в отдельных классах имплементирующих интерфейс Validator

Добавлено через 4 минуты и 1 секунду
Цитата(garbuz @  8.5.2009,  18:57 Найти цитируемый пост)
Полностью согласен, но на первое время думал обойтись без серверной валидации.

зря... делается она придельно просто и красиво с помощью спринга

Автор: garbuz 13.5.2009, 17:06
goodday1941, спасибо за советы, учту. А вот что на счет последнего вопроса?
Цитата

Создание нового поста лучше производить в отдельном контроллере или же в этом же? Большинство небольших примеров что я видел, работали по правилу один запрос - один контроллер. Правильно ли это? Я думаю сейчас делать все в одном контроллере, например в PostController будет работа с потами (типа просмотр, создание, удаление, редактирование и т.п.) Рулить собираюся всем через параметр action, в контроллере перебираю его через if и в зависимости от значения выполняю действия. Такой подход нормальный? Или же лепить много контроллеров на каждое действие? 

Автор: goodday1941 13.5.2009, 17:40
я делаю обычно так:

форма - 100% SimpleFormController с Validator (даже если нечего валидировать smile, хотя что валидировать есть всегда)
что то визардовое (пошаговое) - соответственно WizardController
ну и далее действие относящиеся к одной сущности (например показать список пользователей, удалить пользователя) - MultiactionController
AbstractController использую очень редко но бывают ситуации


рулить через параметр экшин ИМХО недопустимо....  if - else это из розряда функционального программирования ) легко запутаться
лучше иметь 10 простых классов по 100 строк чем один на 1000

Автор: garbuz 13.5.2009, 18:24
goodday1941, еще раз спасибо за советы. Про formController приму к сведению, даже наверно постараюсь переделать то, что уже есть.
Недавно читал про MultiactionController. Там идея понятна, если конечно сведения из моей книги не устарели smile
Допустим будет что-то типа такого 
Код

public class PostController extends MultiActionController{
     private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }

    public ModelAndView create(request, response){
    // тут создаем новый пост 
    }

    public ModelAndView delete(request, response){
          // тут получаем id поста и удаляем его
    } 
}

В случае отображения, как я понимаю урл должен быть типа /post.htm?action=show&postId=x
В случае create, как я понимаю урл должен быть типа /post.htm?action=create
В случае delete, как я понимаю урл должен быть типа /post.htm?action=delete&postId=x

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

Добавлено через 7 минут
И как быть если у нас один котроллер должен быть  Multiaction, а так же должен обрабатывать данные от формы? От какого екстендиться предпочтительнее?

Автор: goodday1941 13.5.2009, 19:19
Цитата(garbuz @  13.5.2009,  18:24 Найти цитируемый пост)
В случае отображения, как я понимаю урл должен быть типа /post.htm?action=show&postId=x
В случае create, как я понимаю урл должен быть типа /post.htm?action=create
В случае delete, как я понимаю урл должен быть типа /post.htm?action=delete&postId=x

нет

урл зависит от того что прописано в маппинге...
например при SimpleUrlHandlerMapping будем иметь урлы = названию метода
тоесть 
     
Код

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
            <props>
                <prop key="/test/*">multiController</prop>
              </props>
        </property>
    </bean>


так же можно явно указывать какой урл соответствует какому методу, думаб в вашей книге это есть ) если конечно ваша книга называется Spring in Action )

Добавлено через 3 минуты и 26 секунд
Цитата(garbuz @  13.5.2009,  18:24 Найти цитируемый пост)
И как быть если у нас один котроллер должен быть  Multiaction, а так же должен обрабатывать данные от формы? От какого екстендиться предпочтительнее? 

тут будет два контроллера


Цитата(garbuz @  13.5.2009,  18:24 Найти цитируемый пост)
Естественно, что действие удаления поста будет доступно только для админов, но обычный пользователь тоже сможет написать такой урл и отправить его серверу и тогда произойдет удаление поста, который не следует удалять. Как быть в такой ситуации?

почитай про asegi - это приблуда на спринг для аутентификации, 
ну или более простой вариант повесить фильтр на урл доступ к которому имеют только одмины

Добавлено через 5 минут
фильтр проверяет залогинен ли пользователь если нет то редиректит запрос на страницу с логином

Автор: garbuz 13.5.2009, 19:40
Цитата(goodday1941 @  13.5.2009,  19:19 Найти цитируемый пост)
так же можно явно указывать какой урл соответствует какому методу, думаб в вашей книге это есть ) если конечно ваша книга называется Spring in Action )

А можно примерчик, если есть конечно. Книга Spring in Action, но первое издание. Посмотрел во втором, там про Multiaction не написано вообще, точнее написано, а примера нет.

Автор: garbuz 13.5.2009, 23:33
Итак. С самого начала начал лепить практически везде abstract контроллеры. Ну что же, первый блин как всегда. Решил все переделать с самого начала, но сперва решил еще раз посоветоваться. Вот что я решил:
1) HomePageController ext AbstractController - выводит на главную страницу все посты и информацию к ним (дату, теги, автора).

2) PostController ext MultiactionController - показывает конкретный пост с коментариями и удаляет посты, принимает в качестве параметра postId.

3) CreatePostController ext SimpleFormController - создает и редактирует пост, так как там тоже все на формах. А возможно в этом контроллере проводит вякую валидацию и создание объекта, а потом с этим объектом редиректиться на PostController и там уже в соответствующих метода записывать объект в базу? Или так не стоит делать?

4) CommentController ext SimpleFormController - создает новый комментарий, который приходит из формы, хотя в форме всего одно поле. (думаю что удаление и редактирование комментариев не нужно вовсе, хотя удаление может быть...)

ну что? Кто что думает на счет таког подхода?

Теперь осталось разобраться как всем эти рулить.

Нашел в оф доках  ParameterMethodNameResolver, который выбирает метод в зависимости от значения указанного параметра, например action=create, action=edit.
Так же нашел PropertiesMethodNameResolver, который, как я понял, биндит урл на конкретный метод. 
Там же в обоих примерах упоминается некий делегат, которому, как я понял, мы можем делегировать нек методы. Вопрос для чего? Не проще ли все объявленные методы в контроллере реализовать? Зачем еще делегат?

Короче вот как-то так, жду ответов smile Заранее спасибо!  smile 

Автор: _and 14.5.2009, 05:13
garbuz, советую посмотреть на сэмплы спринга типа jpetstore и остальные(обычно лежат в архиве спринга) половина вопросов отпадут сами собой

Цитата(garbuz @  14.5.2009,  03:33 Найти цитируемый пост)
3) CreatePostController ext SimpleFormController - создает и редактирует пост, так как там тоже все на формах. А возможно в этом контроллере проводит вякую валидацию и создание объекта, а потом с этим объектом редиректиться на PostController и там уже в соответствующих метода записывать объект в базу? Или так не стоит делать?

этого точно не стоит делать, каждое действие должно лежать в своем классе


Цитата(garbuz @  14.5.2009,  03:33 Найти цитируемый пост)
4) CommentController ext SimpleFormController - создает новый комментарий, который приходит из формы, хотя в форме всего одно поле. (думаю что удаление и редактирование комментариев не нужно вовсе, хотя удаление может быть...)

можно добавить поля имя автора камента и мыло. Удаления камента сделаь для админа и автора поста

Цитата(garbuz @  13.5.2009,  23:40 Найти цитируемый пост)
А можно примерчик, если есть конечно. Книга Spring in Action, но первое издание. Посмотрел во втором, там про Multiaction не написано вообще, точнее написано, а примера нет.

взято из сэмпла по спрингу petclinic
Код

<!-- маппинг -->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/welcome.htm">clinicController</prop>
                <prop key="/vets.htm">clinicController</prop>
                <prop key="/owner.htm">clinicController</prop>
            </props>
        </property>
    </bean>
<!-- контроллер -->
<bean id="clinicController" class="org.springframework.samples.petclinic.web.ClinicController">
        <property name="methodNameResolver" ref="clinicControllerResolver"/>
        <property name="clinic" ref="clinic"/>
    </bean>
<!-- резолвер для мультиакшн контроллера -->
    <bean id="clinicControllerResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
        <property name="mappings">
        <props>
            <prop key="/welcome.htm">welcomeHandler</prop>
            <prop key="/vets.htm">vetsHandler</prop>
            <prop key="/owner.htm">ownerHandler</prop>
        </props>
        </property>
    </bean>

Автор: goodday1941 14.5.2009, 11:07
Цитата(garbuz @  13.5.2009,  23:33 Найти цитируемый пост)
Нашел в оф доках  ParameterMethodNameResolver, который выбирает метод в зависимости от значения указанного параметра, например action=create, action=edit.

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

Цитата(garbuz @  13.5.2009,  23:33 Найти цитируемый пост)
3) CreatePostController ext SimpleFormController - создает и редактирует пост, так как там тоже все на формах. А возможно в этом контроллере проводит вякую валидацию и создание объекта, а потом с этим объектом редиректиться на PostController и там уже в соответствующих метода записывать объект в базу? Или так не стоит делать?

валидацью можно проводить и в этом классе но лучше написать отдельный класс валидатор.. в интернете примеров валом есть как это делается
создание объекта - что имеется ввиду?

Автор: garbuz 14.5.2009, 11:51
Цитата(goodday1941 @  14.5.2009,  11:07 Найти цитируемый пост)

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

Значит лучше использовать PropertiesMethodNameResolver?

Со всем остальным пока буду разбираться.

Автор: goodday1941 14.5.2009, 12:16
Цитата(garbuz @  14.5.2009,  11:51 Найти цитируемый пост)

Значит лучше использовать PropertiesMethodNameResolver?

я использую SimpleUrlHandlerMapping и не указую резолвера когда создаю спринг бины

тогда например название метода в MultiactionController'е будет соответствовать урлу, а для остальных что пропишем при маппинге то и получим

но это дело вкуса многие прописывают для мультиэкшн контроллера PropertiesMethodNameResolver

Автор: garbuz 14.5.2009, 15:41
Итак господа, смотрим что получилось. Пинаем, ругаем советуем.

Код

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="home" class="ru.blog.web.controllers.HomePageController">
        <property name="daoFacade" ref="daoFacade"/>
    </bean>

    <bean name="post" class="ru.blog.web.controllers.PostController">
        <property name="daoFacade" ref="daoFacade"/>
        <property name="methodNameResolver" ref="postControllerResolver"/>
    </bean>

   
    <bean id="postControllerResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
        <property name="mappings">
        <props>
            <prop key="/showPost.htm">show</prop>
            <prop key="/newPost.htm">create</prop>
            <prop key="/editPost.htm">edit</prop>
        </props>
        </property>
    </bean>

    <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/home.htm">home</prop>
                <prop key="/showPost.htm">post</prop>
                <prop key="/newPost.htm">post</prop>
                <prop key="/editPost.htm">post</prop>
                           
            </props>
        </property>
    </bean>



    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value=""></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>



Получаем все посты и отображаем их на главной.
Код

public class HomePageController extends AbstractController {

    private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }

    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<Post> posts =  daoFacade.getPostDAO().getAll();
        return new ModelAndView("home", "posts", posts);
    }
}



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

home.jsp
Код

<%@ include file="include.jsp" %>
<html>
<head><title>Home page</title></head>
<body>
<div style="width:900px">
    <%@ include file="header.jsp" %>

    <%
        boolean loggedIn = false;
        if (request.getUserPrincipal() != null){
            loggedIn = true;
        }
        pageContext.setAttribute("loggedIn", new Boolean(loggedIn));
    %>

    <table border="1">

        <c:forEach items="${posts}" var="post">
            <tr>
                <td colspan="2"><h4><c:out value="${post.postName}"/></h4>
                    <c:if test="${loggedIn}">
                        <c:set var="author" value="<%=request.getUserPrincipal().getName()%>"/>
                        <c:if test="${post.author.login eq author}">
                            <a href="editPost.htm?postId=${post.postId}">edit</a>
                        </c:if>
                    </c:if>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <c:out value="${post.preview}"/> <br><br>
                    <a href="showPost.htm?postId=${post.postId}">Read more >></a>
                </td>
            </tr>
            <tr>
                <td><c:out value="${post.author.login}"/></td>
                <td><c:out value="${post.createdDate}"/></td>

            </tr>
            <tr>
                <td colspan="2">
                    <c:forEach items="${post.tags}" var="tag">
                        <c:out value="${tag.tagName}"/>
                    </c:forEach>
                </td>
            </tr>
        </c:forEach>

    </table>
</div>

</body>
</html>




Код

public class PostController extends MultiActionController {

    private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }


    public ModelAndView show(HttpServletRequest request, HttpServletResponse response) {
        String postId = request.getParameter("postId");
        Post post = daoFacade.getPostDAO().getById(Long.parseLong(postId));
        List<Comment> comments = daoFacade.getCommentDAO().getFromPost(post);
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("post", post);
        model.put("comments", comments);
        return new ModelAndView("post", model);
    }


    public ModelAndView create(HttpServletRequest request, HttpServletResponse response) {
        List<Category> categories = daoFacade.getCategoryDAO().getAll();
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("categories", categories);
        return new ModelAndView("newPost", model);
    }

    public ModelAndView edit(HttpServletRequest request, HttpServletResponse response) {
        String postId = request.getParameter("postId");
        Post post = daoFacade.getPostDAO().getById(Long.parseLong(postId));
        List<Category> categories = daoFacade.getCategoryDAO().getAll();
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("post", post);
        model.put("categories", categories);
        return new ModelAndView("editPost", model);
    }


}


Сейчас PostController занимается тем, что показывает конкретный пост, выбирает данные для создания нового поста и переправляет нас на нужную вьюху, выбирает данные для редактирования поста и перенаправляет нас на нужную вьюху. Контроллеры, которые будут создавать посты и редактировать, еще не готовы. Пока в FormController не разобрался, как-то примеров внятных не получается найти. Может кто выложит и объяснит что к чему?

И еще вопрос, который уже был выше. Итак, пользователь у нас залогинен, ему отображается ссылочка для редактирования поста типа 
http://localhost:8080/blog/editPost.htm?postId=1, он там подредактирует, отсабмитит и все окей. Если пользователь не залогинен, тогда он отсылается на форму логина, это делает сам томкат. Но что делать если пользователь залогинен, но не является автором поста но каким-то чудом узнал такую ссылку и прошел по ней. Открывается та же форма редактирования. Выше предлагались решения, пока решил остановиться на фильтре. Как в фильтре проверить может ли этот пользователь править пост или нет?


Автор: goodday1941 14.5.2009, 16:37
времени нет смотреть, но то что бросилось в глаз:

 <%@ include file="header.jsp" %>

скриплеты - зло - по возможности попробуй отказаться от них раз и навсегда

используй jstl теги

используй теги Spring MVC для форм которые пишуться под SimpleFormController

Автор: garbuz 14.5.2009, 17:09
Цитата(goodday1941 @  14.5.2009,  16:37 Найти цитируемый пост)
<%@ include file="header.jsp" %>

скриплеты - зло - по возможности попробуй отказаться от них раз и навсегда

используй jstl теги

Окей, буду стараться.


Как на счет остальных вопросов в предыдущем посте.

Автор: garbuz 15.5.2009, 15:34
Разбираюсь с simpleFormController. Вот цитата из Spring in Action
Цитата

Form controllers take the concept of command controllers a step further, by adding functionality to display a form when an HTTP GET request is received and process the form when an HTTP POST is received.

Значит моя писанина выше с PostController не верна! Ведь именно он сейчас отображает вьюхи с формами! Так же это значит, что мой PostController не должен быть никаким multiAction контреллером, а должен лишь выполнять одно действие - выбирать пост и отображать его.

Итак, что я понял из прочитанного. Если где-то ошибусь - прошу поправить.

1) Когда приходит GET запрос, то контроллер показываем форму, когда POST, то обрабатывает форму.
2) У бина формы есть две вьюхи - одна сама форма, вторая - куда переходим после удачной обработки формы.
3) В конструкторе контроллера можно вызвать два метода setCommandClass()setCommandName, эти же параметры мы можем указать бину через xml. Как я понимаю это класс и имя объекта, которые получатся после сабмита формы
4) В методе formBackingObject происходит создание объекта, который нам вернет сабмит формы.
5) Метод referenceData нужен для того, чтобы передать на форму какие-то доп данные. Например для создания поста, я в форму должен передать список категорий. Категории получаем именно в этом методе.
6) Метод onSubmit нужен для получения объекта из формы, дальнейшей работы с ним и перехода на successView

Непонятно следующее! Как происходит заполнение объекта данными из формы? Может кто объяснит немного? И еще интересует вопрос использования спринговых тегов, можно даже примерчик сюда кинуть ))

Вот мои мысли по поводу этого контроллера, если где не прав - исправляйте, если где мало сказал - дополняйте smile

Автор: Старовъръ 15.5.2009, 18:47
Пример:
Код

<form:form commandName="form">
   <form:input path="searchValue"/>
</form>
<form:form commandName="form"> - значит, что работа будет проходить с объектом form(это который backing object). 
<form:input path="searchValue"/> - поле ввода, связанное с полем searchValue объекта form.
Спринг сам заполнит нужное поле, то есть выполнит операцию:
form.setSearchValue("введенное в поле ввода значение")

Добавлено через 6 минут и 33 секунды
Цитата

И еще вопрос, который уже был выше. Итак, пользователь у нас залогинен, ему отображается ссылочка для редактирования поста типа 
http://localhost:8080/blog/editPost.htm?postId=1, он там подредактирует, отсабмитит и все окей. Если пользователь не залогинен, тогда он отсылается на форму логина, это делает сам томкат. Но что делать если пользователь залогинен, но не является автором поста но каким-то чудом узнал такую ссылку и прошел по ней. Открывается та же форма редактирования. Выше предлагались решения, пока решил остановиться на фильтре. Как в фильтре проверить может ли этот пользователь править пост или нет?
Я выше не читал, но так понял, что Spring Security здесь не участвует? Имхо, лучше сразу делать с ним, потратишь время на решение этого вопроса иным способом, а потом снова тратить время на изучение нужного способа.

Автор: garbuz 15.5.2009, 19:01
Старовъръ, спасибо. Не мог бы код jsp скинуть, где эти теги учавствуют и есть какая-нить форма? Если есть конечно smile

Автор: Старовъръ 15.5.2009, 19:07
Формочка пока совсем еще примитивная: парочка меток, поле ввода, сабмит и табличка:
Код

<%@include file="/WEB-INF/jspf/common/include.jspf" %>
<html>
<head><title><fmt:message key="listUser.title"/></title></head>
<body>
<div>
    <fmt:message key="listUser.title"/>
</div>
<form:form commandName="form">
    <div>
        <fmt:message key="listUesr.search"/> <form:input path="searchValue"/>
        <input type="submit" value="<fmt:message key="common.ok"/> "/>
    </div>
    <div>
        <table width="3">
            <tr>
                <th><fmt:message key="user.login"/></th>
                <th><fmt:message key="user.address.city"/></th>
                <th><fmt:message key="user.rating"/></th>
                <th><fmt:message key="user.pm"/></th>
            </tr>
            <c:if test="${fn:length(users) eq 0}">
                <tr><fmt:message key="listUser.noUsers"/></tr>
            </c:if>
            <c:forEach items="${users}" var="user" varStatus="varStatus">
                <tr>
                    <td><c:out value="${user.login}"/></td>
                    <td><c:out value="${user.address.city}"/></td>
                    <td><c:out value="${user.rating}"/></td>
                    <td><c:url value="htt://PM"/></td>
                </tr>
            </c:forEach>
        </table>
    </div>
</form:form>
</body>
</html>
</html>
Вот backing object:
Код

/**
 * @author ctapobep
 *         Date: 18.04.2009
 *         Time: 12:44:44
 */
public class SearchUsersForm {
    private final String DEFAULT_SORTED_FIELD = "login", DEFAULT_SEARCH_VALUE = "";
    private String backPage, forwardPage;
    private String searchValue;
    private String sortedField;
    private int pageNumber;

    public SearchUsersForm() {
        sortedField = DEFAULT_SORTED_FIELD;
        searchValue = DEFAULT_SEARCH_VALUE;
    }

    public String getBackPage() {
        return backPage;
    }

    public void setBackPage(String backPage) {
        this.backPage = backPage;
    }

    public String getForwardPage() {
        return forwardPage;
    }

    public void setForwardPage(String forwardPage) {
        this.forwardPage = forwardPage;
    }

    public int getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    public String getSearchValue() {
        return searchValue;
    }

    public void setSearchValue(String searchValue) {
        this.searchValue = searchValue;
    }

    public String getSortedField() {
        return sortedField;
    }

    public void setSortedField(String sortedField) {
        this.sortedField = sortedField;
    }
}

Вот контроллер:
Код

import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import ru.javatalks.jtchess.model.search.AccountSearchRequest;
import ru.javatalks.jtchess.model.search.SearchRequest;
import ru.javatalks.jtchess.model.service.UserDetailsService;
import ru.javatalks.jtchess.web.form.SearchUsersForm;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ctapobep
 *         Date: 18.04.2009
 *         Time: 9:06:13
 */
public class SearchUsersController extends SimpleFormController {

    private UserDetailsService userDetailsService;
    private int recordsLimit;

    public SearchUsersController() {
        setCommandClass(SearchUsersForm.class);
        setCommandName("form");
    }

    @Override
    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response,
                                    Object command, BindException e) throws Exception {
        SearchUsersForm form = (SearchUsersForm) command;
        ModelAndView modelAndView = new ModelAndView(getSuccessView());
        modelAndView.addObject("form", form);
        modelAndView.addObject("users", userDetailsService.getUsers(createSearchRequest(form)));
        return modelAndView;
    }

    private SearchRequest createSearchRequest(SearchUsersForm form) {
        AccountSearchRequest searchRequest = new AccountSearchRequest();
        searchRequest.setSearchField("login");
        searchRequest.setSearchValue(form.getSearchValue());
        searchRequest.setSortedField(form.getSortedField());
        String page = form.getBackPage();
        searchRequest.setLimit(recordsLimit);
        return searchRequest;
    }

    @Override
    protected Map referenceData(HttpServletRequest httpServletRequest, Object command,
                                Errors errors) throws Exception {
        SearchUsersForm form = (SearchUsersForm) command;
        Map<String, Object> attr = new HashMap<String, Object>(3);
        attr.put("users", userDetailsService.getUsers(createSearchRequest(form)));
        return attr;
    }

    public void setUsersService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void setRecordsLimit(int recordsLimit) {
        this.recordsLimit = recordsLimit;
    }
}
Правда это у меня еще все в разработке... Спринг не так давно начал изучать.

Автор: Старовъръ 15.5.2009, 19:42
Кстати, можно использовать и обычные html-теги:
<input type="textarea" name="form.searchValue">
Но если произошла ошибка и страницу нужно наново показать, то в данном случае все введенные данные потеряются. Используя же spring bind tags ничего страшного не произойдет - поля наново заполнятся.

Автор: garbuz 15.5.2009, 22:09
Старовъръ, спасибо, буду курить smile завтра )

Автор: Kangaroo 17.5.2009, 01:54
Цитата(garbuz @  15.5.2009,  15:34 Найти цитируемый пост)
Метод onSubmit нужен для получения объекта из формы, дальнейшей работы с ним и перехода на successView

Объект из формы передается как параметр в онСабмит.

По всем остальным пунктам - правильно.

Автор: garbuz 26.5.2009, 19:10
Появилось время, сел опять за спринг. Разбираюсь дальше с FormController.
Все вроде бы сделал правильно. 
Код

public class CreatePostController extends SimpleFormController {
    private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }


    @Override
    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse httpServletResponse, Object o, BindException e) throws Exception {
        Post post = (Post) o;
        daoFacade.getPostDAO().create(post);
        return new ModelAndView(getSuccessView());
    }



    @Override
    protected Object formBackingObject(HttpServletRequest request) throws Exception {
        Post post = new Post();
        post.setAuthor(daoFacade.getUserDAO().getByLogin(request.getUserPrincipal().getName()));
        return post;
    }

    @Override
    protected Map referenceData(HttpServletRequest request) throws Exception {
        List<Category> categories = daoFacade.getCategoryDAO().getAll();
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("categories", categories);
        return model;
    }
}


Код

<form:form method="POST" commandName="post">
      <table style="width:800px">
        <tr>
            <td>Choose categories</td>
            <td>
                <form:select path="categories" multiple="multiple">
                    <form:options items="${categories}" itemValue="categoryId" itemLabel="categoryName"/>
                </form:select>
            </td>
        </tr>
        <tr>
            <td>Post Name</td>
            <td>
                <form:input path="postName" />

            </td>
        </tr>
        <tr>
            <td>Preview</td>
            <td>
                <form:textarea path="preview" rows="10" cols="40"></form:textarea>
            </td>
        </tr>
        <tr>
            <td>Post Body</td>
            <td>
                <form:textarea path="postBody" rows="10" cols="40"></form:textarea>
                <input type="submit" value="submit">
            </td>
        </tr>
    </table>
    
</form:form>


Код

<bean name="createPost" class="ru.blog.web.controllers.CreatePostController">
        <property name="daoFacade" ref="daoFacade"/>
        <property name="formView" value="newPost"/>
        <property name="successView" value="post"/>
        <property name="commandClass" value="ru.blog.entity.Post"/>
        <property name="commandName" value="post"/>

    </bean>


Странно то, что после сабмита формы я не попадаю в метод onSubmit, у меня проиходит снова вызов formBackingObject и referenceData. Почему?
И еще вопрос. Например я отсабмитился, все нормально. Далее должен следовать переход на success view. А там сразу бы хотелось отобразить новый пост, а этим занимается другой контроллер. Короче надо перейти на другой конторллер и еще ему параметр передать.

Автор: garbuz 27.5.2009, 13:54
Цитата из Spring in action
Цитата

The onSubmit() method handles the form submission (an HTTP POST
request) by passing the command object (which is an instance of Rant) to the
addRant() method of the injected RantService reference.

Не понимаю, почему у меня после сабмита формы не вызывается этот метод. Метод пост в форме указан явно, commaddName тоже поменял с post на newPost, даже атрибут action явно указал, но все равно попадаю в formBackingObject()

Автор: Kangaroo 27.5.2009, 14:26
Цитата(garbuz @  26.5.2009,  19:10 Найти цитируемый пост)
Странно то, что после сабмита формы я не попадаю в метод onSubmit, у меня проиходит снова вызов formBackingObject и referenceData. Почему?

formBackingObject  вызывается всегда.
А вот referenceData не должен. Может какие-то ошибки валидации и Спринг обратно показывает форму?

Попробуй:
1. Пройтись дебагом. Посмотри метод processFormSubmission , там как раз вызывается onSubmit
2. Посмотри полный лог Спринга (левел DEBUG).


Цитата(garbuz @  26.5.2009,  19:10 Найти цитируемый пост)
А там сразу бы хотелось отобразить новый пост, а этим занимается другой контроллер. Короче надо перейти на другой конторллер и еще ему параметр передать. 

В онсабмите:
Код

return new ModelAndView(new RedirectView("куда шагать"));

Автор: garbuz 27.5.2009, 15:22
Цитата(Kangaroo @  27.5.2009,  14:26 Найти цитируемый пост)
formBackingObject  вызывается всегда

Почему? Разве не только перед отображением формы? Насколько я понимаю, в этом методе создается объект, который будет запонятсья данными из формы, и его же мы получам в onSubmit как параметр. Зачем еще раз вызов метода formBackingObject(), зачем нам второй объект?
Если я неверно понимаю ситуацию - прошу объяснить smile


Цитата(Kangaroo @  27.5.2009,  14:26 Найти цитируемый пост)
А вот referenceData не должен. Может какие-то ошибки валидации и Спринг обратно показывает форму?

В методе processFormSubmission поставил брейкпоинт и обнаружил эксепшн! Там что-то не то с валидацией! Буду разбираться что. Хотя валидация явно пока еще не прикручена.



Цитата(Kangaroo @  27.5.2009,  14:26 Найти цитируемый пост)
Посмотри полный лог Спринга (левел DEBUG).

А где это можно глянуть?



Автор: garbuz 28.5.2009, 00:53
Как выяснилось, ошибка в том, что в объект в поле типа Set попадает строка. Есть на странице множественный select, как выбранные элементы списка превратить в набор и отдать его объекту?

Автор: garbuz 28.5.2009, 16:29
Мучаю дальше SimpleFormController (если разберусь, то можно и статейку в faq набросать smile )
Сейчас стоит задача забиндить коллекцию в объект. Нашел в инете примерчик
Цитата

This method allows you to register custom editors for certain fields of your command class. For instance, you will be able to transform Date objects into a String pattern and back, in order to allow your JavaBeans to have Date properties and still be able to set and display them in an HTML interface. 


Код

 @Override
    protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
        binder.registerCustomEditor(Set.class, "categories", new CustomCollectionEditor(Set.class){
            protected Object convertElement(Object element){
                if (element != null){
                    Long categoryId = Long.parseLong(element.toString());
                    Category category = daoFacade.getCategoryDAO().getById(categoryId);
                    return category;                   
                }
                return null;
            }
        });
    }

Правда не совсем понятно, как это все работает. 
Итак, как происходит процесс у меня, судя по дебаггеру.
1) Вызывается formBackingObject(). Кстати, что за свойство sessionForm(true/false). Я чего-то так и не понял, за что отвечает? Поставил значение true и после сабмита метод formBackingObject() больше не стал вызываться, то что я и хотел.
2) Вызов initBinder, где сначала происходит только регистрация customEditor, как я понял
3) Список категорий получаю в методе referenceData()
4) Снова initBinder, но в этом методе уже происходит вызов метода converElement, в качестве объекта element приходит id категории, т.е. просто число. Вот тут непонятно? Откуда он взялся? Из тех данных что поулчены в referenceData? Или откуда еще? Причем приходит не один id, а итерируюстя id всей коллекции категории, да. То что нужно. Но если у меня всего 4 категории, то вызов converElement происходит 8 раз, по два раза для id каждой категории. Почему?
5) Отображается форма.
6) Заполняю, сабмичу.
7) Снова вызов initBinder, в качестве element приходит "" (об этом позже)
8) Появляется ексепшн
9) Вызов processFormSubmission()
10) Снова вызов referenceData
11)  Снова вызов initBinder как в пункте 4
12) И так по кругу....

Короче вот такая ерунда. Непонятно зачем метод initBinder вызывается столько раз? Мне кажется, что он должен вызываться один раз после сабмита, получать значение из формы и вытаскивать объект по этому значению. Короче каша в голове. Кто объяснит, буду признателен.

и еще один момент на счет jsp
Код

<form:select path="categories" multiple="multiple">
     <form:options items="${categories}" itemValue="categoryId" itemLabel="categoryName"/>
</form:select>

catefories - это коллекция, которую получаю в referenceData.
HTML на странице выглядит так 
Код

<td>Choose categories</td>

            <td>
                <select id="categories" name="categories" multiple="multiple">
                    
                        <option value="">Ajax</option>
                    
                        <option value="">Java</option>
                    
                        <option value="">Linux</option>
                    
                        <option value="">Новости</option>
                    
                    
                </select><input type="hidden" name="_categories" value="1"/>

            </td>


Значение атрибута value пустые, поэтому и в initBinder после сабмита приходит пустая строка. Почему пустые, пример select и options взял с офф референса. 

Буду рад любой помощи smile

Автор: Kangaroo 29.5.2009, 01:37
Ого, сколько проблем  smile  Сегодня не успеваю, завтра утром постараюсь помочь.

Автор: garbuz 29.5.2009, 02:42
Цитата(Kangaroo @  29.5.2009,  01:37 Найти цитируемый пост)
го, сколько проблем  smile  Сегодня не успеваю, завтра утром постараюсь помочь. 

Жду! А то мне даже не уснуть smile

Автор: Kangaroo 29.5.2009, 15:29
Цитата(garbuz @  27.5.2009,  15:22 Найти цитируемый пост)
Зачем еще раз вызов метода formBackingObject(), зачем нам второй объект?

Это связано с параметром sessionForm.
Зачем этот флаг нужен: если он установлен, то комманд обжект хранится в в сессии, а не в реквесте. Это нужно, например, для визард форм, одна форма на несколько страниц.
Вот для этого и в вызывается formBackingObject второй раз (при "ПОСТ") - чтобы или вытянуть из сессии уже существующий комманд обжект, или сделать новый.
Поставь его в false у себя.

Цитата(garbuz @  27.5.2009,  15:22 Найти цитируемый пост)
А где это можно глянуть?

Если у тебя log4j подключен, поставь левел DEBUG для пакета org.springframework

Цитата(garbuz @  28.5.2009,  00:53 Найти цитируемый пост)
Есть на странице множественный select, как выбранные элементы списка превратить в набор и отдать его объекту? 

Посмотри http://sirinsevinc.wordpress.com/tag/multi-select/, хотя у тебя вроде похоже сделано.


Цитата(garbuz @  28.5.2009,  16:29 Найти цитируемый пост)
Непонятно зачем метод initBinder вызывается столько раз? Мне кажется, что он должен вызываться один раз после сабмита, получать значение из формы и вытаскивать объект по этому значению.

Из-за ошибок бинда получается такой бардак, попробуй их исправить и посмотрим дальше.

По селекту - вроде все правильно. Ты точно айдишники передаешь? Геттеры/сеттеры правильные?


И еще.
1. Почему не возьмешь книгу по Спрингу, например "Spring Live". Там вроде это хорошо объясняется.
2. Почему не смотришь исходники Спринга? Для чего их открытыми держат? smile Там и увидишь всю логику обработки запроса.

Автор: garbuz 29.5.2009, 18:02
Kangaroo, во-первых, спасибо большое за ответы!  smile 

Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)
Это связано с параметром sessionForm.
Зачем этот флаг нужен: если он установлен, то комманд обжект хранится в в сессии, а не в реквесте. Это нужно, например, для визард форм, одна форма на несколько страниц.
Вот для этого и в вызывается formBackingObject второй раз (при "ПОСТ") - чтобы или вытянуть из сессии уже существующий комманд обжект, или сделать новый.
Поставь его в false у себя.

В принципе я все понял из твоего объяснения, но если я ставлю 
Код

setSessionForm(true);

то у меня formBackingObject вызывается один раз до сабмита формы, как я хотел. Т.е. как я понимаю, в этом методе, создаем объект, который будем заполнять данными из формы. Соответственно после сабмита нам надо получить его уже с данными. Если же поставить 
Код

setSessionForm(false);

то метод вызывается еще раз после сабмита, создается новый комманд обджект, а это мне не надо. Или я опять что-то неверно понял?


Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)
Если у тебя log4j подключен, поставь левел DEBUG для пакета org.springframework

Нет, пока без него. Стыдно товарищи, но я им еще вообще не пользовался.

Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)

Из-за ошибок бинда получается такой бардак, попробуй их исправить и посмотрим дальше.
По селекту - вроде все правильно. Ты точно айдишники передаешь? Геттеры/сеттеры правильные?

Исправил. Забил на спринговые теги, оставил только селект.
Код

<form:select path="categories" multiple="multiple">
       <c:forEach items="${cats}" var="cat">
               <option value="${cat.categoryId}"><c:out value="${cat.categoryName}"/></option>
       </c:forEach>
</form:select>

Так id выводится, все работает, биндер соответственно тоже отрабатывает нормально. С этим вроде разобрался smile
Еще вопрос по спринговым тегам. В примерах встречал <spring:bind> это как я понимаю в старых версиях спринга было, сейчас <form:. Верно? Или путаю? smile


Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)

И еще.
1. Почему не возьмешь книгу по Спрингу, например "Spring Live". Там вроде это хорошо объясняется.
2. Почему не смотришь исходники Спринга? Для чего их открытыми держат? smile Там и увидишь всю логику обработки запроса. 

Книгу нашел, буду глядеть если что. В исходникики тоже постараюсь заглянуть smile 

Следующие вопросы )
1)Хочу сделать отдельное поле для ввода тегов через запятую, набор тегов тоже представляет собой коллекцию, но в итоге это будет выглядеть не как массив, как в случае с множественным селектом, а как простая строка. Какой биндер выбрать, чтобы на основе этой строки выбралась/создалась коллекция тегов. 
2)Так же туда же хочу запихать чекбокс, там пользователь отмечает черновик это или же нет. Ну понятно что я имею ввиду. Тоже вопрос какой биндер выбрать, чтобы вытащить объетк типа Status и назначить его новому посту.

Спасибо!

Автор: Kangaroo 29.5.2009, 23:46
Цитата(garbuz @  29.5.2009,  18:02 Найти цитируемый пост)
то метод вызывается еще раз после сабмита, создается новый комманд обджект, а это мне не надо. Или я опять что-то неверно понял?

Он берется пустой и заполняется (биндится) значениями с формы. Если бы у тебя сессонФорм = тру, то брался бы не пустой, а полученный из сессии (может в нем уже есть данные с предыдущих форм-страниц).


Цитата(garbuz @  29.5.2009,  18:02 Найти цитируемый пост)
В примерах встречал <spring:bind> это как я понимаю в старых версиях спринга было, сейчас <form:. Верно? Или путаю? 

Я с этим не работал, но думаю что в <form: используется <spring:bind>. Он просто более нижнего уровня.

По вопросам:
1. Не знаю такого, напиши сам.

2. Зачем Status, если можно boolean и и стандартный биндер checkbox-boolean.

Автор: garbuz 31.5.2009, 03:13
Цитата(Kangaroo @  29.5.2009,  23:46 Найти цитируемый пост)

Он берется пустой и заполняется (биндится) значениями с формы. Если бы у тебя сессонФорм = тру, то брался бы не пустой, а полученный из сессии (может в нем уже есть данные с предыдущих форм-страниц).

Кажется начинаю понимать. Сам объект, который мне собственно и нужен, формируется два раза - до и после сабмита. Если я ставлю значение false, то мой объект будет тот, который уже создается после сабмита, и в который потом заносятся значения. А тот что был получен до сабмита, его схавает сборщик мусора.


Цитата(Kangaroo @  29.5.2009,  23:46 Найти цитируемый пост)
1. Не знаю такого, напиши сам.

вот. В принципе то, что я хотел (для реализации пришлось лезть в исходники спринга  smile )
Код

binder.registerCustomEditor(Set.class, "tags", new CustomCollectionEditor(Set.class) {
            public void setValue(Object value) {
                if (value != null) {
                    String str = value.toString();
                    String[] tags = str.trim().split(",");
                    Collection collection = Arrays.asList(tags);
                    super.setValue(collection);
                }
            }

            protected Object convertElement(Object element) {
                if (element != null) {
                    String tagName = element.toString();
                    Tag tag;
                    try {
                        tag = daoFacade.getTagDAO().getByName(tagName);
                        return tag;
                    } catch (IndexOutOfBoundsException e) {
                        tag = new Tag();
                        tag.setTagName(tagName);
                        tag.setPosts(new HashSet());
                        daoFacade.getTagDAO().create(tag);
                        return tag;
                    }
                }
                return null;
            }

        });


Вроде даже как работает smile



Цитата(Kangaroo @  29.5.2009,  23:46 Найти цитируемый пост)
2. Зачем Status, если можно boolean и и стандартный биндер checkbox-boolean. 

У поста планируется не два статуса, а больше. Так что обычный булеан тут не прокатит. Буду тоже извращаться и писать что-нибудь подходящее. 

Еще раз спасибо! smile

Автор: garbuz 1.6.2009, 15:30
Вот биндер для статуса, решил использовать радиобаттоны
Код

<td>Draft     : <form:radiobutton path="status" value="inactive"/> </td>
<td>Publish : <form:radiobutton path="status" value="active"/></td>

Сам кастом едитор
Код

binder.registerCustomEditor(Status.class, "status", new PropertyEditorSupport(){
            public void setAsText(String text){
                if (text instanceof String){
                    if (text.equals("inactive")){
                        Status status = daoFacade.getStatusDAO().getByName("inactive");
                        setValue(status);
                    } else if (text.equals("active")){
                        Status status = daoFacade.getStatusDAO().getByName("active");
                        setValue(status);
                    }

                }
            }

        });

Вопрос вот в чем. Когда Происход регистрация биндера, до показа формы, то сразу вызывается метод setTextAs, причем вызывается два раза с двумя значениями радиобаттнов, т.е. по идее из базы дергаются два объекта. После сабмита этот метод вызывается снова, но уже один раз с тем значением, которое мы выбрали на странице. Вытаскивается нужный объект типа Status. Почему спринг перед сабмитом дергает этод метод и вытаскивает "лишние объекты"?

Автор: garbuz 2.6.2009, 01:47
Начал прикручивать валидацию!  smile 
С текстовыми полями все ясно вроде бы, переопределяем метод getText:
Код

 public String getAsText() {
                Object value = getValue();
                if (value != null) {
                    Set<Tag> tags = (Set<Tag>) getValue();
                    StringBuilder builder = new StringBuilder();
                    for (Tag c : tags) {
                        if (c instanceof Tag) {
                            String tagName = c.getTagName();
                            System.out.println("tag name = " + tagName);
                            builder.append(tagName + ", ");
                        }
                    }
                    return builder.toString();
                }
                return null;
            }

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

PS. Вопрос по методу выше, почему метод вызывается еще несколько раз(а именно 4) до показы формы, потом после сабмита и после валидации. Неужели так и должно быть?

Автор: Старовъръ 2.6.2009, 12:01
1. Ммм.. а как валидация происходит? Не вижу тега <form:errors...>
2. <spring:bind> - это устаревшая вещь.
3. Почему бы не разбивать вопросы по разным темам? Название темы должно отражать ее суть и в одной теме желательно должен звучать один вопрос. Другим будет легче искать уже отвеченные темы, если их разбивать по отдельным вопросам.

Автор: garbuz 2.6.2009, 12:08
Цитата(Старовъръ @  2.6.2009,  12:01 Найти цитируемый пост)
1. Ммм.. а как валидация происходит? Не вижу тега <form:errors...>
2. <spring:bind> - это устаревшая вещь

<form:errors...> на странице
Код

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ include file="include.jsp" %>
<html>
<head><title>New Post</title></head>
<body>
<form:form method="POST" commandName="newPost" action="createPost.htm">
    <table style="width:800px">
        <tr>
            <td>Choose categories</td>
            <td>
                <form:select path="categories" multiple="multiple">
                    <c:forEach items="${cats}" var="cat">
                        <option value="${cat.categoryId}"><c:out value="${cat.categoryName}"/></option>
                    </c:forEach>
                </form:select>
            </td>
            <td><form:errors path="categories" cssStyle="color:red;"/></td>
        </tr>
        <tr>
            <td>Post Name</td>
            <td>
                <form:input path="postName" />
            </td>
            <td><form:errors path="postName" cssStyle="color:red"/></td>
        </tr>
        <tr>
            <td>Preview</td>
            <td>
                <form:textarea path="preview" rows="10" cols="40"></form:textarea>
            </td>
            <td><form:errors path="preview" cssStyle="color:red;"/></td>
        </tr>
        <tr>
            <td>Post Body</td>
            <td>
                <form:textarea path="postBody" rows="10" cols="40"></form:textarea>
            </td>
            <td><form:errors path="postBody" cssStyle="color:red;"/></td>
        </tr>
          <tr>
            <td>Tags</td>
            <td>
                <form:input path="tags"/>
            </td>
            <td><form:errors path="tags" cssStyle="color:red;"/></td>
        </tr>
         <tr>
             <td>Status</td>
             <td>Draft   : <form:radiobutton path="status" value="inactive"/><br>
                 Publish : <form:radiobutton path="status" value="active"/></td>
             <td><form:errors path="status" cssStyle="color:red;"/></td>
         </tr>
          <tr>
             <td></td>
             <td></td>
             <td><input type="submit" value="submit"></td>
         </tr>
    </table>

</form:form>
</body>
</html>


А про <spring:bind я так и думал, что это устаревшая версия. Вопросы остаются открытыми:
Цитата

А что делать с радиобаттонами и селектом? Как у них на странице засетить выбранные ранее значения?

PS. Вопрос по методу выше, почему метод вызывается еще несколько раз(а именно 4) до показы формы, потом после сабмита и после валидации. Неужели так и должно быть?

Автор: garbuz 2.6.2009, 13:27
Еще вопрос.
Есть контроллер, который просто отображает какой-либо пост. На странице внизу есть форма для комментарев. Т.е. сама форма отображается все тем же контроллером. Какого типа должен быть контроллер, который обрабатывает комментарии? 
1)Если он будет предком SimpeFormController, тогда получается, что сама форма отображается другим контроллером. Или это не важно? Можно обойтись только POST запросом и все. Но с другой стороны если прикручивать валидацию и будет ошибка, то надо как-то снова отобразить эту страницу с формой, но этим же занимается другой контроллер.
2) Сделать предком AbstractController и просто вытаскивать данные из запроса. Но тогда мы теряем валидацию и прочие прелести SimpleFormController'a. 

Как в этом случае лучше поступить?

Автор: garbuz 2.6.2009, 14:16
Найдено рещение для радиобаттонов. Надо тоже было переопределить метод getAsText, который должен возвращать строку, подобно той, которая прописана у радиобаттонв на странице в атрибуте value, тогда после сабмита и возврата на форму, этот радиобаттно становится выделенным smile 

Остался множественный селект :(

Автор: garbuz 3.6.2009, 00:35
Решил пока оставить select в покое.
Перефразирую свой вопрос выше более понятно.

Есть PostController, который в качестве параметра принимает id поста, выбирает пост с комментариями из базы и вызывает вьюху, где все эти данные отображаются. Внизу страницы есть текстовое поле для комментариев. Так вот, какого типа должен быть контроллер, который будет добавлять эти комментарии?
Я вижу два варианта.
1) Это будет класс, расширяющий SipmleFormController
2) Это будет класс, расширяющий AbstractController

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

Если это второй вариант, то тут как мне кажется все проще. Получаем из запроса текст как параметр, валидируем его собственными средтвами, снова отображаем эту страничку путем редиректа на PostController. Хотя если возникнет ошибка при валидации ее придется как-то передать сквозь редирект. 

Кто что посоветует? Может есть более красивые и правильные решения? Надеюсь, что понятно обрисовал всю ситуацию smile

Автор: Старовъръ 4.6.2009, 14:09
Нет, не понятно) Если на странице есть инпуты, используется SimpleFormController. Он же расширяет AbstractController, посему ты и так, и так будешь использовать AbstractController smile

Автор: garbuz 4.6.2009, 18:00
Цитата(Старовъръ @  4.6.2009,  14:09 Найти цитируемый пост)
Нет, не понятно) Если на странице есть инпуты, используется SimpleFormController. Он же расширяет AbstractController, посему ты и так, и так будешь использовать AbstractControlle

Мда, что-то я как-то затупил, почему сразу не использовать один контроллер для отображения поста и создания комментраиев. Сам пост и имеющиеся комментарии можно выбирать в методе referenceData как доп данные. А сам новый комментарий будет как commandObject.
Старовъръ, спасибо за наводку  smile 

Автор: Omut 4.6.2009, 22:48
Цитата(Старовъръ @ 2.6.2009,  12:01)

3. Почему бы не разбивать вопросы по разным темам? Название темы должно отражать ее суть и в одной теме желательно должен звучать один вопрос. Другим будет легче искать уже отвеченные темы, если их разбивать по отдельным вопросам.

Для других обучающихся лучше когда в одной теме. Суть темы предельно проста - обучение Spring'у. Так держать, гарбуз!

Автор: Kangaroo 4.6.2009, 23:31
Цитата(Omut @  4.6.2009,  22:48 Найти цитируемый пост)
Для других обучающихся лучше когда в одной теме. Суть темы предельно проста - обучение Spring'у. Так держать, гарбуз! 

Не-а.

В одной теме удобно - это когда туториал. Читаешь и разбираешься.
А вот разные проблемы/вопросы решать лучше в разных темах, чтобы другим легче было их найти и прочитать решение (а не искать свою проблему среди Н-страниц)


Вот как-то так.  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)