There are about ten tiles in our fiori app, and I put my shared files in /common, the following image shows the project structure(/analytics, /appsettings... are all tiles except /common):
Many i18n are the same between the tiles, so I want to maintain all i18n in /common.
In /analytic/webapp/Component.js, I defined i18n resource:
init: function() {
UIComponent.prototype.init.apply(this, arguments);
// resourceroots is also defined in manifest.json,
// why I defined resourceroots thrice is another topic, it's stupid, I know
jQuery.sap.registerModulePath("myprojectcommon", "./../../common");
jQuery.sap.registerModulePath("myproject.common", "/myproject/resources/common/");
myprojectcommon.util.Utils.setComponent(this);
var setComponentCommonResource = myprojectcommon.util.Utils.setCommonResource.bind(this);
setComponentCommonResource();
}
/common/util/Utils:
setCommonResource: function() {
var i18nModel = new sap.ui.model.resource.ResourceModel({
bundleName: "myproject.common.i18n.i18n"
});
this.setModel(i18nModel, "i18n");
}
Now the common i18n is loaded, but i18n defined in manifest.json failed, such as
"sap.app": {
"title": "{{appTitle}}",
"description": "{{appDescription}}"
}
Which results:
What should I do?
There seems no API to set title in Component.js: https://sapui5.hana.ondemand.com/#/api/sap.ui.core.UIComponent
Related questions: One app with multiple component.js: How to load shared modules?
fixed by
"sap.app": {
"i18n": "./../../common/i18n/i18n.properties"
}
"sap.ui5": {
"resourceRoots": {
"myprojectcommon": "./../../common/"
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "myprojectcommon.i18n.i18n"
}
},
}
below is how ui5 load i18n files location:
https://github.com/SAP/openui5/blob/c014e24356497a44125c1dd5419fdf536119600e/src/sap.ui.core/src/sap/ui/core/Manifest.js#L200
Related
I can't figure out how to track the User Location via GPS.
This code can I add in my Mapbox:
const geolocate = (new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
}));
map.addControl(geolocate)
But it doesn't show it in iOS. What to do? Maybe add some Ionic/Cordova tricks?
Maybe this helps:
Not all browsers support geolocation, and some users may disable the
feature. Geolocation support for modern browsers including Chrome
requires sites to be served over HTTPS. If geolocation support is not
available, the GeolocateControl will not be visible.
from:
https://docs.mapbox.com/mapbox-gl-js/api/#geolocatecontrol
Are you loading the mapbox-gl css? Without the css files the markers and controls are invisible.
You might need to add this line in angular.json:
{
...
"projects": {
"app": {
...
"architect": {
"build": {
...
"options": {
...
"styles": [
...
},
{
"input": "node_modules/mapbox-gl/dist/mapbox-gl.css"
}
I have created a shell-in-shell construct in the index.html:
sap.ui.getCore().attachInit(function () {
// create a new Shell that contains the root view
var oShell = new sap.m.Shell({
id: "appShell",
app: new sap.ui.core.ComponentContainer({
name: "internal_app",
height: "100%"
})
});
// load the view that contains the unified shell
var oAppShellView = sap.ui.view({
type: sap.ui.core.mvc.ViewType.XML,
viewName: "internal_app.view.AppShell"
});
// access the unified shell from the view
var oUnifiedShell = oAppShellView.byId("unifiedShell");
// place the app shell in the unified shell
oUnifiedShell.addContent(oShell);
oAppShellView.placeAt("content");
});
In addition, a default model has been defined in manifest.json:
....
},
"models": {
"": {
"type": "sap.ui.model.json.JSONModel"
}
},
....
In the controller of the view internal_app.view.AppShell (which has been created by the code snippet above) I would now like to access the default model but neither this.getModel() nor this.getOwnerComponent().getModel() (getModel() and getOwnerComponent() return undefined) worked. I assume that the AppShell controller does not have an owner. But how can I access the default model in the onInit of that controller?
The app structure in your case is somewhat unusual - Nevertheless, you can always access the model, defined in manifest.json, as long as you can access the inner component.
Assuming this is referencing the controller of the internal_app.view.AppShell, you can get the default model like this:
onInit: function() {
var innerShell = sap.ui.getCore().byId("appShell"); // only if the app is standalone
this.componentLoaded(innerShell.getApp()).then(this.onComponentCreated.bind(this));
},
componentLoaded: function(componentContainer) {
var component = componentContainer.getComponent();
return component ? Promise.resolve(component) : new Promise(function(resolve) {
componentContainer.attachEventOnce("componentCreated", function(event) {
resolve(event.getParameter("component"));
}, this);
}.bind(this));
},
onComponentCreated: function(component) {
var myDefaultModel = component.getModel(); // model from manifest.json
// ...
}
I did the steps to catch and handle invalid hashes with SAPUI5 but my application is not working.
When i try to navigate to NotFound view changing the Hash i only gets an Info message:
But the view isn't displayed.
[EDIT]:
Adding source code files:
Here i added the bypassed section
I've created the target in Targets section of the manifest:
This is the NotFound.controller.js
sap.ui.define([
"my/path/controller/BaseController"
], function (BaseController) {
"use strict";
return BaseController.extend("my.path.controller.NotFound", {
onInit: function () {
var oRouter, oTarget;
oRouter = this.getRouter();
oTarget = oRouter.getTarget("NotFound");
oTarget.attachDisplay(function (oEvent) {
this._oData = oEvent.getParameter("data"); // store the data
}, this);
},
// override the parent's onNavBack (inherited from BaseController)
onNavBack : function (oEvent){
// in some cases we could display a certain target when the back button is pressed
if (this._oData && this._oData.fromTarget) {
this.getRouter().getTargets().display(this._oData.fromTarget);
delete this._oData.fromTarget;
return;
}
// call the parent's onNavBack
BaseController.prototype.onNavBack.apply(this, arguments);
}
});
});
Here the NotFound.view.xml:
<mvc:View
controllerName="my.path.controller.NotFound"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<MessagePage
title="{i18n>NotFound}"
text="{i18n>NotFound.text}"
description="{i18n>NotFound.description}"
showNavButton="true"
navButtonPress="onNavBack"/>
</mvc:View>
And here the onInit method at the App controller:
onInit: function(){
jQuery.sap.log.setLevel(jQuery.sap.log.Level.INFO);
var oRouter = this.getRouter();
oRouter.attachBypassed(function (oEvent) {
var sHash = oEvent.getParameter("hash");
// do something here, i.e. send logging data to the backend for analysis
// telling what resource the user tried to access...
jQuery.sap.log.info("Sorry, but the hash '" + sHash + "' is invalid.", "The resource was not found.");
});
oRouter.attachRouteMatched(function (oEvent){
var sRouteName = oEvent.getParameter("name");
// do something, i.e. send usage statistics to backend
// in order to improve our app and the user experience (Build-Measure-Learn cycle)
jQuery.sap.log.info("User accessed route " + sRouteName + ", timestamp = " + new Date().getTime());
});
}
and
Any can help me?
Regards,
Check this plunker:
https://plnkr.co/edit/pxOkRSM8c97hXO6gkbpV
The key config is this on manifest.json:
"targets": {
"tgHome": {
"viewPath": "sapui5Example",
"viewName": "home"
},
"notFound": {
"viewPath": "sapui5Example",
"viewName": "NotFound",
"transition": "show"
}
}
To fire the 'not found' route, download the plunker and in the URL, after the hash just type anything and you will the the not Found Page (if you do it directly on plunker it won't work). Here is a pic:
Given a tree how to initialize it in such way that the nodes are expanded at will?
I already tried to get a reference with #ViewChildren(Tree) tree but is resulting in undefined references when trying to access his children
This is a hack that basically simulates clicks along the tree. I add this solution but I really hope someone could find something better.
Given a component with a tree we can get a reference to the treenodes and then "click" them as necessary:
#Component({
selector: 'filemanager',
templateUrl: './filemanager.html',
directives: [Tree]
})
export class FileManagerComponent implements AfterViewInit {
constructor(private renderer:Renderer) {}
ngAfterViewInit() {
setTimeout(() => { // a timeout is necessary otherwise won't find the elements
// get the first "p-tree" tag and find his first "toggler"
let element = document.getElementsByTagName("p-tree")[0].getElementsByClassName("ui-tree-toggler fa fa-fw fa-caret-right")[0];
//"click" the toggler using the angular2 renderer
let event = new MouseEvent('click', {bubbles: true});
this.renderer.invokeElementMethod(element, 'dispatchEvent', [event]);
}, 200);
}
// more methods and state...
}
In case you need to initialize deeper nodes in the tree you will need to nest setTimeout functions.
For initialize your tree component expanded you only need set in you json format the property expanded as true.
Sample:
{
"data":
[
{
"label": "Pictures",
"data": "Pictures Folder",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"expanded": true, // this flag shoud be true
"children": [
{"label": "barcelona.jpg", "icon": "fa-file-image-o", "data": "Barcelona Photo"},
{"label": "logo.jpg", "icon": "fa-file-image-o", "data": "PrimeFaces Logo"},
{"label": "primeui.png", "icon": "fa-file-image-o", "data": "PrimeUI Logo"}]
},
]
}
You can handle this with a function that put all the expanded attributes to true.
expandAll(toggle: boolean) {
this.tree.map(node => {
node.expanded = toggle;
});
}
ngOnInit() {
setTimeout(()=>{
this.expandAll(true);
}, 0);
}
I'm writing an application which should embed specific website into a <webview> and inject some CSS and JS code to adapt this website for viewing on certain touch-sensitive device.
The problem is that I can't find a way to inject my code when page is loaded, instead the code is injected AFTER the page is rendered and, as result, all modifications become visible.
While code injection perfectly works with chrome extensions and content script (by setting run_at attribute to document_end on manifest.json, this is not the case for webviews.
This is my code:
manifest.json
{
"name": "App",
"version": "0.0.1",
"manifest_version": 2,
"app": {
"background": {
"scripts": [ "main.js" ]
}
},
"permissions": [
"webview"
]
}
main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', { state: "normal" },
function(win) {
win.contentWindow.onload = function() {
var wv = this.document.querySelector('webview');
wv.addEventListener('contentload', function(e) {
this.insertCSS({ code: "body { background: red !important; }" });
});
}
}
);
});
index.html
<webview src="https://developer.chrome.com/apps/tags/webview" style="width: 100%; height: 100%;"></webview>
The same on the Gist: https://gist.github.com/OnkelTem/ae6877d2d7b2bdfea5ae
If you try this app, you will see that only after the webview is loaded and fully rendered my CSS rule is applied and the page background becomes red. In this example I use contentload webview event, but I also tried all other webview events: loadstart, loadstop, loadcommit - with no any difference.
I tried also using webview.contentWindow, but this is object is EMPTY all the time, despite documentation states it should be used.
Any ideas? Is it possible at all?
First of all, use the loadcommit event instead of the contentload event.
Second, add runAt: 'document_start' to the webview.insertCSS call (this also applies to webview.executeScript, if you ever want to use it). The implementation of executeScript is shared with the extension's executeScript implementation, but unfortunately the app documentation is incomplete. Take a look at chrome.tabs.insertCSS until the app documentation is fixed.
Here is an example that works as desired:
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', { state: 'normal' },
function(win) {
win.contentWindow.onload = function() {
var wv = this.document.querySelector('webview');
// loadcommit instead of contentload:
wv.addEventListener('loadcommit', function(e) {
this.insertCSS({
code: 'body { background: red !important; }',
runAt: 'document_start' // and added this
});
});
}
}
);
});
Note: Although the previous works, I recommend to put the script that manipulates the webview in index.html, because the resulting code is much neater.
// main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', { state: 'normal' });
});
<!-- index.html -->
<webview src="https://developer.chrome.com/apps/tags/webview" style="width: 100%; height: 100%;"></webview>
<script src="index.js"></script>
// index.js
var wv = document.querySelector('webview');
wv.addEventListener('loadcommit', function() {
wv.insertCSS({
code: 'body { background: red !important; }',
runAt: 'document_start'
});
});