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

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)
}

Related

How can I add data to a Kotlin object and get it on a Vuejs page

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

How to handle external undefined variable in typescript 2.0

I have been developing an application which need to handle an external variable which is defined in my index.html inside <script> tag and window scope. I need to access that in my typescript file to do some operation but during compile its showing error as shown below.
//index.html
<!DOCTYPE html>
<html>
<head>
<title>Angular QuickStart</title>
<script>
window.widgetResources = {
'sessionId': '2f60e0a2-3fa2-46f4-9a5c-4a8afe5007c8',
'staticResourceURL': 'http://localhost:9090/OfferFinder/16101/1/0/',
'offers': {
</script>
</head>
<body>
<app>loadings...</app>
</body>
</html>
//WidgetResourcesList.js
export class WidgetResourcesList {
//noinspection TypeScriptUnresolvedVariable
widgetResources = window.widgetResources;
}
//error getting
C:\quickstart>tsc
app/services/WidgetResourcesList.ts(5,28): error TS2339:
`Property 'widgetResources' does not exist on type 'Window'.`
Simple
declare global {
interface Window {
widgetResources: {
sessionId: string,
staticResourceUrl: string,
offers: {}
};
}
}
export class WidgetResourcesList {
widgetResources = window.widgetResources;
}

How to fix Invalid field or parameter url in SP.Executor.js in people picker Sharepoint Provided-Hosted App

#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<!-- IE9 or superior -->
<meta http-equiv="X-UA-Compatible" content="IE=9">
<title>People Picker HTML Markup</title>
<!-- Widgets Specific CSS File -->
<link rel="stylesheet"
type="text/css"
href="../Scripts/Office.Controls.css" />
<!-- Ajax, jQuery, and utils -->
<script src="~/Scripts/MicrosoftAjax.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
// Function to retrieve a query string value.
// For production purposes you may want to use
// a library to handle the query string.
function getQueryStringParameter(paramToRetrieve) {
var params =
document.URL.split("?")[1].split("&");
var strParams = "";
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == paramToRetrieve)
return singleParam[1];
}
}
</script>
<!-- Cross-Domain Library and Office controls runtime -->
<script type="text/javascript">
//Register namespace and variables used through the sample
Type.registerNamespace("Office.Samples.PeoplePickerBasic");
//Retrieve context tokens from the querystring
Office.Samples.PeoplePickerBasic.appWebUrl =
decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
Office.Samples.PeoplePickerBasic.hostWebUrl =
decodeURIComponent(getQueryStringParameter("SPHostUrl"));
//Pattern to dynamically load JSOM and and the cross-domain library
var scriptbase =
Office.Samples.PeoplePickerBasic.hostWebUrl + "/_layouts/15/";
//Get the cross-domain library
$.getScript(scriptbase + "SP.RequestExecutor.js",
//Get the Office controls runtime and
// continue to the createControl function
function () {
$.getScript("../Scripts/Office.Controls.js", createControl)
}
);
</script>
<!--People Picker -->
<script src="../Scripts/Office.Controls.PeoplePicker.js"
type="text/javascript">
</script>
</head>
<body>
Basic People Picker sample (HTML markup declaration):
<div id="PeoplePickerDiv"
data-office-control="Office.Controls.PeoplePicker">
</div>
<script type="text/javascript">
function createControl() {
//Initialize Controls Runtime
Office.Controls.Runtime.initialize({
sharePointHostUrl: Office.Samples.PeoplePickerBasic.hostWebUrl,
appWebUrl: Office.Samples.PeoplePickerBasic.appWebUrl
});
//Render the widget, this must be executed after the
//placeholder DOM is loaded
Office.Controls.Runtime.renderAll();
}
</script>
</body>
</html>
I want to create a people picker function in SharePoint Provider-Hosted app. I tried this tutorial: https://msdn.microsoft.com/en-us/library/office/dn636915.aspx
I'm stuck on this error.
Invalid field or parameter url in SP.Executor.js
Check whether your SP.RequestExecutor.js is loaded successfully or not if not then you can give the path of the same and load it directly or you can use the below code to get the SP.RequestExecutor.js
var scriptbase = hostweburl + "/_layouts/15/";
$.getScript(scriptbase + "SP.Runtime.js",
function () {
$.getScript(scriptbase + "SP.js",
function () { $.getScript(scriptbase + "SP.RequestExecutor.js", createControl); }
);
}
);
Hope this will resolve your issue.
This solved my error. Thanks to #Rahul for the hint
var scriptbase = hostWebUrl + "/_layouts/15/";
$.getScript(scriptbase + "SP.Runtime.js",
function () {
$.getScript(scriptbase + "SP.js",
function () { $.getScript(scriptbase + "SP.RequestExecutor.js",
$.getScript("../Scripts/Office.Controls.js", createControl));
}
);
}
);

