How to invoke a class method in google app script from client side? - class

How to invoke a class method in google app script from client side ?
//client side
function myClientSideFun() {
google.script.run.withSuccessHandler(onSuccess).myClass.myClassMethod()
function onSucces(msg) { console.log(msg) }
}
//server side
class MyClass {
myClassMethod() { return "myMsg" }
}
let myClass = new MyClass()

Unless you export the class method in a different top level function, it is not possible to directly call class methods from the client. Classes are just syntactic sugars around existing objects. The documentation on Private functions clearly says that obj.objectMethod() isn't callable from the client.

As a simple example of using an object method.
HTML_Test
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input id="testParam" type="text">
<script>
(function () {
google.script.run.withSuccessHandler(
function(param) {
document.getElementById("testParam").value = param;
}
).getTestParam();
})();
</script>
</body>
</html>
Code.gs
class TestObject {
constructor(param) {
this.param = param;
}
get getParam() { return this.param; }
}
var obj = new TestObject("hello");
function getTestParam() {
return obj.getParam;
}
Options 2
To build on what #Bede noted there are many ways to use server side objects.
HTML_Test
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<input id="testParam" type="text">
<script>
(function () {
google.script.run.withSuccessHandler(
function(param) {
document.getElementById("testParam").value = param;
}
).getTestParam({name: "getParam2"});
})();
</script>
</body>
</html>
Code.gs
class TestObject {
constructor(param1,param2) {
this.param1 = param1;
this.param2 = param2;
}
getParam1() { return this.param1 }
getParam2() { return this.param2 }
}
var obj = new TestObject("hello","goodbye");
var getTestParam = param => { return obj[param.name](); }

Many thanks for the example and for the answer, based on that it seems I am figuring out another way how could I encapsulate functions on server side - actually with an object literal:
const appFunctionLibrary = {
appFunctions: {
"fun1": function (arg1) {
return arg1
},
"fun2": function (arg1, arg2) {
return [arg1, arg2]
},
"fun3": function () {
return "fun without param"
}
}
}
const main = requestedFunction =>
appFunctionLibrary.appFunctions[requestedFunction.name]
(requestedFunction.arguments)
Calling from client side /1 way: also create an "object" in client script and call ../:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button type="button" onclick="clientObject.clientFun.call(clientObject)">Test</button>
<script>
function ClientObj() {
this.clientFun = function(){
let arg1 = 'hello'
let arg2 = 'from client'
google.script.run.withSuccessHandler(function onSuccess(msg) {
console.log(msg)
}).main({name:'fun2', arguments: `${arg1}, ${arg2}`}) // .main({name:'fun3', arguments: ""}) .main({name:'fun1', arguments: `${arg1}`})
}
}
clientObject = new ClientObj()
</script>
</body>
</html>

Related

How to make a web component return a Proxy (and be extended)

