5

hi I'm new in JSF and have such problem. On my page I have list of news and each news has checkbox(We can make this checkboxes checked and than delete checked news). This works fine. But after delete I return back to my page and press F5 and then my app think that checkbox that was bellow the deleted one is checked and deletes it. for example I have this : enter image description here

press delete button and have this: enter image description here than press f5 and I see this: enter image description here

so My delete method look like this :

Map<Integer, Boolean> allCheckboxes = newsForm.getCheckboxes();
Set<Integer> checkboxes = newsForm.getCheckboxes().keySet();
Set<Integer> checkedCheckboxes = new HashSet<>();
for(Integer id : checkboxes){
    boolean value = allCheckboxes.get(id);
    if(value){
        checkedCheckboxes.add(id);
    }
}
if (checkedCheckboxes.size() != 0) {
    newsDao.deleteNewsById(checkedCheckboxes.toArray());
} else {
    Integer[] delete = { newsForm.getNews().getId() };
    newsDao.deleteNewsById(delete);
}
newsForm.setNewsList(newsDao.getNewsList());
return list() + REDIERCT;

at my page i use selectBooleanCheckbox:

<h:selectBooleanCheckbox id="checkbox" 
                            value="#{newsForm.checkboxes[news.id]}" />

so I don't undestand why when I press f5 my Map<Integer, Boolean> allCheckboxes has some elements with value true. also after each delete I recreate Map<Integer, Boolean> allCheckboxes

UPDATE news.xhtml

<h:form id="main-form" onsubmit="return getSelectedCheckBoxes()">
                <h:commandLink styleClass="news-link" action="#{controller.list}"
                    value="#{messages['body.news']}" />
                &gt;&gt;
                <h:outputText value="#{messages['body.news.titles.list']}" />
                <br />
                <ui:repeat id="repeat" var="news" value="#{newsForm.newsList}">
                    <div id="news-table">
                        <div id="news-list-title">
                            <h:outputText value="#{messages['body.news.title']}" />
                            <h:outputText style="margin-left:10px;" value="#{news.newsTitle}" />
                        </div>
                        <div id="news-list-date">
                            <h:outputText value="#{news.newsDate}">
                                <f:convertDateTime pattern="dd/MM/yyyy" />
                            </h:outputText>
                        </div>
                        <div id="news-list-brief">
                            <h:outputText value="#{news.brief}" />
                        </div>
                    </div>
                    <div id="links-style-area">
                        <h:commandLink action="#{controller.view(news.id)}"
                            value="#{messages['body.label.view']}" />
                        <h:commandLink action="#{controller.edit(news.id)}"
                            value="#{messages['body.label.edit']}" />
                        <h:selectBooleanCheckbox id="checkbox" 
                            value="#{newsForm.checkboxes[news.id]}" />
                    </div>
                </ui:repeat>
                <p id="button-style"> 
                    <h:commandButton styleClass="button" action="#{controller.delete}"
                        onclick="clicked = 'deleteList'"
                        value="#{messages['body.button.delete']}"
                        rendered="#{not(empty newsForm.newsList)}" />
                </p>

                <h:outputText id="checkbox-error"
                    value="#{messages['error.error.delete.list']}"
                    styleClass="errorMessage" />
            </h:form>

NewsForm bean:

@ManagedBean(name="newsForm")
@SessionScoped
public class NewsBean implements Serializable{
    private static final long serialVersionUID = 1L;
    private News news;
    private List<News> newsList;
    private Map<Integer, Boolean> checkboxes = new HashMap<>();

    public NewsBean(){}

    public News getNews() {
        return news;
    }
    public void setNews(News news) {
        this.news = news;
    }

    public List<News> getNewsList() {
        return newsList;
    }
    public void setNewsList(List<News> newsList) {
        this.newsList = newsList;
    }

    public Map<Integer, Boolean> getCheckboxes() {
        return checkboxes;
    }

    public void setCheckboxes(Map<Integer, Boolean> checkboxes) {
        this.checkboxes = checkboxes;
    }
}

controller bean:

@ManagedBean(name="controller")
@SessionScoped
public class ControllerBean implements Serializable{

    private static final long serialVersionUID = 1L;
    private static final String PREVIOUS_PAGE = "previousPage";
    private static final String LIST = "news";
    private static final String EDIT = "edit";
    private static final String VIEW = "view";
    private static final String REDIERCT = "?faces-redirect=true";

