Introduction to scalatags in Scala.js
Introduction
scalatags is a Scala library that allows generating dom parts or snippets in a functional style. In the same spirit as Elemento for GWT, scalatags
‘s goal is to interact with the dom using clean and understandable code while keep it to the minimum. In this post, we are going to go through some examples of usage of scalatags
based on the web component’s project.
Setup
To set up scalatags
in a Scala.js project, the following dependency need to be added to the build.sbt
:
libraryDependencies += "com.lihaoyi" %%% "scalatags" % "0.6.7"
or
libraryDependencies ++= Seq( "com.lihaoyi" %%% "scalatags" % "0.6.7" )
Working with the DOM
Thanks to the contribution of scalway, the web component’s example have been improved using scalatags
, so the creational statements are now cleaner and more concise.
With scalatags
val amountInput = input(*.tpe := "number", *.id := "amountInput").render val dateInput = input(*.tpe := "date", *.id := "dateInput").render val reasonInput = textarea(*.id := "reasonInput").render val amountLabel = label("Amount: ", *.`for` := "amountInput").render val dateLabel = label(*.`for` := "dateInput", "Date: ").render val reasonLabel = label(*.`for` := "reasonInput", "Reason: ").render val submitButton = button("Add", *.cls := "action-button", *.onclick := { () => val id = UUID.randomUUID().toString val expense = new Expense(id, getExpenseAmount(), getExpenseDate(), getExpenseReason()) var expenseAsJson = expense.asJson println(expenseAsJson) dom.window.localStorage.setItem(expense.id, expenseAsJson.toString()) dom.document.dispatchEvent(new wrappers.Event("addExpense")) } ).render
Without scalatags
val amountInput = dom.document.createElement("input").asInstanceOf[HTMLInputElement] amountInput.`type` = "number" amountInput.id = "amountInput" val dateInput = dom.document.createElement("input").asInstanceOf[HTMLInputElement] dateInput.`type` = "date" dateInput.id = "dateInput" val reasonInput = dom.document.createElement("textarea").asInstanceOf[HTMLTextAreaElement] reasonInput.id = "reasonInput" val amountLabel = dom.document.createElement("label").asInstanceOf[HTMLLabelElement] amountLabel.htmlFor = "amountInput" amountLabel.textContent = "Amount: " val dateLabel = dom.document.createElement("label").asInstanceOf[HTMLLabelElement] dateLabel.htmlFor = "dateInput" dateLabel.textContent = "Date: " val reasonLabel = dom.document.createElement("label").asInstanceOf[HTMLLabelElement] reasonLabel.htmlFor = "reasonInput" reasonLabel.textContent = "Reason: " val submitButton = dom.document.createElement("button").asInstanceOf[HTMLButtonElement] submitButton.textContent = "Add" submitButton.classList.add("action-button") submitButton.addEventListener("click", (event: Event) => { val id = UUID.randomUUID().toString val expense = new Expense(id, getExpenseAmount(), getExpenseDate(), getExpenseReason()) var expenseAsJson = expense.asJson println(expenseAsJson) dom.window.localStorage.setItem(expense.id, expenseAsJson.toString()) dom.document.dispatchEvent(new wrappers.Event("addExpense")) }) getContainer().appendChild(amountLabel) getContainer().appendChild(amountInput) getContainer().appendChild(dateLabel) getContainer().appendChild(dateInput) getContainer().appendChild(reasonLabel) getContainer().appendChild(reasonInput) getContainer().appendChild(submitButton)
We can see that scalatags
has helped in reducing boilerplate.
With scalatags
val data = (0 until dom.window.localStorage.length).map { i => val key = dom.window.localStorage.key(i) Option(dom.window.localStorage.getItem(key)) }.collect { case Some(e) => decode[Expense](e).toSeq.last } dataTable = table(*.cls := "data-table", thead(tr(th("id"), th("amount"), th("date"), th("reason"))), data.map { expense => tr( td(expense.id), td(expense.amount), td(expense.date), td(expense.reason) ) } ).render getContainer().appendChild(dataTable)
Without scalatags
dataTable = dom.document.createElement("table").asInstanceOf[HTMLTableElement] dataTable.classList.add("data-table") val tableHeader = dom.document.createElement("thead").asInstanceOf[HTMLElement] val idHeaderCell = dom.document.createElement("th").asInstanceOf[HTMLElement] idHeaderCell.textContent = "id" val amountHeaderCell = dom.document.createElement("th").asInstanceOf[HTMLElement] amountHeaderCell.textContent = "amount" val dateHeaderCell = dom.document.createElement("th").asInstanceOf[HTMLElement] dateHeaderCell.textContent = "date" val reasonHeaderCell = dom.document.createElement("th").asInstanceOf[HTMLElement] reasonHeaderCell.textContent = "reason" val tableHeaderRow = dom.document.createElement("tr").asInstanceOf[HTMLTableRowElement] tableHeaderRow.appendChild(idHeaderCell) tableHeaderRow.appendChild(amountHeaderCell) tableHeaderRow.appendChild(dateHeaderCell) tableHeaderRow.appendChild(reasonHeaderCell) tableHeader.appendChild(tableHeaderRow) dataTable.appendChild(tableHeader) for (i <- 0 until dom.window.localStorage.length) { val key = dom.window.localStorage.key(i) val expenseJsonOption = Option(dom.window.localStorage.getItem(key)) if (expenseJsonOption.isDefined) { val expense = decode[Expense](expenseJsonOption.get).toSeq.last val row = dom.document.createElement("tr").asInstanceOf[HTMLTableRowElement] val idCell = dom.document.createElement("td").asInstanceOf[HTMLTableDataCellElement] idCell.textContent = expense.id val amountCell = dom.document.createElement("td").asInstanceOf[HTMLTableDataCellElement] amountCell.textContent = expense.amount val dateCell = dom.document.createElement("td").asInstanceOf[HTMLTableDataCellElement] dateCell.textContent = expense.date val reasonCell = dom.document.createElement("td").asInstanceOf[HTMLTableDataCellElement] reasonCell.textContent = expense.reason row.appendChild(idCell) row.appendChild(amountCell) row.appendChild(dateCell) row.appendChild(reasonCell) dataTable.appendChild(row) } } getContainer().appendChild(dataTable)
Coupled with Scala streams, the creation of the expenses table is cleaner and meaningful.
More examples can be found in this pull request.
Conclusion
scalatags
can become an indispensable tool for creating and interacting with dom elements in Scala.js especially for large scale applications. scalatags
can help make the codebase more maintainable by reducing the boilerplate code and improving readability.
Published on Web Code Geeks with permission by Zakaria Amine, partner at our WCG program. See the original article here: Introduction to scalatags in Scala.js Opinions expressed by Web Code Geeks contributors are their own. |