2

I'm trying to iterate through a list of objects and generate a div class="card-deck" every 4 objects and a nested div class="card" for every object.

This is the code that generates the exception on line 234

UPDATE: Note: line 234 is mentioned in html and had the <!-- Error-Line 234 --> due to a missing ) at ${#numbers.sequence(0,3}

    <div class="card-deck" th:each="qr: ${objects}" th:if="${qr.tableid}%4==0"> <!-- Iterate every 4 objects -->

    <!--syntax error missed clossing ) at ${#numbers.sequence(0,3) triggered the exception here -->
    <div class="card" th:each="i : ${#numbers.sequence(0,3)} ">   <!-- Error-Line 234 -->


        <!-- Some More Code -->
        <img th:src="${qr.qrcodestaticpath}" class="card-img-top" alt="...">
        <div class="card-body">
            <h5 class="card-title" align="center" th:text="'Table '+${qr.tableid}"></h5>
            <p class="card-text" align="center" th:text="'Random Generated QR Code'"></p>
            <h6 align="center" th:text=" ${qr.qrcodestring}"></h6>

        </div>
    </div>
  </div>

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/qrcodes.html]" - line 234, col 10)

I've already been on these topics

and gone through this documentation

and still can't figure a proper way of doing it , without triggering an exception

UPDATE: Exception is fixed, the logic i'm trying implementing doesn't have the desired outcome:

Outcome of the above snippet:

enter image description here

Imagine there are 8 tables, table1, table2 ... table8 , i'm trying to generate a <div class="card-deck" ... for every 4 or 5 tables. Due to <div class="card" th:each="i : ${#numbers.sequence(0,3)} "> I get the 4 same tables.

  • qr.tableid are the table numbers, 1 to x

For purposes of demonstration take a look at this java snippet

int numOfObjects=11;
    for(int i=0 ;i<numOfObjects;i++)
    {
        if(i%4==0)
        {
           System.out.println();
           System.out.print("Deck:");
        }
          System.out.print("Card"+(i+1)+" ");    
     }

Output:

enter image description here

This is my Controller

@GetMapping("/qrcodes")
      public String greetingForm(Model model) {

        List<QrObject> qr =qrRepo.findAll();
        int numOfobj= qr.size();
        int decks;

        if(numOfobj % 4==0)
            decks = numOfobj / 4 ;
        else
            decks = (numOfobj / 4) +1 ;

        int posa_periseuoun = numOfobj % 4 ;
        model.addAttribute("objects", qr);
        model.addAttribute("decks",decks);
        model.addAttribute("cards",posa_periseuoun);
        model.addAttribute("size", numOfobj);
        return "qrcodes";
      }
