i am using Cross app navigation in my app I want to navigate directly to Object details - sapui5

i am using Cross app navigation in my app
I want to navigate directly to Object details
#Object-Display&/Details/000000910500453026
First time it navigates properly , but when i try to repeat the flow , it automatically navigates to root view
**#Object-Display
I just call the cross app navigator and call the app
oCrossAppNavigator.hrefForExternal({
target: {
semanticObject: "Object",
action: `Display&/Details/${sProductCode}`,
},
})) ||
"";
oCrossAppNavigator.toExternal({
target: {
shellHash: sHash,
},
});
Hi All, i am using Cross app navigation in my app
I want to navigate directly to Object details
**<URL>#Object-Display&/Details/000000910500453026**
First time it navigates properly , but when i try to repeat the flow , it automatically navigates to root view
****<URL>#Object-Display**
I just call the cross app navigator and call the app
oCrossAppNavigator.hrefForExternal({
target: {
semanticObject: "Object",
action: `Display&/Details/${sProductCode}`,
},
})) ||
"";
oCrossAppNavigator.toExternal({
target: {
shellHash: sHash,
},
});

I had the same issue once. Are you using the cross app navigation in combination with the Admin template in SAP Portal? If so... check if the navigation item is in a multi-level hierarchy. When you use any form of navigation to an application that is located in the lower levels of the menu hierarchy then the Shell.controller.js is automatically routing you back to the root.
To solve this, I used a SAP Portal plugin and hacked the Shell.controller.js.
Code snippet:
/**
* OVERRIDE! sap.ushell.services.cp.designtime.renderers.cp.admin.Shell -> _selectControlByHash
* Default SAP Portal behaviour is changed to support deep-link navigation.
* #param {navigation intent} h
*/
customSelectControlByHash(h) {
const a = this.arrRefControls;
const f = this.getView().fixedItemsList;
let b;
let l;
h = h.replace("#", "");
if (a) {
b = a[h] || a[h.split("?")[0]];
if (!b) {
const s = sap.ushell.Container.getService("SiteService").getSiteModel();
const p = s.getAllPagesAndApps();
const d = s.getPageByAlias(p, h);
if (d) {
b = a[d.getIntentAction()];
}
}
if (b) {
if (f) {
f.setSelectedItem();
}
l = b.getParent();
/** Replacing ORIGINAL CODE **/
// if (l && l.setSelectedItem) {
// l.setSelectedItem(b);
// } else if (l && l.setExpanded) {
// l.setExpanded(true);
// if (this.siteService.siteModel.isAdminSite() && !this.siteService.isDesignTime()) {
// const o = new sap.ui.core.CustomData({
// key: "refresh",
// value: true,
// });
// b.addCustomData(o);
// }
// if (b._selectItem) {
// b._selectItem();
// }
// }
/** End ORIGINAL CODE **/
/** Start CUSTOM CODE **/
// prevent using b._selectItem() as this is triggering a re-routing event.
if (l && l.setExpanded && !l.setSelectedItem) {
//Collapse all others
l.getNavigationList().getItems().forEach(e => e.getProperty("expanded") && e.setProperty("expanded", false));
// Expand target
l.setExpanded(true);
if (b._selectItem && this.siteService.isDesignTime()) {
b._selectItem(); // Keep behaviour only when in DesignTime mode (Administration site)
} else if (l && l.getNavigationList) {
l.getNavigationList().setSelectedItem(b); // Instead of using _selectItem(), use setSelectedItem() using the parent navigation list.
}
} else if (l && l.setSelectedItem) {
l.getItems().forEach(e => e.getProperty("expanded") && e.setProperty("expanded", false));
l.setSelectedItem(b);
}
/** End CUSTOM CODE **/
}
}
}
/* Hacking the SideNavigation via "byId()" and via DOM Manipulation below.
* It is impossibility to extend or redefine the SideNavigation in the adminTemplate.
* This plugin is too late.
*/
enhanceNavigation() {
try {
// Load the custom icons
//ToDo: Can we remove IconFontLoader here and move it to library.
new IconFontLoader().loadCustomIcons().then(function () {
if (this._applyCrossAppNavigationFix) {
// Inject custom logic to override: sap.ushell.services.cp.designtime.renderers.cp.admin.Shell -> _selectControlByHash
// This must fix the CrossApplicationNavigation logic when deep-linking.
const oMainShellController = sap.ui.getCore().byId("mainShell").getController();
if (oMainShellController && oMainShellController._selectControlByHash) {
oMainShellController._selectControlByHash = this.customSelectControlByHash.bind(oMainShellController);
}
}....

Related

Can an automated apps script email notification link back to specific sheet?

much to my surprise I've successfully made an apps scripts that sends me email notifications when a specific cell is changed to 'Submitted,' but I have no idea how to make this identify the sheet it came from - have linked a copy of the sheet below, there are going to be around 20 of these, each with 6 submission sheets, and I need to do a thing as soon as the sheet has been marked submitted, i.e. same day. I'd rather not hard code in separate messages for each sheet, can I do something around getting the URL and sheet with the get active sheet coding and insert it into the email message? I'm also aware currently I've hard coded in the sheet names and therefore need 6 different triggers, I'm working on that - tried loads of different coding pages and this is the only one that worked!
https://docs.google.com/spreadsheets/d/1b0LOr9vhmFu4WtYy_RbS-1cvXncNOI_x3YT0f30fZgY/edit#gid=1979912158
Cheers,
Meg
function emailSubmit() {
MailApp.sendEmail("Testemail", "Test", "Test message");
}
function onEdit(e) {
const specificSheet = "Sub1"
const specificCell = "C11"
let sheetCheck = (e.range.getSheet().getName() == specificSheet)
let cellCheck = (e.range.getA1Notation() == specificCell)
if (!(sheetCheck && cellCheck) || e.value !== "Submitted") {
return;
}
else {
emailSubmit()
}
}
function onEdit2(e) {
const specificSheet = "Sub2"
const specificCell = "C11"
let sheetCheck = (e.range.getSheet().getName() == specificSheet)
let cellCheck = (e.range.getA1Notation() == specificCell)
if (!(sheetCheck && cellCheck)) {
return
}
else {
emailSubmit()
}
}
To obtain the spreadsheet object bound to the fired onEdit trigger, use the event object source
Sample:
function emailSubmit(spreadsheet, sheet) {
console.log("spreadsheet: " + spreadsheet);
console.log("sheet: " + sheet);
MailApp.sendEmail("Testemail", "Test", "Spreadsheet " + spreadsheet + " and tab " + sheet + "have been submitted");
}
function onEdit(e) {
const allowedSheets = ["Sub1","Sub2"];
const specificCell = "C11";
const spreadsheetName = e.source.getName();
const sheetName = e.range.getSheet().getName();
let sheetCheck = (allowedSheets.indexOf(sheetName) != -1);
let cellCheck = (e.range.getA1Notation() == specificCell);
if (!(sheetCheck && cellCheck) || e.value !== "Submitted") {
return;
}
else {
emailSubmit(spreadsheetName, sheetName);
}
}
References:
Event Objects
getName()
indexOf()

Is it possible to handle swipe gestures in PWA application?

I'm creating a PWA, ASP server side and JS client side.
Users interact nicely with it using buttons.
The boss ask me if we can implement something like "scroll between app screen" or "perform some operation (edit, delete..) on elements" using the swipe gesture, as many native apps do.
Is there an easy way? or any way anyhow?
Thanks!
There are a couple of libraries that can help implement touch gestures for PWAs - have a look at Hammerjs:
https://hammerjs.github.io/
Your question is fairly generic though, there might be better solutions but that's just one off the top of my head
Have a drawer component in a PWA that can swipe to close using similar JS as below. Made an example that logs out the direction of a swipe / gesture here: https://jsfiddle.net/jamiesmith/e9gndqpc/3/
Below checks a function called ignoreSwipe so we can add a class of ignoreSwipe to some element(s) we specifically want to ignore gestures from.
Some standards below makes use of:
TouchEvent.touches - https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/touches
TouchEvent.touchstart & touchmove - https://developer.mozilla.org/en-US/docs/Web/API/Touch_events
let _xDown, _yDown;
document.querySelector('div.app')
.addEventListener(
'touchstart',
handleTouchStart,
false
);
document.querySelector('div.app')
.addEventListener(
'touchmove',
handleTouchMove,
false
);
function ignoreSwipe(event) {
// if some touches come from elements with ignoreswipe class > ignore
return Array.from(event.touches).some((t) =>
t.target.classList.contains('noswipe')
);
}
function handleTouchStart(event) {
if (ignoreSwipe(event)) {
_xDown = undefined;
_yDown = undefined;
return;
}
const firstTouch = event.touches[0];
_xDown = firstTouch.clientX;
_yDown = firstTouch.clientY;
}
function handleTouchMove(event) {
if (!_xDown || !_yDown) {
return;
}
const xUp = event.touches[0].clientX;
const yUp = event.touches[0].clientY;
const xDiff = _xDown - xUp;
const yDiff = _yDown - yUp;
if (Math.abs(xDiff) > Math.abs(yDiff)) {
/*most significant*/
if (xDiff > 0) {
/* left swipe */
console.log('app: left swipe ', true);
} else {
/* right swipe */
console.log('app: right swipe ', true);
}
} else {
if (yDiff > 0) {
/* up swipe */
console.log('app: up swipe ', true);
} else {
/* down swipe */
console.log('app: down swipe ', true);
}
}
/* reset values */
_xDown = null;
_yDown = null;
}

UI5: Validate Whole Form's Required and Visible Fields for Null/Blank Values

onPress of submit button, I want to validate all SimpleForms' fields (ComboBox, Input, DatePicker, etc.) that are
required &
visible
to see if they are null or blank (""). If a targeted (required & visible) field is null/blank, set that control's state to "Error" and display an error message. If no targeted field is null/blank, pop up a success dialog box.
This method is automated so in the future, any fields added later will automatically be checked without need of manual additions to controller code.
Controller code:
requiredAndVisible: function(oControl) {
if (typeof oControl.getRequired === "function") { //certain ctrls like toolbars dont have getRequired as a method, so we want to skim those out, else itll throw an error later in the next check
if (oControl.getRequired() === true && oControl.getVisible() === true) {
return oControl;
}
}
},
onSubmit: function() {
var valid = true,
oView = this.getView(),
aFormInitial = oView.byId("formInitial").getContent(), // get all the controls of SimpleForm1
aFormConfig = oView.byId("formConfiguration").getContent(), // get all controls of SimpleForm2
aControls = aFormInitial.concat(aFormConfig), // combine the 2arrays together into 1
aFilteredControls = aControls.filter(this.requiredAndVisible); // check each element if it required & visible using the 1st function. return only the controls that are both req'd & visible
aFilteredControls.forEach(function(oControl) { // in resultant array, check each element if...
if (!oControl.getValue() || oControl.getValue().length < 1) { // its value is null or blank
oControl.setValueState("Error");
valid = false; // set valid to false if it is
} else {
oControl.setValueState("None");
}
});
if (valid === false) {
// **replace this code with w/e error handling code u want**
oView.byId("errorMsgStrip").setVisible(true);
} else if (valid === true) {
// **replace this code with whatever success handling code u want**
var oDialogConfirm = new sap.ui.xmlfragment("dialogID", "dialog.address.here", this);
oDialogConfirm.open();
}
},

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

How to Keep Tabs on a Particular Tab?

In my firefox sdk addon, I want to use a custom webpage from my data directory as my settings/about page.
But I am having trouble keeping tabs on the tab!
So I have a button that calls the OptionsPanel() function to open my webpage in a new tab. Now, I want to make it so if the user forgets that tab is open and pushes the button again, that it activates the already-open settings tab. That means I need to know that the tab is open and I need to be able to switch to it if it is OR open it if it is not already open.
Here is what I've come up with so far, but it doesn't work; it just always opens a new tab. I don't even know if I'm barking up the right tree.
const tabs = require("sdk/tabs");
var optionsTab;
function OptionsPanel(){
var opsTab = GetTabByID(optionsTab.id);
if(opsTab == null){
tabs.open( data.url("OptionsPanel.html") );
optionsTab.id = tabs.tab.id; <======errors out as undefined
}else{
opsTab.activate();
}
}
//return a handle to the tab that matches specified tab id
function GetTabByID(whatid){
for(let thistab of tabs){
if(thistab.id = whatid){
return thistab;
}
}
return null;
}
So, here are my goals:
Open my page in a new tab if it isn't already open.
If the tab is already open, then switch to that tab.
If the page is open when the browser loads, then be ready to switch to that tab if the user pushes the options button.
Why would you think tabs module has a tab property?
Normally you would use the activeTab property instead. However it does not get updated immediately after tabs.open is called. One has to use tabs[tabs.length - 1] instead.
const tabs = require("sdk/tabs");
var optionsTabId;
function OptionsPanel(){
var opsTab = GetTabByID(optionsTabId);
if (opsTab == null) {
tabs.open( data.url("OptionsPanel.html") );
optionsTabId = tabs[tabs.length - 1].id;
} else {
opsTab.activate();
}
}
Additionally, you made a mistake in GetTabByID.
//return a handle to the tab that matches specified tab id
function GetTabByID(whatid){
for(let thistab of tabs){
if(thistab.id == whatid){ // use == to compare
return thistab;
}
}
return null;
}
Keep in mind this assumes that it is not possible to navigate away from your options tab. I would check optsTab.url just in case.
Alternatively you could make use of the tab event interface
const tabs = require("sdk/tabs");
const OPTIONS_URL = data.url("OptionsPanel.html");
var optionsTab = null;
function OptionsPanel(){
if (optionsTab == null) {
tabs.open(OPTIONS_URL);
tabs.once('ready', function(tab) {
optionsTab = tab;
optionsTab.on('ready', function readyListener(tab) {
if (tab.url !== OPTIONS_URL) {
optionsTab.off('ready', readyListener);
optionsTab = null;
}
})
optionsTab.once('close', function(tab) {
optionsTab = null;
})
});
} else {
optionsTab.activate();
}
}