How can I add data to a Kotlin object and get it on a Vuejs page - vue.js

I'm absolutely new to Kotlin. I'm trying to make a simple object on the backend side by Kotlin and get it on frontend Vuejs. How can I do something like this (this is the raw code of HeaderBar.kt, all my attempts were denied by compiler):
object HeaderBar {
val computed = object {
fun items(): Array<Item> {
items.add(Item(
"NY",
"Bill"
))
return items
}
}
data class Item(
val city: String,
val name: String
)
}
on Kotlin side?
And get the items on HeaderBar.vue. I'm not sure, but I do this by:
<template>
<div class="main-header">
<div v-for="item in items" class="items">
<span class="city">{{item.city}}</span>
<span class="name">{{item.name}}</span>
</div>
</div>
<template>
<script>
export default path.to.HeaderBar
</script>

First off all it's not simple question. Kotlin/Js not so mature as Kotlin/Jvm so there are many not so simple tasks.
First you need to somehow compile to javascript and then you need to attach Vue to kotlin/javascript code.
Webpack can make it easer, so I write a simple example to show you how to write your example in Kotlin.
!Warning!: all code below is just draft (and has been writen only in demonstration purpose), so use it in your projects with special caution!
Lets create project with below structure:
Application.kt:
package vue_test
fun VueJs(init: VueContext.() -> Unit) = Vue(VueContext().apply(init))
class VueContext {
var el: String = ""
var data: dynamic = js("{}")
}
fun main(args: Array<String>) {
val app: dynamic = VueJs {
el = "#app"
data = mapOf("items" to listOf(
Item("NY", "Bill"),
Item("Test", "Test2")
)).toJs()
}
}
data class Item(
val city: String,
val name: String
)
fun Map<String, Any>.toJs(): dynamic {
val result: dynamic = object {}
for ((key, value) in this) {
when (value) {
is String -> result[key] = value
is List<*> -> result[key] = (value as List<Any>).toJs()
else -> throw RuntimeException("value has invalid type")
}
}
return result
}
fun List<Any>.toJs(): dynamic {
val result: dynamic = js("[]")
for (value in this) {
when (value) {
is String -> result.push(value)
is Item -> {
result.push(value.toJs())
}
else -> throw RuntimeException("value has invalid type")
}
}
return result
}
fun Item.toJs(): dynamic {
val result: dynamic = object {}
result["city"] = this.city
result["name"] = this.name
return result
}
I have write few function toJs which converts Kotlin object to Js object. It theory you may use JSON serialization to simplify this, or other more simple solution (if exists).
Vue.kt
#file:JsModule("vue")
package vue_test
#JsName("default")
external open class Vue(init: dynamic)
In this file we have only Vue declarations.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Test project</title>
</head>
<body class="testApp">
<h1>Kotlin-Js test</h1>
<div id="app">
<div class="main-header">
<div v-for="item in items" class="items">
<span class="city">{{item.city}}</span>
<span class="name">{{item.name}}</span>
</div>
</div>
</div>
<script type="text/javascript" language="JavaScript" src="frontend.bundle.js"></script>
</body>
</html>
Buldle has been created by webpack, and I has put this script to bottom because Vue needed to start his manipulations only then all necessary html tags has been already exists.
My build.gradle file with kotlin-frontend plugin and kotlin-js plugin:
buildscript {
ext.kotlin_version = '1.2.10'
repositories {
mavenCentral()
jcenter()
maven {
url "https://dl.bintray.com/kotlin/kotlin-eap"
}
maven {
url "https://repo.gradle.org/gradle/libs-releases-local"
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-frontend-plugin:0.0.21"
}
}
group 'test'
version '1.0-SNAPSHOT'
apply plugin: 'kotlin-platform-js'
apply plugin: 'org.jetbrains.kotlin.frontend'
repositories {
mavenCentral()
}
kotlinFrontend {
sourceMaps = true
npm {
dependency("vue")
}
webpackBundle {
port = 8080
bundleName = "frontend"
contentPath = file('src/main/web')
webpackConfigFile = project.projectDir.path + '/webpack.config.js'
}
}
compileKotlin2Js {
kotlinOptions.metaInfo = true
kotlinOptions.outputFile = "$project.buildDir.path/js/${project.name}.js"
kotlinOptions.sourceMap = true
kotlinOptions.moduleKind = 'commonjs'
kotlinOptions.main = "call"
}
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version"
}
settings.gradle
rootProject.name = 'test-kotlin-vue'
and last file, custom webpack configuration:
var config = require('./build/WebPackHelper.js')
var path = require('path')
module.exports = {
entry: config.moduleName,
output: {
path: path.resolve('./bundle'),
publicPath: '/build/',
filename: 'frontend.bundle.js'
},
module: {
rules: []
},
resolve: {
modules: [path.resolve('js'), path.resolve('..', 'src'), path.resolve('.'), path.resolve('node_modules')],
extensions: ['.js', '.css'],
alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
}
},
devtool: '#source-map'
};
console.log(module.exports.resolve.modules);
With kotlin-frontend plugin you could not use separate webpack config, but in this example Vue needed a full version to compile template, so it's needed to add alias in webpack. And I don't know how to do this in build.gradle.
Hope this will help you!
To start project with dev bundle run this command: gradle build webpack-run, and then open http://localhost:8080 in your browser
To stop test run command: gradle webpack-stop

