I have this problem. I was trying to build a spreasheet that, on open, would prompt a UI that allows the user to select between 3 custom buttons. A real scenerio would be this:
on open, it displays the message: "Hi! Which project do you wanna work on?" and, below this message, three buttons that say "Project 1", "Project 2" and "Project 3". Depending on the button pressed, It should modify the spreadsheet accordingly (for example, It would unify A1:A10 and write in it "Project 1" and so on).
How do I do this? Is this possible? Thanks:)
You can't do it to the Google Spreadsheet Ui because the button selections are fixed, OK, Cancel and such. But you can create a custom dialog.
In Code.gs I create an Installed onOpen
function myOnOpen(e) {
var html = HtmlService.createHtmlOutputFromFile("HTML_Simple");
SpreadsheetApp.getUi().showModalDialog(html,"Test");
}
Next I make a simple custom dialog with project buttons called HTML_Simple.html.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<p>Which project do you want to work on?</p>
<input id="project1" type="button" value="Project 1" onclick="clickProject(this)">
<input id="project2" type="button" value="Project 2" onclick="clickProject(this)">
<input id="project3" type="button" value="Project 3" onclick="clickProject(this)">
<script>
function clickProject(button) {
try {
alert("You picked "+button.id);
google.script.host.close();
}
catch(err) {
alert(err);
}
}
</script>
</body>
</html>
Now you can test the button id and branch to do whatever you want.
Reference
HTML Service
I would like to crawl a web page and extracts a video url embedded in the page. I first used Inspect tool and could easily see the embedded link as shown in the picture below:
And the target <video> tag:
<video class="jw-video jw-reset" tabindex="-1" disableremoteplayback="" webkit-playsinline="" playsinline="" preload="metadata" src="https://lh3.googleusercontent.com/YYxKbKt3Apa8A2LkHKBJ7Fx6GU_iCIjEeGyyPJm_Ll-9hO4K8fDZV1pAbYprwpRhS5yFanf7=m18?title=[CayPhim.Net]-Bay-Vien-Ngoc-Rong-Sieu-Cap-tap-6.[360p]"></video>
I then tried to View Page Source tool and searched for the link but I couldn't find it. Instead, I found some javascript code that seems to be used to get and add the link to the page dynamically (at page loading time):
<div id="switchserver" style="height:100%;">
<div id="phim_html5" style="height:100%;">
<div class="loading"></div>
</div>
<script>$(document).ready(function () {
$.ajax({
url: "http://player.cayphim.net/jwplayer7/index_googima.php",
type: "GET",
cache: false,
data: {
"url": "8ce46ffa35805780571877c8ae5808f6a5e8898ebf9d294326735716694ccb4279505da51df9678cc8601a390a422d5e639449ec90332ee518e06f1dd579606d106f292d49bb38d9b2e80d0ee965a5c0e2911922e48ac972c521c4236512d356681404472b2cb39d9fff915bb4da21c8315d3fd6fc6cb0d2ed27183598661d40",
"name": "QmF5IFZpZW4gTmdvYyBSb25nIFNpZXUgQ2FwIHRhcCA2",
"sub": ""
},
success: function (msg) {
$("#phim_html5").html(msg);
}, error: function () {
$("#phim_html5").html("<div class='player-error'>Server quá tải. Vui lòng chọn server khác bên dưới...</div>");
},
});
});
</script>
<img style="display: none" src="http://image.cayphim.net/1553256337-lSC0nSX6Wj9dlOXfK29gK2iwoKF9D0p4YwnxYgmyCwmyBfJ29eW1wKpPetD3BkBKF9D0p4MsZPPjEReTD0UVY0K4YvoGKF9D0p42wODHaQKFo5pGMVmG9XmN05lP80DksxCQaHXKF9D0p4MMJwwhnyohFxsEYKF9D0p4wRJH5xYk1eXEr9mETjpng" />
</div>
Now I used Advanced REST Client to make a GET request to http://player.cayphim.net/jwplayer7/index_googima.php with parameters as specified in the javascript code but I got response containing some thing like this:
<div id="playerjw7">Trình duyệt của bạn không hỗ trợ xem phim bằng Player HTML5. Vui lòng cài đặt Chrome hoặc Firefox</div>
Yeah, it's Vietnamese, but it means "Your browser doesn't support Video Player HTML5. Please install Chrome or Firefox".
How could I programmatically scrape and extract the embedded video url?
I have a Firefox WebExtension. It's duly signed by addons.mozilla.org and generally working correctly. But the user experience of installing it isn't very good.
I'm installing it from my own web site using the InstallTrigger.install() sequence documented here by Mozilla.
But this sequence seems to ignore the IconURL parameter. Instead my user gets a fairly scary popup box like this.
Is there any way to put my own icon on that box instead? Is there any way to simplify the installation sequence?
My code is pretty much cut and past from the mozilla docs. Here it is.
HTML button
<button id="extension-button-1" type="button"
class="btn btn-default btn-primary"
onclick="return EgetExtension(event);"
data-xpi="/install/browserextension/extension.xpi"
data-iconurl="/install/browserextension/icon32.png"
data-hash="sha256:aacf102b0cc6c9ffe370redacteddd1a8cac05cd4b2e9redactedd31394fb1a0"> Cobrowse </button>
Javascript
var params = {
"Extennsion": {
URL: event.target.getAttribute('data-xpi'),
IconURL: event.target.getAttribute('data-iconurl'),
Hash: event.target.getAttribute('data-hash'),
toString: function () { return this.URL; }
}
};
InstallTrigger.install(params);
manifest.json
{
"name": "Cobrowse",
"short_name": "C3PO",
"version": "3.7.1.17",
"manifest_version": 2,
"author": "Example.",
"description": "example. ",
"homepage_url": "http://ww2.glance.net/products/panorama/co-browse/",
"icons": {"128": "g128.png"},
"background": {
"scripts": ["background.js"],
"persistent": true },
"permissions": [
"https://*/*",
"http://*/*",
"tabs",
"storage"
],
... etc
}
What you desire is not possible. The Firefox source code (2) does not provide a way for you to specify this icon. It would be possible to change this if you had an extension or theme already installed which did so, but that is a bit counter to what you desire.
The icon is assigned by CSS based on the popupid. This is done in chrome://browser/skin/browser.css at line 3749 (on Firefox 53.0). The popupid is assigned based on the reason that the notification is being shown. In this case that is addon-install-blocked.
Follow up to related question
here
For whatever reason I'm unable to access my model in my xml view when it's set through sap.ui.getCore().setModel(). If I set it on the this.getView() I have no problems at all.
My view XML
<mvc:View controllerName="ca.toronto.rcsmls.webapp.controller.Login"
xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:l="sap.ui.layout">
<Page title="{i18n>loginPageTitle}">
<content>
<Panel id="loginPanel" busyIndicatorDelay="0"
headerText="{i18n>loginPanelTitle}" class="sapUiResponsiveMargin loginPanel"
width="auto">
<l:VerticalLayout width="100%">
<Input type="Text" placeholder="{i18n>loginUidHolder}" value="{/mlsUser/uid}" />
<Input type="Password" placeholder="{app>/Password}"
value="{/mlsUser/password}" />
<Button text="{i18n>loginButtonText}" press="doLogin"
class="sapUiSmallMarginEnd customBold" width="100%" />
</l:VerticalLayout>
</Panel>
</content>
</Page></mvc:View>
My controller JS contains this for setModel()
onInit : function() {
sap.ui.getCore().setModel(new sap.ui.model.json.JSONModel("webapp/controller/app.json"), "app");
}
Again, if I set the model to this.getView().setModel() instead of getCore() XML and controller work fine together. I also added data-sap-ui-xx-bindingSyntax="complex" to my index.html but that didn't seem to make a difference. Any help would be appreciated.
Edited to include more information
My Component.js
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/model/json/JSONModel",
], function (UIComponent, JSONModel) {
"use strict";
return UIComponent.extend("ca.toronto.rcsmls.webapp.Component", {
metadata : {
manifest: "json"
},
init : function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
// set data model
var oData = {
mlsUser : {
uid : "",
password : "",
}
};
var oModel = new JSONModel(oData);
this.setModel(oModel);
// create the views based on the url/hash
this.getRouter().initialize();
}
});
});
My manifest.json
{
"_version": "1.1.0",
"sap.app":
{
"_version": "1.1.0",
"id": "ca.toronto.rcsmls",
"type": "application",
"i18n": "i18n/i18n.properties",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion":
{
"version": "1.0.0"
},
"ach": "CA-UI5-DOC"
},
"sap.ui":
{
"_version": "1.1.0",
"technology": "UI5",
"deviceTypes":
{
"desktop": true,
"tablet": true,
"phone": true
},
"supportedThemes":
[
"sap_bluecrystal"
]
},
"sap.ui5":
{
"_version": "1.1.0",
"rootView": "ca.toronto.rcsmls.webapp.view.App",
"dependencies":
{
"minUI5Version": "1.30",
"libs":
{
"sap.m":
{
}
}
},
"config":
{
"authenticationService": "http://172.21.226.138:9080/RcsMlsSvc/jaxrs/user/authenticate/",
"assignedWorkService": "http://172.21.226.138:9080/RcsMlsSvc/jaxrs/mls/searchAssignedWork"
},
"models":
{
"i18n":
{
"type": "sap.ui.model.resource.ResourceModel",
"settings":
{
"bundleName": "ca.toronto.rcsmls.webapp.i18n.i18n"
}
}
},
"routing":
{
"config":
{
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "ca.toronto.rcsmls.webapp.view",
"controlId": "root",
"controlAggregation": "pages"
},
"routes":
[
{
"pattern": "",
"name": "login",
"target": "login"
},
{
"pattern": "work",
"name": "work",
"target": "work"
}
],
"targets": {
"login": {
"viewName": "Login"
},
"work": {
"viewName": "Work"
}
}
},
"resources":
{
"css":
[
{
"uri": "css/style.css"
}
]
}
}
}
Model app.json
{
"BaseURL": "https://smp-pNNNNNNtrial.hanatrial.ondemand.com",
"ES1Root": "https://sapes1.sapdevcenter.com",
"AppName": "qmacro.myfirst",
"Username": "yourusername",
"Password": "yourpassword"
}
I found an example where core binding works here. It is a much simpler application. I'm still trying to figure out the what the differences are between this project and mine
I was made aware of this question by a similar question on Github
By default, UI5 components don't inherit models and binding contexts from their environment (the ComponentContainer where they are placed). This was done for the sake of isolation.
You can change that default behavior by setting the property propagateModel:true for the container:
new sap.ui.core.ComponentContainer({
name: "ca.toronto.rcsmls.webapp.Component",
propagateModel: true
}).placeAt("content");
Documentation for propagateModel can be found in the API reference as well as in the UI5 developer guide (the corresponding paragraph in the latter page was only added recently, it was not available when you raised your question).
But when the model is created in the scope of the component and shall be used inside the component, then there should be no need to add it to the Core. Just assign it to the component to share it between views inside that component:
Assign it either from some method in a view controller
onInit : function() {
this.getOwnerComponent().setModel(
new sap.ui.model.json.JSONModel(
"webapp/controller/app.json"), "app");
}
or during init of the Component.js itself
init : function() {
// create and set model to make it available to all views
this.setModel(
new sap.ui.model.json.JSONModel(
"webapp/controller/app.json"), "app");
// never forget to call init of the base class
UIComponent.init.apply(this, arguments);
}
The most modern approach is to configure the model in the manifest.json and to let the framework instantiate an assign it. See Walkthrough Tutorial - Step 10: Descriptor for Applications for an example.
Using any of these approaches, there should not even be a need to read and set the model in another view, as long as that view is a descendant of the component (part of the control tree returned by createContent).
I experienced the same behavior.
If I create a simple one-file application, without any complex UI elements, the core-based binding works like a charm.
If I create a complex container, like an App with a shell, this kind of binding will not work anymore. It seems that these containers hide the global model from the view.
As a workaround I used the following code snippet:
this.getView().setModel(sap.ui.getCore().getModel(modelName), "modelName");
Or even you can bind the model directly to the control where you want to use.
None of them are the best solution if you have to use the global model in several view/control, but that's working for me.
I the view above only the placeholder of the password field is using your app model. If this placeholder is not filled correctly then I guess the json file could not be loaded or the content is not matching the binding path you use in your view for the placeholder property of the password field. To find out more please post also the content of your json file. It should look somehow like this:
{ "Password" : "Enter your password", ... }
So according to the binding in the view there must be "Password" property at the root level of the data object.
Below you find a running example that should help you. As you can see you it work like a charm, so you can easily put a named model onto the Core and reference it in your view.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SAPUI5 single file template | nabisoft</title>
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-libs="sap.m"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"></script>
<!-- use "sync" or change the code below if you have issues -->
<!-- XMLView -->
<script id="myXmlView" type="ui5/xmlview">
<mvc:View
controllerName="MyController"
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:l="sap.ui.layout"
xmlns:mvc="sap.ui.core.mvc">
<App>
<Page title="My Page Title">
<content>
<Panel id="loginPanel" busyIndicatorDelay="0"
headerText="My Login Panel Title" class="sapUiResponsiveMargin loginPanel"
width="auto">
<l:VerticalLayout width="100%">
<Input type="Text" placeholder="{Enter User ID}" value="{/mlsUser/uid}" />
<Input type="Password" placeholder="{app>/Password}" value="{/mlsUser/password}" />
<Button text="{Login}" press="doLogin" class="sapUiSmallMarginEnd customBold" width="100%" />
</l:VerticalLayout>
</Panel>
</content>
</Page>
</App>
</mvc:View>
</script>
<script>
sap.ui.getCore().attachInit(function () {
"use strict";
//### Controller ###
sap.ui.controller("MyController", {
onInit : function () {
var oData, oModel;
// 1. app model is only used for the placeholder field in the view
oData = {
Password : "Enter your password"
};
oModel = new sap.ui.model.json.JSONModel(oData);
sap.ui.getCore().setModel(oModel, "app");
// 2. default model is used in the view as well
oData = {
mlsUser : {},
Login : "Login now"
};
oModel = new sap.ui.model.json.JSONModel(oData);
sap.ui.getCore().setModel(oModel);
// 3. we need this because of the relative binding
// of the text property of the login button
this.getView().bindElement("/");
}
});
//### THE APP: place the XMLView somewhere into DOM ###
sap.ui.xmlview({
viewContent : jQuery("#myXmlView").html()
}).placeAt("content");
});
</script>
</head>
<body class="sapUiBody">
<div id="content"></div>
</body>
</html>
In this thread some people have mentioned that "sap.ui.getCore() works with small stuff, but for whatever reason not in more complex applications".
#Marc has also posted the right link to the API docs, where you can find the following:
A ManagedObject inherits models from the Core only when it is a
descendant of an UIArea
Of course, you have to know what this means in order write code that does what you expect. Here is a little example that tells you what could happen in case you have a small "misunderstanding" (see below).
The code below creates two instances of sap.m.Text and binds their text properties to a JSONModel, which is available as a named model "core" directly on the Core (retrieved with sap.ui.getCore()). There are 2 buttons, one for each sap.m.Text instance. In the corresponding press handlers of the buttons I just display the text property of the corresponding sap.m.Text instance. As you can see, both sap.m.Text instances are bound to the same property in the JSONModel. However, only the second sap.m.Text is added to the DOM.
Now the interesting part, which might be related to the confusion of this thread:
Only the text property of the second sap.m.Text control contains the expected text "Hello World" from the JSONModel. The text property of the first sap.m.Text control does not have the value "Hello World" from the model! This is expected behavior of SAPUI5 and it is documented! So I guess in case you face similar issues in your own "complex" app, then it might be quite probable that you have a hard to find bug related to this "expected" behavior.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SAPUI5 single file template | nabisoft</title>
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-libs="sap.m"
data-sap-ui-bindingSyntax="complex"
data-sap-ui-compatVersion="edge"
data-sap-ui-preload="async"></script>
<!-- use "sync" or change the code below if you have issues -->
<script>
sap.ui.getCore().attachInit(function () {
sap.ui.define([
"sap/m/Text",
"sap/m/Button",
"sap/ui/model/json/JSONModel"
], function (Text, Button, JSONModel) {
"use strict";
var oModel = new JSONModel({
hello : "Hello World"
});
sap.ui.getCore().setModel(oModel, "core");
// not in DOM
var oText1 = new Text({
text : "{core>/hello}"
});
//oText1.placeAt("text1"); // uncomment this to enable the binding
// add to DOM
var oText2 = new Text({
text : "{core>/hello}"
});
oText2.placeAt("text2");
// action buttons to display text property of text controls
new Button({
text : "show oText1",
press : function(){
alert("oText1.getText() = " + oText1.getText());
}
}).placeAt("btn1");
new Button({
text : "show oText2",
press : function(){
alert("oText2.getText() = " + oText2.getText());
}
}).placeAt("btn2");
});
});
</script>
</head>
<body class="sapUiBody">
<div id="text1"></div>
<div id="text2"></div>
<span id="btn1"></span>
<span id="btn2"></span>
</body>
</html>
sap.ui.getCore() and this.getView() return not the same object i think it's obvious why this doesn't work.
You are trying to get a object(model) from another object (the core) although the desired model is bound to another object(the view)
it's like i have two colored boxes (one red, one blue) and im trying to get the color red from the blue colored box
Here is an similar issue but the cause in this one depends on the id handling of the views in the UI5 framework:
https://scn.sap.com/thread/3551589
You can see the core and the view does not return the same object.
Also checkout the data binding section on the openui5 website: https://openui5beta.hana.ondemand.com/#docs/guide/e5310932a71f42daa41f3a6143efca9c.html
Create Model in components.js:
var oModel= new sap.ui.model.json.JSONModel;
oModel.loadData("webapp/controller/app.json");
this.setModel(oModel, "app");
Get Model:
This will create a reference to the model which has been created in the Components.js
var oModel= this.getView().getModel("app");
Refer to the model with "{modelName(app)>desiredProperty}"
<Input type="Password" placeholder="{app>Password}"
value="{app>mlsUser/password}" />
could you post your json content?
Hope this was helpful
I have a couple of questions related to dojo. Firstly I have an example I copied from online and ran it and it works perfectly. By the way I am working on a web application in visual studio 2010. It runs fine but my question is that I use the urls (version 1.5) from the ajax.googleapis.com and it works but as soon as I use the src="/folder/dojo.js.uncompressed.js" with the local copy (version 1.7.1) in my folder in my web app, it doesn't work. Any ideas about this.
The second question is using the datagrid example but instead of hard coding the values for the datagrid; I want to pass the results of a sql query from a database to the datagrid. Does anyone have ideas on this?
Here is code showing an attempt to use a locally stored file:
<title>The Simplest Dojo DataGrid Example of All Time</title>
<link rel="stylesheet" type="text/css"
href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/claro/claro.css" />
<link rel="stylesheet" type="text/css"
href="/Styles/Grid.css" />
<link rel="stylesheet" type="text/css"
href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojox/grid/
resources/claroGrid.css" />
</head>
<body class="claro">
<div style="width: 600px; height: 200px">
<table id="billsGrid" dojoType="dojox.grid.DataGrid">
<thead>
<tr>
<th field="number">Number</th>
<th field="name">Name</th>
<th field="position">Position</th>
<th field="victories" width="180px">Super Bowl Victories</th>
</tr>
</thead>
</table>
</div>
<script type="text/javascript"
src="/Styles/dojo.js.uncompresses.js"
djConfig="parseOnLoad:true"></script>
<script type="text/javascript">
dojo.require("dojox.grid.DataGrid");
dojo.require("dojo.data.ItemFileReadStore");
</script>
<script type="text/javascript">
dojo.ready(function () {
var theGreatestTeamOfAllTime = {
items: [{
"number": "12",
"name": "Jim Kelly",
"position": "QB",
"victories": "0"
},
{
"number": "34",
"name": "Thurman Thomas",
"position": "RB",
"victories": "0"
},
{
"number": "89",
"name": "Steve Tasker",
"position": "WR",
"victories": "0"
},
{
"number": "78",
"name": "Bruce Smith",
"position": "DE",
"victories": "0"
}
],
identifier: "number"
};
var dataStore =
new dojo.data.ItemFileReadStore(
{ data: theGreatestTeamOfAllTime }
);
var grid = dijit.byId("billsGrid");
grid.setStore(dataStore);
});
</script>
</body>
</html>
This does not work with jscript errors cropping up.
could not load dojox/grid/DataGrid.js
To make it work locally, examine your djConfig and the script includes for dojo to ensure paths are correct, especially the baseUrl
djConfig = {
parseOnLoad: true,
baseUrl: "../dojoroot/dojo/"
}
ALso make sure you are including the correct stylesheets (all includes and stylesheets must point to the same dojo version)
Examine the Net tab in firebug to see any errors in urls
regarding your second question, dojo has a concept of datastores - these are client (javascript) side holders of data. Your server-side can return the query result in JSON (preferred), XML or any other format.
The client (javascript/html) can use AJAX to fetch this data and render it in a datagrid.
dojo has fancy stores such as queryreadstore that supports paging/lazyloading
A good place to start is the nightly tests for dojo:
http://archive.dojotoolkit.org/nightly/dojotoolkit/dojox/grid/tests/
Here are the steps I follow:
1) download dojo toolkit from http://dojotoolkit.org/download/
2) install it with your application root. Typically, in my application root directory, I have a directory called dojoroot under which i untar dojo toolkit
3) in your html, head section make sure your css links are pointing to local dojoroot. FOr xample:
4) Make sure your djConfig is correctly set:
djConfig = {
parseOnLoad: true,
baseUrl: "../../../dojoroot/dojo/"
};
note that the baseUrl is important - it is the directory in which dojo.js is located
5) for the dojo.js src include, make sure the path is correct. for example:
If there are errors, your Net tab in firebug will show them - they are usually related to path issues