The goal is to have a base class A extending HTMLElement that customizes getters and setters. Then class B would extend class A and do stuff.
The way to do this is by wrapping class A with a proxy (not the instance, but the class) so B can extend A.
I tried to return a Proxy in the constructor, but I get custom element constructors must call super() first and must not return a different object
<!DOCTYPE html>
<body>
<script>
window.onerror = function (error, url, line) {
document.getElementById('error').innerHTML = document.getElementById('error').innerHTML + '<br/>' + error;
};
</script>
<div id="error">Console errors here:<br /></div>
<my-b-element></my-b-element>
<script type="module">
class A extends HTMLElement {
constructor() {
super();
return new Proxy(this, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
console.log(`get ${name} = ${rv}`);
// do something with rv
return rv;
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent property '${name}', initial value: ${value}`);
}
return Reflect.set(target, name, value, receiver);
}
});
}
}
class B extends A {
constructor() {
super();
}
}
customElements.define("my-b-element", B);
document.querySelector('my-b-element').nonExistentProperty = 'value1';
</script>
</body>
</html>
In case it helps anyone, here's how it's done without any proxy.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
class Reactive extends HTMLElement {
#data = {};
connectedCallback() {
this.propertyObserver();
}
propertyObserver() {
const properties = Object.getOwnPropertyDescriptors(this);
// defines the new object properties including the getters and setters
for (let key in properties) {
const descriptor = properties[key];
this.#data[key] = descriptor.value;
descriptor.get = () => this.#data[key];
descriptor.set = (value) => {
const result = this.trap(key, value);
this.#data[key] = typeof result === 'undefined' ? value : result;
}
delete descriptor.value;
delete descriptor.writable;
}
Object.defineProperties(this, properties);
}
trap() {
// placeholder in case the child doesn't implement it
}
}
class Child extends Reactive {
a = 1;
b = 2;
constructor () {
super();
}
connectedCallback() {
super.connectedCallback();
}
trap(key, value) {
// You can return a value to override the value that is set
console.log(`LOG new ${key}: ${value} old: ${this[key]}`);
}
}
customElements.define("x-element", Child);
</script>
</head>
<body>
<x-element></x-element>
<script>
document.querySelector('x-element').a = 20;
</script>
</body>
</html>

why my addLoadEvent just the first js file executes?

function addLoadEvent (func) {
let oldonload = window.onload;
if (typeof window.onload !== 'function') {
window.onload = func;
} else {
window.onload = function () {
oldonload();
func();
}
}
this addLoadEvent is right
<script src="addLoadEvent.js"></script>
<script src="getAbbreviation.js"></script>
<script src="getCitation.js"></script>
it just the "getAbbreviation.js" works

SharePoint bulk update of multiple list items

I am currently working with binding SharePoint list items using JQuery datatables and rest API with the following code from Microsoft samples. I want to extend the sample to include multiple selection of row items with check boxes so that I can then another method to update the selected items. Please let me if this is possible with any guidance
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://cdn.datatables.net/1.10.15/js/jquery.dataTables.js"></script>
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.15/css/jquery.dataTables.min.css" />
<table id="requests" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>ID</th>
<th>Business unit</th>
<th>Category</th>
<th>Status</th>
<th>Due date</th>
<th>Assigned to</th>
</tr>
</thead>
</table>
<script>
// UMD
(function(factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], function ($) {
return factory( $, window, document );
});
}
else if (typeof exports === 'object') {
// CommonJS
module.exports = function (root, $) {
if (!root) {
root = window;
}
if (!$) {
$ = typeof window !== 'undefined' ?
require('jquery') :
require('jquery')( root );
}
return factory($, root, root.document);
};
}
else {
// Browser
factory(jQuery, window, document);
}
}
(function($, window, document) {
$.fn.dataTable.render.moment = function (from, to, locale) {
// Argument shifting
if (arguments.length === 1) {
locale = 'en';
to = from;
from = 'YYYY-MM-DD';
}
else if (arguments.length === 2) {
locale = 'en';
}
return function (d, type, row) {
var m = window.moment(d, from, locale, true);
// Order and type get a number value from Moment, everything else
// sees the rendered value
return m.format(type === 'sort' || type === 'type' ? 'x' : to);
};
};
}));
</script>
<script>
$(document).ready(function() {
$('#requests').DataTable({
'ajax': {
'url': "../_api/web/lists/getbytitle('IT Requests')/items?$select=ID,BusinessUnit,Category,Status,DueDate,AssignedTo/Title&$expand=AssignedTo/Title",
'headers': { 'Accept': 'application/json;odata=nometadata' },
'dataSrc': function(data) {
return data.value.map(function(item) {
return [
item.ID,
item.BusinessUnit,
item.Category,
item.Status,
new Date(item.DueDate),
item.AssignedTo.Title
];
});
}
},
columnDefs: [{
targets: 4,
render: $.fn.dataTable.render.moment('YYYY/MM/DD')
}]
});
});
</script>

Ionic Local storage put information into array

Here is the code
Controller.js
$scope.addFavorite = function (index) {
$scope.temp = {
id: index
};
$scope.dish = $localStorage.getObject('favorites', '{}');
console.log($localStorage.get('favorites'));
$localStorage.storeObject('favorites', JSON.stringify($scope.temp));
var favorites = $localStorage.getObject('favorites');
console.log(favorites);
favoriteFactory.addToFavorites(index);
$ionicListDelegate.closeOptionButtons();
}
Service.js
.factory('favoriteFactory', ['$resource', 'baseURL', function ($resource, baseURL) {
var favFac = {};
var favorites = [];
favFac.addToFavorites = function (index) {
for (var i = 0; i < favorites.length; i++) {
if (favorites[i].id == index)
return;
}
favorites.push({id: index});
};
favFac.deleteFromFavorites = function (index) {
for (var i = 0; i < favorites.length; i++) {
if (favorites[i].id == index) {
favorites.splice(i, 1);
}
}
}
favFac.getFavorites = function () {
return favorites;
};
return favFac;
}])
.factory('$localStorage', ['$window', function($window) {
return {
store: function(key, value) {
$window.localStorage[key] = value;
},
get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
storeObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value);
},
getObject: function(key,defaultValue) {
return JSON.parse($window.localStorage[key] || defaultValue);
}
}
}])
I want to make a Favorites function, and I want to put every item's ID that marked into an array.
But, it couldn't expand the array and only change the value.
Did I make something wrong on here? Or maybe I put a wrong method on here?
Thank you in advance!
I just create logic for storing object, you have to made logic for remove object from localstorage.
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS</title>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script>document.write('<base href="' + document.location + '" />');</script>
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
</head>
<body ng-controller="MainCtrl">
<div ng-repeat="item in items">
{{item.item_name}}
<button ng-click="addFavorite(item.id)">Add to Favorite</button>
<br><hr>
</div>
</body>
</html>
<script type="text/javascript">
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$http)
{
$scope.items = [
{id:1,item_name:'Apple'},
{id:2,item_name:'Banana'},
{id:3,item_name:'Grapes'},
]
$scope.addFavorite = function (index)
{
if(localStorage.getItem('favorites')!=undefined)
{
var old_favorite = JSON.parse(localStorage.getItem('favorites'));
var obj = {index:index};
old_favorite.push(obj);
localStorage.setItem('favorites',JSON.stringify(old_favorite));
}
else
{
var obj = [{index:index}];
localStorage.setItem('favorites',JSON.stringify(obj));
}
}
});
</script>

How to add annotations in video using video.js

is it possible to add annotation in my video using Video.js?
Below is my work out
<link href="http://vjs.zencdn.net/4.4/video-js.css" rel="stylesheet">
<script src="http://vjs.zencdn.net/4.4/video.js"></script>
<video id="my_video_1" class="video-js vjs-default-skin" controls
preload="auto" width="640" height="264" poster="my_video_poster.png"
data-setup="{}">
<source src="my_video.mp4" type='video/mp4'>
<source src="my_video.webm" type='video/webm'>
</video>
<script>
var Plugin = videojs.getPlugin('plugin');
var ExamplePlugin = videojs.extend(Plugin, {
constructor: function(player, options) {
Plugin.call(this, player, options);
player.on('timeupdate', function(){
var Component = videojs.getComponent('Component');
var TitleBar = videojs.extend(Component, {
constructor: function(player, options) {
Component.apply(this, arguments);
if (options.text)
{
this.updateTextContent(options.text);
}
},
createEl: function() {
return videojs.createEl('div', {
className: 'vjs-title-bar'
});
},
updateTextContent: function(text) {
if (typeof text !== 'string') {
text = 'hello world';
}
videojs.emptyEl(this.el());
videojs.appendContent(this.el(), text);
}
});
videojs.registerComponent('TitleBar', TitleBar);
var player = videojs('my-video');
player.addChild('TitleBar', {text: 'hellow people!'});
});
}
});
videojs.registerPlugin('examplePlugin', ExamplePlugin);
videojs('#my-video', {}, function(){
this.examplePlugin();
});
</script
</html>
//copy and paste this code it will surely work.