Google apps script: How to place function called by handler inside object? - event-handling

I create an object that returns textBox(), which could change their properties depending on the user's actions. However, once I put the function doChange(e) inside the object as a method like this:
function textBoxExt() {
...
this.doChange = function(e) {...}
...
}
my code returns an error: Cannot find a function doChange()...
I tried different ways to declare a function as a method in my object, but none of them worked. Please tell me, how do I modify the code below to move doChange(e) inside the object as a method. It is possible for functions that calls by handlers?
var style = {
modified: {color: 'black', backgroundColor: 'yellow', },
};
function textBoxExt() {
var app = UiApp.getActiveApplication();
var changeHandler = app.createServerHandler('doChange');
var box = app.createTextBox();
box.addValueChangeHandler(changeHandler);
this.init = function(id) {
return box.setId(id);
}
}
var doChange = function(e){
var app = UiApp.getActiveApplication();
app.getElementById(e.parameter.source).setStyleAttributes(style.modified);
return app;
}
function doGet() {
var app = UiApp.createApplication();
app.add(new textBoxExt().init('box1').setText('text1'));
return app;
}

You cant do this with gas. Handlers are passed as text. Besides objects only exist during the current handler so it cant possibly be attached to an object from a previous server call.

Related

Fragment is destroyed when nav back from Fiori Launchpad?

I use 2 XML fragment, one for display data, the other for edit.
I switch the fragment using this method:
onAfterRendering : function () {
this._toggleForm("Display");
},
_toggleForm : function(sFragmentName) {
var oPage = this._detailPage;
//my detail page has an object header, a fragment form and a form in detail view.
if(oPage.getContent().length > 2) {
oPage.removeContent(1);
}
oPage.insertContent(this._getFormFragment(sFragmentName), 1);
},
_formFragments: {},
_getFormFragment: function (sFragmentName) {
var oFormFragment = this._formFragments[sFragmentName],
oView = this.getView();
if (oFormFragment) {
return oFormFragment;
}
oFormFragment = sap.ui.xmlfragment(oView.getId(), "namespace.fragment." + sFragmentName, this);
oView.addDependent(oFormFragment);
return this._formFragments[sFragmentName] = oFormFragment;
}
Everything works fine... BUT, if I call the app from the Fiori launchpad, the first call is ok, but the second time give me this error in insertContent :
The object with ID XXX-detail--general was destroyed and cannot be used anymore.
Display/Change fragment is destroyed after exit, but this._fromFragment still stored a reference, and returned this reference oFormFragment when _getFormFragment is called when I entered the second time, which caused this error.
Fixed by add:
onExit : function () {
for(var sPropertyName in this._formFragments) {
if(!this._formFragments.hasOwnProperty(sPropertyName)) {
return;
}
this._formFragments[sPropertyName].destroy();
this._formFragments[sPropertyName] = null;
}
}
Answer #AndriiNaumovych 's question:
It seems that only sap.ui.comp.smartform.SmartForm has a EditTogglable property, and it need a sap:updatable="true" in metadata.xml (I saw that in Explore, not specified in doc.)
I use sap.ui.layout.form.SimpleForm, editable seems not working in JSON model without metadata. So I use this example with fragment.

How to create reusable components using page objects

