Home > Net >  How to execute a javascript function for every element in a list with Freemarker in Kotlin Ktor?
How to execute a javascript function for every element in a list with Freemarker in Kotlin Ktor?

Time:11-05

I'm calling an api with Kotlin and Ktor to get a book in return as json response. From this response I want to load the book cover in my frontend Freimarker .ftl file. I'm doing this call in javascript and write it to html. When I'm calling the function like in my code shown, only the first element in the list get the book cover. The rest is empty. The javascript is executed as inline code in html. I was excepting this function will be called for every new item in the list.

So what can I do to load the image for every element in my list?

index.ftl:

<#-- @ftlvariable name="books" type="kotlin.collections.List<com.nw.models.Book>" -->
<#import "_layout.ftl" as layout />
<@layout.header>
    <#list books?reverse as book>
<div >
    <hr>
        <div >
        <div  style="width: 18rem;">
            <img id="img-book"  src="https://via.placeholder.com/128x209.png">
            <div >
                <h5 >${book.title}</h5>
                <p >${book.author}</p>
                <input type="hidden" id="img-url" name="imageUrl" placeholder="${book.imageUrl}">
                <script type="text/javascript">
                    (function() {
                        let a1 = document.getElementById("img-url").getAttribute("placeholder");
                        const image = document.getElementById("img-book");
                        image.src = a1;
                    })();
                </script>
                <a href="/books/${book.id}" >Go to Book</a>
            </div>
        </div>
        </div>
</div>
    </#list>
    <hr>
    <p>
        <a href="/books/new">Create book</a>
    </p>
</@layout.header>

CodePudding user response:

With the current DOM scripting approach the OP has to assure that the html render process actually creates a unique id value for each book item, thus img-url is not a valid id value for each book item. The same btw applies to each image element's img-book id value. And the placeholder attribute is not to be abused as data storage; the correct way is the usage of a custom data-* attribute.

One very easily could even implement an approach which is entirely free of id attributes and hidden input fields by just using both data related features the custom data-* attribute and the element's related dataset property.

<#-- @ftlvariable name="books" type="kotlin.collections.List<com.nw.models.Book>" -->
<#import "_layout.ftl" as layout />
<@layout.header>

  <#list books?reverse as book>
    <div >

      <hr>
      <div >
        <div  style="width: 18rem;">

          <img data-book-cover-src="${book.imageUrl}"  src="https://via.placeholder.com/128x209.png">

          <div >
            <h5 >${book.title}</h5>
            <p >${book.author}</p>
            <a href="/books/${book.id}" >Go to Book</a>
          </div>

        </div>
      </div>

    </div>
  </#list>

  <script type="text/javascript">
    document
      .querySelectorAll('img[data-book-cover-src]')
      .forEach(elmNode =>

        elmNode.src = elmNode.dataset.bookCoverSrc
      );
  </script>

  <hr>
  <p>
    <a href="/books/new">Create book</a>
  </p>
</@layout.header>
  • Related