    @ManagedProperty(value="#{jpaDao}")
    private INewsDao newsDao;
    @ManagedProperty(value="#{newsForm}")
    private NewsBean newsForm;

    public ControllerBean() {
    }

    @PostConstruct
    public void init() {
        try {
            list();
        } catch (NewsManagerException e) {
        }
    }

    public String list() throws NewsManagerException {
        newsForm.setNews(new News());
        newsForm.setCheckboxes(new HashMap<Integer, Boolean>());
        newsForm.setNewsList(newsDao.getNewsList());
        setAttribute(PREVIOUS_PAGE, LIST);
        return LIST;
    }

    public String add() {
        News news = new News();
        news.setNewsDate(new Date());
        newsForm.setNews(news);
        return EDIT;
    }

    private void setAttribute(String name, String value) {
        Map<String, Object> sessionMap = FacesContext.getCurrentInstance()
                .getExternalContext().getSessionMap();
        sessionMap.put(name, value);
    }

    private String getAttribute(String name) {
        Map<String, Object> sessionMap = FacesContext.getCurrentInstance()
                .getExternalContext().getSessionMap();
        return (String) sessionMap.get(name);
    }

    public String save() throws NewsManagerException {
        int id = newsForm.getNews().getId();
        setAttribute(PREVIOUS_PAGE, VIEW);
        if (id == 0) {
            newsDao.createNews(newsForm.getNews());
            return VIEW;
        }
        newsDao.editNews(newsForm.getNews());
        return VIEW;
    }

    public String edit(int id) throws NewsManagerException {
        News news = newsDao.getNewsById(id);
        newsForm.setNews(news);
        return EDIT;
    }

    public String view(int id) throws NewsManagerException {
        setAttribute(PREVIOUS_PAGE, VIEW);
        newsForm.setNews(newsDao.getNewsById(id));
        return VIEW;
    }

    public String cancel() throws NewsManagerException {
        String page = getAttribute(PREVIOUS_PAGE);
        News news = newsForm.getNews();
        if (news.getId() != 0) {
            int id = news.getId();
            news = newsDao.getNewsById(id);
            newsForm.setNews(news);
        }
        return page;
    }

    public String delete() throws NewsManagerException, IOException {
        setAttribute(PREVIOUS_PAGE, LIST);
        Map<Integer, Boolean> allCheckboxes = newsForm.getCheckboxes();
        Set<Integer> checkboxes = newsForm.getCheckboxes().keySet();
        Set<Integer> checkedCheckboxes = new HashSet<>();
        for(Integer id : checkboxes){
            boolean value = allCheckboxes.get(id);
            if(value){
                checkedCheckboxes.add(id);
            }
        }
        if (checkedCheckboxes.size() != 0) {
            newsDao.deleteNewsById(checkedCheckboxes.toArray());
        } else {
            Integer[] delete = { newsForm.getNews().getId() };
            newsDao.deleteNewsById(delete);
        }
        newsForm.setNewsList(newsDao.getNewsList());
        return list() + REDIERCT;
    }

    public INewsDao getNewsDao() {
        return newsDao;
    }

    public void setNewsDao(INewsDao newsDao) {
        this.newsDao = newsDao;
    }

    public NewsBean getNewsForm() {
        return newsForm;
    }

