0

I've been following this guide http://www.thymeleaf.org/doc/articles/springmvcaccessdata.html to learn how to render data models into a Springboot application with Thymeleaf. I have a function that retrieves a list of objects from my Parse-Server and renders them as model attributes:

@RequestMapping(value = "/requests", method = RequestMethod.GET)
    public String findRequestsByCurrentUser(Model model) {

        ParseUser currentUser = ParseUser.getCurrentUser();
        log.info(String.valueOf(currentUser.getObjectId()));

        findRequestsByCurrentUser(model, currentUser);

        return "requests";
    }    

private void findRequestsByCurrentUser(Model model, ParseUser currentUser) {
            ParseQuery<ParseObject> requestsQuery = ParseQuery.getQuery("Request");
            requestsQuery.whereContains("author", currentUser.getObjectId());
            requestsQuery.findInBackground(new FindCallback<ParseObject>() {
                @Override
                public void done(List<ParseObject> requestList, ParseException e) {
                    if (e == null) {
                        model.addAttribute("requests", requestList);
                    }
                }
            });
        }

Here is a debug of the model that I send to my view:

enter image description here

For some reason I can render currentRole just fine. But I can't render any of the individual attributes from the requests part of the mode. Should I use request.data.requestStatus or request.requestStatus? Even ${request} alone won't render the whole object. I've tried a few different ways. None seem to work. Here is my HTML:

                <center>
                    <table class="table table-striped">
                        <tr>
                            <td><b>Requested By</b></td>
                            <td><b>Reason</b></td>
                            <td><b>Requested Date</b></td>
                            <td><b>Status</b></td>
                        </tr>
                        <tr th:each="request : ${requests}">
                            <div th:switch="${request.data.requestStatus}">
                                <div th:case="Approved">
                                    <td th:text="${request.author.objectId" class="initial-name">Employee Initials
                                    </td>
                                    <td th:text="${request.requestText}">Request Description</td>
                                    <td th:text="${request.dateRequested}">Request Date</td>
                                    <td th:switch="${request.requestStatus}">
                                    <span class="red" th:case="Pending"
                                          th:text="Pending">Status</span>
                                        <span class="green" th:case="Approved"
                                              th:text="Approved">Status</span>
                                        <span class="red" th:case="Rejected"
                                              th:text="Rejected">Status</span>
                                    </td>
                                </div>
                            </div>
                        </tr>
                    </table>
                </center>

In my HTML, I iterate over the request objects provided in requestList and then retrieve their attributes.

Is Thymeleaf sensitive in such a way that if I have one typo anywhere in my HTML none of the objects will render? What else could be going wrong? Do I need to cast my ParseObject to a Java Object? Do I need to pass an ArrayList as opposed to a List?

The problem seems to be in the rendering of the list itself. I removed all the attributes from the HTML and just provided static text for the list. It should have rendered 15 rows of static text but it simply doesn't render anything... I wonder what it could be.

Martin Erlic
  • 5,467
  • 22
  • 81
  • 153
  • 1
    I don't know anything about Parse, but the name findInBackground, and the fact that is takes a callback as argument, is a strong indication that populating the request is done... in the background, by another concurrent thread, and the page is thus rendered while the parse query is being executed, before the done() method is called, and thus before the request is stored in the model. The javadoc (http://parseplatform.org/Parse-SDK-Android/api/com/parse/ParseQuery.html#findInBackground(com.parse.FindCallback)) confirms it. – JB Nizet Apr 03 '17 at 18:46
  • @JBNizet Would you have any suggestions for how to store the Request ParseObject in the model before the page is rendered? – Martin Erlic Apr 03 '17 at 18:52
  • 1
    Use find(), not findInBackground(). – JB Nizet Apr 03 '17 at 18:54
  • That definitely moved the needle! Now it's only complaining that the variable name is not public: ``org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'requestText' cannot be found on object of type 'org.parse4j.ParseObject' - maybe not public?`` So the rest of the solution could just be on the HTML side of things, maybe... – Martin Erlic Apr 03 '17 at 19:06
  • I can definitely render all the objects now. I included some static text in the and it loops 15 times. Just need to figure out the syntax for displaying the ParseObject in Thymeleaf. Thanks a bunch! I'll probably post an answer when I have a full solution. – Martin Erlic Apr 03 '17 at 19:10

2 Answers2

1

I think you could access to your data without the need to create a pojo. You need to access it as a Hashmap

<td th:text="${request.data.get('requestText')}">Request Description</td>
cralfaro
  • 5,822
  • 3
  • 20
  • 30
  • Thanks! Worth a shot, but it's giving me the same error. Now it's complaining about the ``data`` property: ``org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'data' cannot be found on object of type 'org.parse4j.ParseObject' - maybe not public?`` – Martin Erlic Apr 04 '17 at 09:22
  • It works! Request Description – Martin Erlic Apr 04 '17 at 09:23
  • cool! maybe was the problem that the data attribute was not public or didnt have getData method – cralfaro Apr 04 '17 at 09:30
  • Yeah the object provided was not public! Parse is open-source fortunately so that can be overridden or at least an accessor method can be written into it. Cheers. – Martin Erlic Apr 04 '17 at 09:33
0

This seems to be the answer. It allows me to render my ParseObjects into Thymeleaf. As mentioned above, I needed to query with .find() because otherwise the DOM is rendered before the objects do and so they do not appear:

@RequestMapping(value = "/requests", method = RequestMethod.GET)
    public String findRequestsByCurrentUser(Model model) {

        ParseUsercurrentUser = ParseUser.getCurrentUser();
        log.info(String.valueOf(currentUser.getObjectId()));

        findRequestsByCurrentUser(model, currentUser);

        return "requests";
    }


    private void findRequestsByCurrentUser(Model model, ParseUsercurrentUser) {
        ParseQuery<ParseObject> requestsQuery = ParseQuery.getQuery(ParseConstantsUtil.CLASS_REQUEST);
        requestsQuery.whereContains(ParseConstantsUtil.REQUEST_AUTHOR, currentUser.getObjectId());
        try {
            List<ParseObject> requestsArrayList = requestsQuery.find();
            model.addAttribute("requests", requestsArrayList);
            log.info(String.valueOf(requestsArrayList));
            log.info(String.valueOf(requestsArrayList.size()));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

Warning: Unfortunately, since ParseObject on its own does not supply accessor methods for each of my custom fields, i.e. if for example, my ParseObject has a field called "requestText" (database column), I won't be able to render it in Thymeleaf using ${request.requestText}. Instead I have to make a local data model (POJO that extends ParseObject), create the accessor for it, i.e. request.getRequestText() returning the string requestText and then pass the POJO objects to my data model...

If I find a solution I will post in my next question: Cannot render Object with Thymeleaf: Property or field cannot be found on object of type 'org.parse4j.ParseObject' - maybe not public?

Community
  • 1
  • 1
Martin Erlic
  • 5,467
  • 22
  • 81
  • 153