Phill Alexakis
  • 1,449
  • 1
  • 12
  • 31
  • In the question's sample HTML, there are 3 opening `
    ` tags but only two closing `
    ` tags. Is that representative of your actual template?
    – andrewJames Apr 08 '20 at 23:01
  • 1
    Also, this is missing a closing `)` parenthesis: `${#numbers.sequence(0,3}`. – andrewJames Apr 08 '20 at 23:05
  • 1
    Always post *the full* error message. You posted the wrapper exception, which will contain something more specific. – chrylis -cautiouslyoptimistic- Apr 08 '20 at 23:32
  • @andrewjames indeed the syntax error triggered the exception although the logic behind the iteration it's still wrong i think – Phill Alexakis Apr 09 '20 at 07:34
  • @chrylis-onstrike- the full error message is 100 lines + , i didn't want to overload the post with the exception since we know in which line it get's triggered – Phill Alexakis Apr 09 '20 at 07:39
  • No, "we" don't. This isn't the source message, which says "caused by:" and is probably a SpEL `ParseException`. – chrylis -cautiouslyoptimistic- Apr 09 '20 at 08:43
  • @chrylis-onstrike- alright, correct. i updated the post with the parse solution – Phill Alexakis Apr 09 '20 at 08:51
  • _the logic behind the iteration it's still wrong i think_ - can you clarify why you think that? I am not able to recreate any issue, using simple nested loops with your "if" condition. What error are you now getting? What incorrect behavior are you now seeing? – andrewJames Apr 09 '20 at 12:05
  • @andrewjames i'm not getting any errors, i can't produce correctly the `html` i need – Phill Alexakis Apr 10 '20 at 10:06
  • imagine i have 8 `objects` i'm trying to have 5 of them in the first `
    – Phill Alexakis Apr 10 '20 at 10:08
  • I've updated my question, can you understand what i'm trying to achieve? – Phill Alexakis Apr 10 '20 at 10:20
  • I think so... but perhaps not - so the following may be irrelevant. For this, I think you are better off trying to organize your objects using Java - which will make the Thymeleaf iteration straightforward. For example, if you have a `List`, and the `Deck` object in turn has a `List` member variable, then in Java you have much more control over the number of decks you need, and the number of cards in each deck. After that, there is no need for Thymeleaf to have any number sequences or modulo arithmetic. Just two iterators (one nested in the other) for decks and cards. – andrewJames Apr 10 '20 at 15:35
  • @andrewjames i completely understand this, let me update the question with the `ModelView` , i tried something like you said but i could't figure it out, because Cards and Decks are just `html` and my object is `QrObject` ... – Phill Alexakis Apr 10 '20 at 15:46

1 Answers1

2

Here is an approach which I think represents what you are trying to do.

End Result

Jumping to the end result, this will display the following text in a browser:

Deck: Card1 Card2 Card3 Card4
Deck: Card5 Card6 Card7 Card8
Deck: Card9 Card10 Card11 

More usefully, the HTML is as follows:

<div class="card-deck">
    <span>Deck: </span>
    <span class="card">Card1 </span>
    <span class="card">Card2 </span>
    <span class="card">Card3 </span>
    <span class="card">Card4 </span>
</div>
<div class="card-deck">
    <span>Deck: </span>
    <span class="card">Card5 </span>
    <span class="card">Card6 </span>
    <span class="card">Card7 </span>
    <span class="card">Card8 </span>
</div>
<div class="card-deck">
    <span>Deck: </span>
    <span class="card">Card9 </span>
    <span class="card">Card10 </span>
    <span class="card">Card11 </span>
 </div>

The Java Objects

The Deck:

public class Deck {

    private final String deckName;
    private final List<Card> cards = new ArrayList();

    public Deck(String deckName) {
        this.deckName = deckName;
    }

    public List<Card> getCards() {
        return cards;
    }

    public String getDeckName() {
        return deckName;
    }

}

The Card:

public class Card {

    private final String cardName;

    public Card(String cardName) {
        this.cardName = cardName;
    }

    public String getCardName() {
        return cardName;
    }

}

Assembling the Decks:

Map<String, Object> model = new HashMap();

// this is equivalent to your findAll()...
List<Card> allCards = new ArrayList();
for (int i = 1; i<= 11; i++) {
    allCards.add(new Card("Card" + i));
}

int maxCardsPerDeck = 4;        
List<Deck> decks = new ArrayList();

Deck deck;
List<Card> deckCards = new ArrayList();
int cardCount = 0;
for (Card card : allCards) {
    cardCount++;
    deckCards.add(card);
    if (cardCount % maxCardsPerDeck == 0 ||
            cardCount == allCards.size()) {
        deck = new Deck("Deck");
        deck.getCards().addAll(deckCards);
        decks.add(deck);
        deckCards.clear();
    }
}

model.put("decks", decks);

The above code is fairly crude - it could probably be refined. But the point is: it assembles a collection of decks, with each deck containing no more than the allowed maximum of cards (4 in this example).

The Thymeleaf

<div class="card-deck"
     th:each="deck: ${decks}">
    <span th:text="${deck.deckName + ': '}">
    </span>
    <span class="card"
          th:each="card: ${deck.cards}"
          th:text="${card.cardName + ' '}">
    </span>
</div>

I used <span>s here, just so things are aligned. You can use whatever you need, and provide the CSS styling you need also.

andrewJames
  • 19,570
  • 8
  • 19
  • 51
  • Your approach is correct, your `Card` is my `QrObject` , i tested it and works fine! Thank you so much! – Phill Alexakis Apr 11 '20 at 09:27
  • This is currently the best solution for the problem I can find. I do however believe that how my data is represented is the responsibility of the presentation layer and as such Thymeleaf should provide features that allow this in the template and not the business logic. – Alwyn Schoeman Jun 03 '20 at 13:43