How to Use ParsleyJS with Browserify

I'm trying to browserify a module containing both jquery and parsleyjs. So far I have this:
var $ = require('jquery');
require('parsleyjs');
If I load this alone, the following line in ParsleyJS throws an ReferenceError jQuery is not defined exception:
window.ParsleyConfig.i18n.en = jQuery.extend(window.ParsleyConfig.i18n.en || {}, {
I think I can use browserify-shim to put jQuery and ParsleyConfig in the global scope, but I could use some help with the details. Also I would prefer a solution that avoids polluting the global scope.
TIA,
- Ole
I will explain my own experience with this problem.
This is not the best way to manage this, I will leave here some clues:
Setup
npm init
npm install --save jquery
npm install --save parsleyjs
app.js
var $ = require('jquery');
window.jQuery = $;
require('parsleyjs/src/i18n/es.js')
require('parsleyjs');
window.Parsley.setLocale('es');
var userFormValidator = require('./validators/userFormValidator.js');
var hello = require('./functions/hello.js');
//global.window.hello = hello;
if (typeof global.window.define == 'function' && global.window.define.amd) {
global.window.define('hello', function () { return hello; });
global.window.define('userFormValidator', function () { return userFormValidator; });
} else {
global.window.hello = hello;
global.window.userFormValidator = userFormValidator;
}
functions/hello.js
module.exports = function () {
alert('hola');
}
validators/userFormValidator.js
module.exports = (function ($) {
var _settings = {};
var _rules = {};
var init = function(settings) {
_settings = {
form : $('#usersForm'),
};
_rules = {
fullname : {
minlength : 3,
maxlength : 80,
required : "true",
},
email: {
minlength : 3,
maxlength : 255,
type : "email",
required : "true",
},
};
_setup();
};
var _setup = function () {
_settings.form.parsley();
var validate = require('./validator.js');
validate(_rules);
};
return {
init: init,
};
})(window.jQuery);
validators/validator.js
var $ = require('jquery');
module.exports = function (rules) {
$.each( rules, function( field, constraints) {
$.each( constraints, function( constraint, value) {
$('[name="' + field +'"]').attr('data-parsley-' + constraint, value);
}.bind(field));
})
}.bind($);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form id="usersForm">
<label for="fullname">Full Name * :</label>
<input type="text" name="fullname" />
<label for="email">Email * :</label>
<input type="text" name="email" />
<input type="submit" />
</form>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript">
userFormValidator.init();
</script>
</body>
</html>
Turn out that the ParsleyJS project has a test browserify.js that shows how to use ParsleyJS with browserify. It does attach all the ParsleyJS objects to the window object, and requires that jQuery do so as well, but at least it works. If anyone has a solution that does this without attaching to the window, please share.
Here are the steps you need to follow to get it working:
mkdir parsleyjs-test
cd parsleyjs-test
npm init (Answer the questions)
npm install --save jquery
npm install --save parsleyjs
npm install --save-dev beefy
touch index.js
CONTENTS OF index.js
window.jQuery = $ = require('jquery');
require('parsleyjs');
TEST WITH BEEFY:
beefy index.js 8080
View results at:
http://localhost:8080
You should see both jQuery and Parsleyjs in the global window namespace. Use ctrl-shift-I to open the web developer tooling in firefox and click on console. Type window in the command line (Bottom of firefox console) and you will see window.jQuery and window.Parsley, etc.
Cheers,
Ole

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");
});