Multi-platformový jazyk vyvíjaný JetBrains a Google.
Beží na JVM, plne spolupracuje s Javou, ale je schopný kompilovať aj do JavaScript-u a natívneho kódu.
Kotlin JVM, Kotlin Native, Kotlin JS
Oficiálny jazyk na vývoj mobilných aplikácií - Android.
Vytvoríme si nový Main.kt súbor:
fun main() { println("Hello world!") }
Po "zbuildovaní" projektu sa vytvoria súbory:
Tie sú referencované v našom index.html súbore
Do Main.kt vložíme:
val API_URL = js("getApiUrl()") as String
Vytvoríme si entitu Pokemon (data class):
data class Pokemon( val name: String, val dex: String, val types: Array<String>, val url: String, val imageUrl: String)
Architektúra aplikácie:
Vytvoríme si kontrakt medzi nimi:
interface PokemonContract { interface View { fun showPokemons(pokemons: List<Pokemon>) fun showLoader() fun hideLoader() } interface Presenter { fun attach(view: View) fun loadPokemons() } }
Implementujeme jednotlivé interfacy:
class PokemonPage(private val presenter: PokemonContract.Presenter) : PokemonContract.View { override fun showPokemons(pokemons: List<Pokemon>) { // TODO } override fun showLoader() { // TODO } override fun hideLoader() { // TODO } }
class PokemonPresenter : PokemonContract.Presenter { private lateinit var view: PokemonContract.View override fun attach(view: PokemonContract.View) { this.view = view } override fun loadPokemons() { // TODO } }
Získame dáta zo vzdialeného servera... Do triedy PokemonPresenter pridáme metódu:
private fun getAsync(url: String, callback: (String) -> Unit) { val xmlHttp = XMLHttpRequest() xmlHttp.open("GET", url) xmlHttp.onload = { if (xmlHttp.readyState == 4.toShort() && xmlHttp.status == 200.toShort()) { callback.invoke(xmlHttp.responseText) } } xmlHttp.send() }
V triede PokemonPresenter implementujeme metódu loadPokemons():
override fun loadPokemons() { view.showLoader() getAsync(API_URL) { response -> val pokemons = JSON.parse<Array<Pokemon>>(response) view.hideLoader() view.showPokemons(pokemons.toList()) pokemons.forEach { pokemon -> println(pokemon.types) } } }
Otestujeme, či sme dostali dáta zo servera... Opravíme Main.kt:
fun main() { val pokemonPresenter = PokemonPresenter() val pokemonPage = PokemonPage(pokemonPresenter) pokemonPresenter.attach(pokemonPage) pokemonPresenter.loadPokemons() }
V triede PokemonPage získame referencie na div elementy v index.html súbore:
private val loader = document.getElementById("loader") as HTMLDivElement private val content = document.getElementById("content") as HTMLDivElement
Zmeníme viditeľnosť loadera:
override fun showLoader() { loader.style.visibility = "visible" } override fun hideLoader() { loader.style.visibility = "hidden" }
Vytvárame pokémonové kartičky pomocou novej triedy CardBuilder:
class CardBuilder { fun build(pokemon: Pokemon): HTMLElement { val containerElement = document.createElement("div") as HTMLDivElement val imageElement = document.createElement("img") as HTMLImageElement val nameElement = document.createElement("div") as HTMLDivElement val dexElement = document.createElement("div") as HTMLDivElement val typesElement = document.createElement("ul") as HTMLUListElement val viewDetailsButtonElement = document.createElement("button") as HTMLButtonElement bind(pokemon = pokemon, imageElement = imageElement, nameElement = nameElement, dexElement = dexElement, typesElement = typesElement, viewDetailsButtonElement = viewDetailsButtonElement) applyStyle(containerElement, imageElement = imageElement, nameElement = nameElement, dexElement = dexElement, typesElement = typesElement, viewDetailsButtonElement = viewDetailsButtonElement) containerElement .appendChild( imageElement, nameElement, dexElement, typesElement, viewDetailsButtonElement ) return containerElement } private fun Element.appendChild(vararg elements: Element) { elements.forEach { this.appendChild(it) } } }
Implementujeme bindovanie HTML elementov k dátam pokemóna:
private fun bind(pokemon: Pokemon, imageElement: HTMLImageElement, nameElement: HTMLDivElement, dexElement: HTMLDivElement, viewDetailsButtonElement: HTMLButtonElement, typesElement: HTMLUListElement) { imageElement.src = pokemon.imageUrl nameElement.innerHTML = pokemon.name dexElement.innerHTML = pokemon.dex bindPokemonTypes(typesElement, pokemon.types) viewDetailsButtonElement.innerHTML = "view details" viewDetailsButtonElement.addEventListener("click", { window.open(pokemon.url) }) } private fun bindPokemonTypes(typesElement: HTMLUListElement, types: Array<String>) { types.forEach {type -> val typeElement = document.createElement("li") as HTMLLIElement typeElement.innerHTML = type typesElement.appendChild(typeElement) } }
Implementujeme metódu na nastavenie css štýlu jednotlivým HTML elementom:
private fun applyStyle(containerElement: HTMLDivElement, imageElement: HTMLImageElement, nameElement: HTMLDivElement, dexElement: HTMLDivElement, typesElement: HTMLUListElement, viewDetailsButtonElement: HTMLButtonElement) { containerElement.addClass("card", "card-shadow") imageElement.addClass("cover-image") nameElement.addClass("text-name", "float-left") dexElement.addClass("text-dex", "float-left") typesElement.addClass("float-left") typesElement.children.asList().forEach { element -> element.addClass("text-type", "float-left") } viewDetailsButtonElement.addClass("view-details", "ripple", "float-right") }
V triede PokemonPage pridáme field CardBuilder
class PokemonPage(private val presenter: PokemonContract.Presenter) : PokemonContract.View { private val cardBuilder: CardBuilder = CardBuilder() ... }
Následne implementujeme metódu showPokemons() + pridáme ešte jednu metódu:
override fun showPokemons(pokemons: List) { pokemons.forEach { pokemon -> val card = cardBuilder.build(pokemon) content.appendChild(card) } } fun show() { presenter.attach(this) presenter.loadPokemons() }
Upravíme Main.kt, zbuildujeme aplikáciu a obdivujeme náš výtvor :)
fun main() { val pokemonPresenter = PokemonPresenter() val pokemonPage = PokemonPage(pokemonPresenter) pokemonPage.show() }
Úlohy: