Web App With Kotlin.js: Getting Started

In this tutorial, you’ll learn how to create a web app using Kotlin.js. This will include manipulating the DOM and fetching data from a server, all in Kotlin! By Ahmed Tarek.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Changing the Loader Visibility

Update the showLoader() and hideLoader() methods in BookStorePage in the following way:

override fun showLoader() {
  loader.style.visibility = "visible"
}
override fun hideLoader() {
  loader.style.visibility = "hidden"
}

Again, you use the usual DOM APIs to change the visibility property of the elements to either "visible" or "hidden", as required.

The loader element is visible by default, so you should see it when you open the index.html page.

Loading

Test your code and hide the loader by adding the updating the main() function to the following:

fun main(args: Array<String>) {
  val bookStorePresenter = BookStorePresenter()
  val bookStorePage = BookStorePage(bookStorePresenter)
  bookStorePage.hideLoader()
}

You’ve updated the main function to directly call hideLoader() to hide the spinner that was visible before.

Build the project and refresh index.html in your browser. The loader should now be gone!

Building Book Elements

Next, you’ll build cards for each book to display, like this one:

Book Card

Create a new class and name it CardBuilder. In this class, you’ll build an HTMLElement to present the book, bind the books’ details to it, and apply CSS. Start by updating the class to the following:

class CardBuilder {

  fun build(book: Book): HTMLElement {
    // 1
    val containerElement = document.createElement("div") as HTMLDivElement
    val imageElement = document.createElement("img") as HTMLImageElement
    val titleElement = document.createElement("div") as HTMLDivElement
    val priceElement = document.createElement("div") as HTMLDivElement
    val descriptionElement = document.createElement("div") as HTMLDivElement
    val viewDetailsButtonElement = document.createElement("button") as HTMLButtonElement

    // 2
    bind(book = book,
        imageElement = imageElement,
        titleElement = titleElement,
        priceElement = priceElement,
        descriptionElement = descriptionElement,
        viewDetailsButtonElement = viewDetailsButtonElement)

    // 3
    applyStyle(containerElement,
        imageElement = imageElement,
        titleElement = titleElement,
        priceElement = priceElement,
        descriptionElement = descriptionElement,
        viewDetailsButtonElement = viewDetailsButtonElement)

    // 4
    containerElement
        .appendChild(
            imageElement,
            titleElement,
            descriptionElement,
            priceElement,
            viewDetailsButtonElement
        )
    // 5    
    return containerElement
  }

  // 6
  private fun Element.appendChild(vararg elements: Element) {
    elements.forEach {
      this.appendChild(it)
    }
  }
}

There’s a lot to do here, so let’s look at the steps one at a time:

  1. Create new elements by using the createElement() browser API, passing in the name of the HTML tag to create. For example, use "div" to create an HTMLDivElement and "img" to create an HTMLImageElement.
  2. Bind the book data to the HTML elements you created. You will implement this bind() method soon.
  3. Apply some CSS classes to the HTML elements you created. You will also implement the applyStyle() method below.
  4. Append all of the individual HTML elements to one container.
  5. Return the container, which is the root element of the card.
  6. Write an extension function that enables you to append a variable number of children to an element, instead of having to call the regular appendChild() method many times.

Binding the Data

To populate the elements with data, add the following method to the CardBuilder class.

private fun bind(book: Book,
                 imageElement: HTMLImageElement,
                 titleElement: HTMLDivElement,
                 priceElement: HTMLDivElement,
                 descriptionElement: HTMLDivElement,
                 viewDetailsButtonElement: HTMLButtonElement) {

  // 1
  imageElement.src = book.coverUrl 
  
  // 2
  titleElement.innerHTML = book.title
  priceElement.innerHTML = book.price
  descriptionElement.innerHTML = book.description
  viewDetailsButtonElement.innerHTML = "view details"
  
  // 3
  viewDetailsButtonElement.addEventListener("click", {
    window.open(book.url)
  })
}

In this method, you:

  1. Set the book cover image URL as the source of the image element on the card.
  2. Set the text content for the various text elements.
  3. Add a click event listener to the button element, which will navigate to the book’s URL if the button is clicked.

Applying CSS

The other method still missing is applyStyle(), which you should also add to the CardBuilder class.

private fun applyStyle(containerElement: HTMLDivElement,
                       imageElement: HTMLImageElement,
                       titleElement: HTMLDivElement,
                       priceElement: HTMLDivElement,
                       descriptionElement: HTMLDivElement,
                       viewDetailsButtonElement: HTMLButtonElement) {
  containerElement.addClass("card", "card-shadow")
  imageElement.addClass("cover-image")
  titleElement.addClass("text-title", "float-left")
  descriptionElement.addClass("text-description", "float-left")
  priceElement.addClass("text-price", "float-left")
  viewDetailsButtonElement.addClass("view-details", "ripple", "float-right")
}

This method adds the proper CSS classes that you need to style the book card with a material design style. You can find these classes already set up in the styles.css file. For example, the card-shadow CSS class gives a material shadow to the card container, and the float-left CSS class aligns the element to the left.

Creating Cards

Let’s go back to the BookStorePage class and start using this card creation code. First, add a property to the class, which will store an instance of CardBuilder.

private val cardBuilder = CardBuilder()

Then, go to the showBooks() method and add the following code:

books.forEach { book ->
  val card = cardBuilder.build(book)
  content.appendChild(card)
}

This code iterates through the list of books, and for each book, builds an HTML element representing it. Then, it adds the element to the content <div> we looked up from the DOM earlier.

Showing the Book Store Page

You’re almost done now. Add the following method to the BookStorePage class:

fun show() {
  presenter.attach(this)
  presenter.loadBooks()
}

This code sets the current BookStorePage instance as the presenter’s view so that it can receive callbacks from it, and then it asks the presenter to start loading the books.

Go to the main() function and update it to call this show() method on bookStorePage. The entire main() method should now look like this:

fun main(args: Array<String>) {
  val bookStorePresenter = BookStorePresenter()
  val bookStorePage = BookStorePage(bookStorePresenter)
  bookStorePage.show()
}

Build the project and refresh index.html.

You should see the loader briefly before the app finishes loading the books. Then the book cards will appear. The cards should have a shadow when you hover over them, and the View Details button should navigate you to the appropriate page for the book.

End Product

Hooray! You have created your first web app in Kotlin :]

Happy Face

Where to Go from Here?

You can download the files for the completed project (as well as the starter project) by clicking on the Download Materials button at the top or bottom of the tutorial.

We’ve covered the basics of using Kotlin.js to build web apps with Kotlin in the browser, but there’s a lot more to discover on this topic.

If you’re interested in setting up a more advanced project and development environment, you can learn about building Kotlin.js projects with Gradle, unit testing Kotlin code with JavaScript test frameworks, and even about debugging Kotlin in the browser.

Finally, you can take a look at how to call a JavaScript function from Kotlin as if it was a static method.

If you have any questions or comments, join in on the forum discussion below! We are happy to hear from you :]

Ahmed Tarek

Contributors

Ahmed Tarek

Author

Márton Braun

Tech Editor

Ryan Dube

Editor

Pankaj Kumar

Illustrator

Joe Howard

Final Pass Editor

Eric Soto

Team Lead

Over 300 content creators. Join our team.