    public void setNewsForm(NewsBean newsForm) {
        this.newsForm = newsForm;
    }

}
Aliaksei Bulhak
  • 6,078
  • 8
  • 45
  • 75
  • What scope is your bean? Did you check if the method is being called on the third request? (the one you press F5). I see that you have `return list() + REDIERCT;` in your delete method, did you check if this is really redirecting? (using a GET request) – Elias Dorneles Mar 11 '13 at 12:05
  • 1. Method is called at each request 2.Looks like redirect works fine. 3. At list() method I get all news from DB and recreate Map – Aliaksei Bulhak Mar 11 '13 at 12:10
  • The weird thing is, if the redirect was working fine the method should not be called when you pressed F5, cause it would simply trigger the last GET request (and GET requests don't trigger action methods). So, why is the method being called at each request? Is that your expected behavior? – Elias Dorneles Mar 11 '13 at 12:17
  • That's is my problem. I don't want it called each time. So I think this is redirect problem, but I can't understand where I make a mistacke – Aliaksei Bulhak Mar 11 '13 at 12:21
  • Right! So, what is the return value of `list()` and the value of the constant `REDIRECT` (that is mispelled, btw)? Check that, and we're probably going to be closer to the answer. – Elias Dorneles Mar 11 '13 at 12:29
  • `list()` return value news (the same as the name of page `news.xhtml`) and `REDIRECT` value `?faces-redirect=true` – Aliaksei Bulhak Mar 11 '13 at 12:32
  • Hmm, that looks good. So, can you check if the F5 request is really a GET request? You can check it in the Network tab of Chrome developer tools (open with Ctrl-Shift-J) or the developer console of Firefox (open with Ctrl-Shift-K). – Elias Dorneles Mar 11 '13 at 12:37
  • I use Chrome. I open Chrome console but I don't know how to use it. can you explain please. When I press button or f5 nothing changes in console(it is clear) – Aliaksei Bulhak Mar 11 '13 at 12:47
  • Ohh I think I found. when press button delete Post method work – Aliaksei Bulhak Mar 11 '13 at 12:51
  • Yeah! Then, if the REDIRECT is working properly, after the POST there should be another GET request redirecting you to news.xhtml. Is that happening? – Elias Dorneles Mar 11 '13 at 12:52
  • btw, when you do press F5, does Chrome asks you to confirm to send posting data? – Elias Dorneles Mar 11 '13 at 12:53
  • `btw, when you do press F5, does Chrome asks you to confirm to send posting data?` yes – Aliaksei Bulhak Mar 11 '13 at 12:54
  • [and this is my chrome network tab look like when I press delete button](http://postimage.org/image/m0ohd0uwx/) – Aliaksei Bulhak Mar 11 '13 at 12:56
  • Cool, a screenshot is always helpful! :) But now I don't understand case, you see, there is the GET request following the POST. When you press F5, Chrome was supposed to repeat the GET request, not the POST request. But if it is asking you to confirm to send the post data, it is actually repeating the last POST request (and hence calling the delete method again). That is weird, man... – Elias Dorneles Mar 11 '13 at 13:01
  • Could you confirm that interpretation, posting the screenshot after the F5? – Elias Dorneles Mar 11 '13 at 13:03
  • [look](http://postimage.org/image/my15cs97n/) – Aliaksei Bulhak Mar 11 '13 at 13:05
  • Yeah, it is really repeating the POST request. That is very strange. Ah, one other thing: have you tried not to call list() at the end of the method, and just returning "news.xhtml?faces-redirect=true" ? As it redirects, the bean would be recreated and you wouldn't need to worry about recreating the map before redirecting (unless your backing bean is session scoped, but it shouldn't). Also, could you post your XHTML code? – Elias Dorneles Mar 11 '13 at 13:12
  • I try this but result still the same – Aliaksei Bulhak Mar 11 '13 at 13:14
  • And one more I find that on IE my app work fine – Aliaksei Bulhak Mar 11 '13 at 13:14
  • @AlekseiBulgak I think the delete is a second part of your problem. My guess is, if you delete the lowest and then F5 you are going to get an out of bounds exception. – Lyrion Mar 11 '13 at 13:16
  • no. App console clear – Aliaksei Bulhak Mar 11 '13 at 13:19
  • @AlekseiBulgak Man, that's really weird. It's looking like a Chrome misbehavior. Have you tried Firefox? And also, could you post the full XHTML and bean code? – Elias Dorneles Mar 11 '13 at 13:20
  • I try Firefox also everything fine. a post xhtml code and bean code – Aliaksei Bulhak Mar 11 '13 at 13:23
  • I reinstall Chrome but nothing changes – Aliaksei Bulhak Mar 11 '13 at 13:28
  • hey, man! I've noticed you posted the code just now. uh, is there a reason for you bean to be `@SessionScoped`? Why don't you make it `@ViewScoped` instead? This works better with this pattern of GET after POST. – Elias Dorneles Mar 11 '13 at 17:41

1 Answers1

3

Your concrete problem is caused because the checkbox values are essentially request/view scoped, but your managed bean is session scoped and you didn't clear the checked checkbox values after deletion.

You need to put view scoped data in the view scope instead of in the session scope.

@ManagedBean
@ViewScoped

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • +1 ... and a good example for what happens if BalusC doesn't post for some days (reinstalling browsers, etc.) ;-). – Matt Handy Mar 13 '13 at 14:40
  • @BalusC I accept your answer but I have some questions. You say that I didn't clear the checked checkbox values after deletion. But I say in my post that after each delete operation I clear checkbox Map. So how I must clear them? – Aliaksei Bulhak Mar 14 '13 at 08:47