I have a text box spinner control and some validations against it.
This text box spinner control is used in n number of pages where I need to check for same validations.
So I would like to create a page spinnertextbox.js and call this in other pages.
So my confusion is how to I access this spinnertextbox.js from test spec files.
Test spec - > Pages -> spinnertextbox.js
Do I call the spinnertextbox.js directly from Testspec (which I feel is wrong).
I tried the following to follow the flow of Test spec to Pages and from Pages to spinnertextbox.js
Below is what I have implemented.
Spinnertextbox.js`
var txtbox = function () {
this.Up = function (upArrow) {
upArrow.click();
};
this.Down = function (downArrow) {
downArrow.click();
};
};
module.exports = txtbox;
Homepage.js ā€“ which is going to call the spinnertextbox.js
var spinner = require('../pages/ spinnertextbox.js');
var home = function () {
var upArrow = element(by.xpath('ā€™));
var downArrow = element(by.xpath(''));
this.spinfn = function (fun) {
var spin = new spinner();
switch (fun) {
case 'uparrowclick':
spin.Up(upArrowt);
break;
case 'downarrowclick':
spin.Down(downArrow);
break;
}
};
};
module.exports = home;
And finally my test spec
Home.spec.js
var home = require('../pages/HomePage.js');
describe('reusability functionality : ', function () {
it('reusability: ', function () {
var hm = new home ();
//call to some other function in home page then
hm.spinfn('txtclick');
d hm isplaySrc.spinfn('uparrowclick');
hm.spinfn('downarrowclick');
});
});
Is this approach acceptable or Iā€™m totally in the wrong direction.

VideoJS 5 plugin add button

I looked everywhere on the internet but I couldn't find any clear documentation or some examples to create my verySimplePlugin for videoJS 5 (Since it uses ES6).
I just want to add a button next to the big play button... Can someone help me?
Thanks...
PS: I'm using it in angularJS but I guess this can not a problem
This is how you can add download button to the end of control bar without any plugins or other complicated code:
var vjsButtonComponent = videojs.getComponent('Button');
videojs.registerComponent('DownloadButton', videojs.extend(vjsButtonComponent, {
constructor: function () {
vjsButtonComponent.apply(this, arguments);
},
handleClick: function () {
document.location = '/path/to/your/video.mp4'; //< there are many variants here so it is up to you how to get video url
},
buildCSSClass: function () {
return 'vjs-control vjs-download-button';
},
createControlTextEl: function (button) {
return $(button).html($('<span class="glyphicon glyphicon-download-alt"></span>').attr('title', 'Download'));
}
}));
videojs(
'player-id',
{fluid: true},
function () {
this.getChild('controlBar').addChild('DownloadButton', {});
}
);
I used 'glyphicon glyphicon-download-alt' icon and a title for it so it fits to the player control bar styling.
How it works:
We registering a new component called 'DownloadButton' that extends built-in 'Button' component of video.js lib
In constructor we're calling constructor of the 'Button' component (it is quite complicated for me to understand it 100% but it is similar as calling parent::__construct() in php)
buildCSSClass - set button classes ('vjs-control' is must have!)
createControlTextEl - adds content to the button (in this case - an icon and title for it)
handleClick - does something when user presses this button
After player was initialized we're adding 'DownloadButton' to 'controlBar'
Note: there also should be a way to place your button anywhere within 'controlBar' but I haven't figured out how because download button is ok in the end of the control bar
This is how I created a simple button plugin for videojs 5:
(function() {
var vsComponent = videojs.getComponent('Button');
// Create the button
videojs.SampleButton = videojs.extend(vsComponent, {
constructor: function() {
vsComponent.call(this, videojs, null);
}
});
// Set the text for the button
videojs.SampleButton.prototype.buttonText = 'Mute Icon';
// These are the defaults for this class.
videojs.SampleButton.prototype.options_ = {};
// videojs.Button uses this function to build the class name.
videojs.SampleButton.prototype.buildCSSClass = function() {
// Add our className to the returned className
return 'vjs-mute-button ' + vsComponent.prototype.buildCSSClass.call(this);
};
// videojs.Button already sets up the onclick event handler, we just need to overwrite the function
videojs.SampleButton.prototype.handleClick = function( e ) {
// Add specific click actions here.
console.log('clicked');
};
videojs.SampleButton.prototype.createEl = function(type, properties, attributes) {
return videojs.createEl('button', {}, {class: 'vjs-mute-btn'});
};
var pluginFn = function(options) {
var SampleButton = new videojs.SampleButton(this, options);
this.addChild(SampleButton);
return SampleButton;
};
videojs.plugin('sampleButton', pluginFn);
})();
You can use it this way:
var properties = { "plugins": { "muteBtn": {} } }
var player = videojs('really-cool-video', properties , function() { //do something cool here });
Or this way:
player.sampleButton()

Google Apps upload from spreadsheet: cannot reference active cell

I have this google app script which should
show file upload dialog
store file in google drive
write the url of the file into the current cell
All goes well except step 3, where the cell updated is always cell A1 in the first sheet. But the cursor is on sheet #3 on another cell.
function onOpen(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var menuEntries = [];
menuEntries.push({name: "File...", functionName: "doGet"});
ss.addMenu("Attach ...", menuEntries);
}
function doGet(e) {
var app = UiApp.createApplication().setTitle("Attach file to sheet");
var form = app.createFormPanel().setId('frm').setEncoding('multipart/form-data');
var formContent = app.createVerticalPanel();
form.add(formContent);
formContent.add(app.createFileUpload().setName('thefile'));
formContent.add(app.createSubmitButton('Submit'));
app.add(form);
SpreadsheetApp.getActiveSpreadsheet().show(app);
return app;
}
function doPost(e) {
var fileBlob = e.parameter.thefile;
var doc = DocsList.getFolderById('0B0uw1JCogWHuc29FWFJMWmc3Z1k').createFile(fileBlob);
var app = UiApp.getActiveApplication();
var label = app.createLabel('file uploaded successfully');
var value = '=hyperlink("' + doc.getUrl() + '","' + doc.getName() + '")'
app.add(label);
app.close();
SpreadsheetApp.getActiveSheet().getActiveCell().setValue(value);
return app;
}
I tried SpreadsheetApp.getActiveSheet().getActiveCell().setValue(value); outside of the doPost() function and this works when called in a normal context. What am I missing here?
judging from this answer, it is not possible to get the current spreadsheet/cell from the doPost function, the way I got it working is to get it in the doGet function via hidden fields and pass it via the form. Full blown working example here.

How do I submit a form using a store under ExtJs?

Is there a way to have a form submit create an object in a store under ExtJs 4?
It seems strange to me that the grid is built completely around the store mechanism and I see no obvious way to plug a form into a store. But I am most likely just missing something.
You can add a model instance to a store upon form submit using this code:
onSaveClick: function()
{
var iForm = this.getFormPanel().getForm(),
iValues = iForm.getValues(),
iStore = this.getTasksStore();
iStore.add( iValues );
},
This is within an MVC controller, so this is the controller.
For model editing, you can 'bind' a form to a model instance using loadRecord:
iFormPanel.loadRecord( this.selection );
You can then update the model instance using updateRecord():
iFormPanel.getForm().updateRecord();
Just for fun (and as it might help some), it is similar to the following code:
onSaveClick: function()
{
var iForm = this.getFormPanel().getForm(),
iRecord = iForm.getRecord(),
iValues = iForm.getValues();
iRecord.set ( iValues );
},
If your store is has autoSync: true. An Update (or Create) call will be made via the configured proxy. If there's no autoSync, you'll have to sync your store manually.
You can subclass Ext.form.action.Action to provide load/save actions for a Form to be performed on a Store. The only gotcha is that somehow there's no "official" way to select any non-standard Action in Ext.form.Basic, so I'd suggest an unofficial override:
Ext.define('Ext.form.Advanced', {
override: 'Ext.form.Basic',
submit: function(options) {
var me = this,
action;
options = options || {};
action = options.submitAction || me.submitAction;
if ( action ) {
return me.doAction(action, options);
}
else {
return me.callParent(arguments);
}
},
load: function(options) {
var me = this,
action;
options = options || {};
action = options.loadAction || me.loadAction;
if ( action ) {
return me.doAction(action, options);
}
else {
return me.callParent(arguments);
}
}
});
And, having created the Actions you need, you could then use them in a Form Panel:
Ext.define('My.form.Panel', {
extend: 'Ext.form.Panel',
requires: [ 'Ext.form.Advanced' ],
loadAction: 'My.load.Action',
submitAction: 'My.submit.Action',
...
});
There are other ways and shortcuts though.