Related

Receiving form params to Ktor server

I'm new to Java and Kotlin, trying to build a contact form with Ktor, so I enabled the insecure connection of my gmail from here, and built the app below:
blogApp.kt:
package blog
import org.jetbrains.ktor.netty.*
import org.jetbrains.ktor.routing.*
import org.jetbrains.ktor.application.*
import org.jetbrains.ktor.features.*
import org.jetbrains.ktor.host.*
import org.jetbrains.ktor.http.*
import org.jetbrains.ktor.response.*
import org.apache.commons.mail.*
fun Application.module() {
install(DefaultHeaders)
install(CallLogging)
install(Routing) {
get("/") {
call.respondText("""
My Example Blog2
<form action="/contact-us" method="post">
<input name="subject" placeholder="Subject">
<br>
<textarea name="message" placeholder="Your message ..."></textarea>
<br>
<button>Submit</button>
</form>
""", ContentType.Text.Html)
}
post("/contact-us") {
SimpleEmail().apply {
setHostName("smtp.gmail.com")
setSmtpPort(465)
setAuthenticator(DefaultAuthenticator("my_alias#gmail.com", "my_gmil_passoword"))
setSSLOnConnect(true)
setFrom("my_alias#gmail.com")
setSubject("subject") // I need to use formParam
setMsg("message") // I need to use formParam
addTo("my_alias#gmail.com")
}.send() // will throw email-exception if something is wrong
call.respondRedirect("/contact-us/success")
}
get("/contact-us/success") {
call.respondText("Your message was sent", ContentType.Text.Html)
}
}
}
fun main(args: Array<String>) {
embeddedServer(Netty, 8080, watchPaths = listOf("BlogAppKt"), module = Application::module).start()
}
build.gradle:
group 'Example'
version 'alpha'
buildscript {
ext.kotlin_version = '1.1.4-3'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
sourceCompatibility = 1.8
ext.ktor_version = '0.4.0'
repositories {
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/ktor" }
maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "org.jetbrains.ktor:ktor-core:$ktor_version"
compile "org.jetbrains.ktor:ktor-netty:$ktor_version"
compile "org.apache.commons:commons-email:1.4"
compile "org.slf4j:slf4j-simple:1.7.25"
compile "ch.qos.logback:logback-classic:1.2.1"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
kotlin {
experimental {
coroutines "enable"
}
}
jar {
baseName 'abc'
manifest {
attributes 'Main-Class': 'blog.BlogAppKt'
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
and things gone smoothly with it, I was able to send an email to myself then redirect to the success page, but the message got sent is with pre-set data:
setSubject("subject") // I need to use formParam
setMsg("message") // I need to use formParam
how can I make the Ktor receive the data the user really entered in the form, how can I read the form params?
You can use call.receive<ValuesMap>() and introspect the data:
import org.jetbrains.ktor.request.* // for recieve
import org.jetbrains.ktor.util.* // for ValuesMap
post("/contact-us") {
val post = call.receive<ValuesMap>()
val subj = post["subject"]
val msg = post["message"]
SimpleEmail().apply { ... }
}
NOTE: ValuesMap is deprecated in latest ktor version, so use the following code:
val post = call.receiveParameters()
Well although the above answer would have been correct at that point of time, today when I am going through kotlin + Ktor, I see that the above answer is not valid anymore.
What you need now is something like this:
call.receive< Parameters >()["PARAM_NAME"]
Parameters class is in the following package: io.ktor.http.Parameters
Nikhil

'x' is not a function when passing parameters in Kotlin Javascript

I keep getting this error: TypeError: Scraper.dumpTitle is not a function
And I can't figure out why...
Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kotlin JS Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<script src="out/production/lib/kotlin.js"></script>
<script src="out/production/Scraper.js"></script>
<!--<script>-->
<!--function loaded() {-->
<!--}-->
<!--</script>-->
<script>
$(function() {
Scraper.dumpTitle(document)
})
</script>
</body>
</html>
Main.js
import kotlin.browser.document
/**
* *
* * -
*/
fun main(args: Array<String>) {
println("Hello")
}
fun dumpTitle(doc: dynamic) {
println(doc.title)
}
fun dumpTitle1() {
println(document.title)
}
generated js
if (typeof kotlin === 'undefined') {
throw new Error("Error loading module 'Scraper'. Its dependency 'kotlin' was not found. Please, check whether 'kotlin' is loaded prior to 'Scraper'.");
}
var Scraper = function (_, Kotlin) {
'use strict';
var println = Kotlin.kotlin.io.println_s8jyv4$;
function main(args) {
println('Hello');
}
function dumpTitle(doc) {
println(doc.title);
}
function dumpTitle1() {
println(document.title);
}
_.main_kand9s$ = main;
_.dumpTitle_za3rmp$ = dumpTitle;
_.dumpTitle1 = dumpTitle1;
Kotlin.defineModule('Scraper', _);
main([]);
return _;
}(typeof Scraper === 'undefined' ? {} : Scraper, kotlin);
notes
calling dumpTitle1() works fine.. so the problem I have is only with passing parameters
no need to point out that I can access the document variable in Kotlin without needing to pass it, I know... but I wanted to pass another document object to use
If you're calling a Kotlin function from JavaScript, you need to use the #JsName annotation to give it a stable name. See here for documentation.
#JsName("dumpTitle")
fun dumpTitle(doc: dynamic) {
println(doc.title)
}

Extending PoS Module Odoo 8.x (formely OpenERP) ?

I need to make some customizations in the PoS module in Odoo 8.
For this i have created a module call "cus_pos". With this code i extended the interface:
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-extend="PosWidget">
<t t-jquery="div.pos-leftpane > div.window > div:last" t-operation="after">
<div class="payment-lines">
<div class="paymentline selected">
<!-- trigger an error <t t-esc="widget.get_list_salespersons()" /> -->
<div class="paymentline-name"> Salesperson: </div>
<select id="salesperson-select" class="paymentline-input">
</select>
</div>
</div>
</t>
</t>
</templates>
But when i try to extend the widget "PosWidget", to add a method to populate the select "salesperson-select", i get this error "Error: QWeb2 - template['PosWidget']: Runtime Error: TypeError: dict.widget.get_list_salespersons is not a function".
To extend the "PosWidget" i had tried this strategies:
One:
openerp.cus_pos = function(instance) {
template: 'PosWidget',
var module = instance.point_of_sale;
module.PosWidget = module.PosWidget.extend({
get_list_salespersons: function() {
console.log("Hurray!!!");
}
});
}
Two:
function openerp_pos_salesperson(instance, module) { //module is instance.point_of_sale
var module = instance.point_of_sale;
var QWeb = instance.web.qweb;
_t = instance.web._t;
module.SalePersonWidget = module.PosWidget.include({
template: 'PosWidget',
get_list_salespersons: function() {
console.log("Hurray!!!");
}
});
}
Three:
function openerp_pos_saleperson(instance, module) { //module is instance.point_of_sale
var module = instance.point_of_sale;
var QWeb = instance.web.qweb;
_t = instance.web._t;
module.SalePersonWidget = module.PosWidget.include({
template: 'PosWidget',
get_list_salespersons: function() {
console.log("Hurray!!!");
}
});
}
(function() {
var _super = window.openerp.point_of_sale;
window.openerp.point_of_sale = function(instance) {
_super(instance);
var module = instance.point_of_sale;
openerp_pos_vendedor(instance,module);
}
})();
Four:
openerp.cus_pos = function(instance) {
var module = instance.point_of_sale;
var _super_ = module.PosWidget.prototype.get_list_salespersons;
module.PosWidget.prototype.get_list_salespersons = function() {
console.log("Hurray!!!");
_super_.call(this);
};
};
Searching for some documentation i found http://thierry-godin.developpez.com/openerp/tutorial-module-creation-pos-modification-english-version/#LI but is outdated.
Any help on my question would be a great help. Many Thanks
Yes Thierry Godin wrote things about V7, but a lot of things are obsolete now in V8.
you should check new V8 modules in OCA on gitHub / OCA / POS
You can too take a look on the Odoo Forum.
After that, if you're still blocked I can check your problem.
There is 2 ways to overload existing Odoo POS :
https://github.com/OCA/pos/blob/8.0/pos_product_template/static/src/js/ppt.js#L74
https://github.com/OCA/pos/blob/8.0/pos_product_template/static/src/js/ppt.js#L109
(It depends of the kind of objects.)
BTW, what is the objective of your module ?
Kind regards.

TypeScript + Dojo + AMD?

Any examples of using AMD with TypeScript and dojo AMD? Keep getting "3" instead of an object (tslab == 3):
require( ["TypeScriptLab"], function ( tslab )
{
new tslab.Tests().run();
} );
The TypeScript looks like this:
export class TypeScriptLab {
test() {
}
}
The generated JS looks like this:
define(["require", "exports"], function(require, exports) {
var TypeScriptLab = (function () {
function TypeScriptLab() { }
TypeScriptLab.prototype.test = function () {
};
return TypeScriptLab;
})();
exports.TypeScriptLab = TypeScriptLab;
})
I defined my packages:
<script>
dojoConfig = {
async: true,
packages: [
{ name: "TSLab", location: "/IPS" }
]
};
</script>
And added a namespace prefix:
require( ["TSLab/typeScriptLab"], function ( tslab )
{
new tslab.Tests().run();
} );
And the module now loads.

Unable to print output to the console in dojo

I'm a beginner in dojo, and I'm trying to print the output to console using dojo code. But I don't what's the problem in the following code, and how can I print the output to the console?
<html>
<head>
<script type = "text/javascript" src = "dojo/dojo.js" data-dojo-config = "async: true, isDebug : true" >
</script>
</head>
<body>
<h1 id = "greeting">Hello</h1>
<script>
define(["dojo/dom"],function(dom) {
var Twitter = declare(null, {username:"defaultusername",
say :function(msg)
{
console.log("Hello "+msg);
}
});
var myInstance = new Twitter();
myInstance.say("Dojo");
});
</script>
</body>
</html>
Use require instead of define:
<script>
require(["dojo/dom", "dojo/_base/declare"], function(dom, declare) {
var Twitter = declare(null, {
username: "defaultusername",
say :function(msg) {
console.log("Hello "+msg);
}
});
var myInstance = new Twitter();
myInstance.say("Dojo");
});
</script>
Console works, but your code inside callback function in declare is not being executed until you require it.
You cannot define in inline script code, that is meant to be a class define, put in the topmost line of a class-file, meaning define maps the filename to the returned value of its function.
This means, if you have
dojo_toolkit /
dojo/
dijit/
dojox/
libs/
myWidgets/
foo.js
And foo.js reads
define(["dijit._Widget"], function(adijit) {
return declare("libs.myWidgets.foo", [adijit], function() {
say: function(msg) { console.log(msg); }
});
});
Then a new module is registered, called libs / myWidgets / foo. You should make sure that the returned declare's declaredClass inside each define matches the file hierachy.
That being said, reason why define does not work for you is the explaination above. It is inline and has no src to guess the declaredClass name from. Rewrite your code to define("aTwitterLogger", [":
define("aTwitterLogger", ["dojo/_base/declare", "dojo/dom"],function(declare, dom) {
var Twitter = declare(null, {
username:"defaultusername",
say :function(msg)
{
console.log("Hello "+msg);
}
});
var myInstance = new Twitter();
myInstance.say("Dojo");
});