Not Sure if the Documenatation is out of Date, but the standard code for creating a new richtext editor returns
Cannot read property 'RichTextEditor' of undefined
It looks like this is because there is no sap.ui.richtexteditor in the list of included resources.
var oRichTextEditor1 = new sap.ui.richtexteditor.RichTextEditor("myRTE1", {
width:"100%",
height:"300px",
showGroupClipboard:true,
showGroupStructure:true,
showGroupFont:true,
showGroupInsert:true,
showGroupLink:true,
showGroupUndo:true,
tooltip:"My RTE Tooltip"
});
What are my other options for a RichText/WYSIWYG editor in SAPUI5 ?
You should use the sap.ui.define syntax to require the RichTextEditor control in your controller. As the control library is not included in the resources, it would not be readily available.
sap.ui.define([
"com/sap/app/controller/BaseController",
.
.
.
"sap/ui/richtexteditor/RichTextEditor"
], function (BaseController, ........, RichTextEditor) {
onAfterRendering: function () {
var oRichTextEditor1 = new RichTextEditor("myRTE1", {
width:"100%",
height:"300px",
showGroupClipboard:true,
showGroupStructure:true,
showGroupFont:true,
showGroupInsert:true,
showGroupLink:true,
showGroupUndo:true,
tooltip:"My RTE Tooltip"
});
}
});
Related
I created a popover which is called on mouseover via addEventDelegate.
The popover has a text element with binding to my Model.
The fragment:
<Popover xmlns="sap.m"
id="myCardPopover"
showHeader="false"
contentWidth="260px"
placement="Bottom">
<Text id="myCardPopoverText" text="{propertyModel>Tooltip}" />
</Popover>
Register the browser events:
attachCardPopoverOnMouseover: function () {
var rootHBox = this.byId("myReportCardContent");
rootHBox.addEventDelegate({
onmouseover: function () {
this.timeId = setTimeout(() => that.onOpenCardPopover(this), 600);
},
onmouseout: function () {
clearTimeout(this.timeId) || that.onCloseCardPopover(this);
}
}, rootHBox);
},
The event listener:
onOpenCardPopover: function (oControl) {
this.oCardTooltipPopover.then(function (oPopover) {
var oContext = oControl.getBindingContext("propertyModel");
oPopover.setBindingContext(oContext);
oPopover.openBy(oControl);
});
},
The Popover itself is a dependent of multiple controls created from an aggregation binding and is created in onAfterRendering.
// Fragment required from "sap/ui/core/Fragment"
createCardPopover: function () {
var oView = this.getView();
var oRootHBox = this.byId("myReportCardContent");
if (!this.oCardTooltipPopover) {
this.oCardTooltipPopover = Fragment.load({
id: oView.getId(),
name: "namespace.view.CardPopup"
}).then(function (oPopover) {
oRootHBox.addDependent(oPopover);
return oPopover;
});
}
},
When I hover over one of my controls, I only get an empty popover. The bound text isn't shown.
However, when I lookup the created popover in the UI5 debug tools, the binding seems correct and also the text is shown up there.
The text's <span> element in the DOM is also empty.
[...] multiple controls created from an aggregation binding
With this.byId("myReportCardContent"), you're accessing the template control rather than the actual rendered ones. A template in aggregation binding is usually a static control (i.e. without propagated models and contexts) that the ManagedObject will clone for each data object. After cloning, the parent models and contexts are propagated to those clones, and not to the internal template control.
So if you add the popover to the dependent aggregation of the template (oRootHBox), there is no context to propagate. The binding paths in the fragment definition stay unresolved.
How the issue can be circumvented is up to you. There are many approaches to show something on mouseover. Just avoid manipulating the template after rendering.
I have a question about custom controls in UI5. Say I want to use a formatter function in the custom control (see the snippet below). A colleague of mine insists that custom control should be as generic as possible (e.g. to be able to specify texts with commas, spaces and newlines in whichever way you need it to be). Thus my idea was to pass formatter function to the custom control. Is it possible and if yes how to do it?
sap.ui.define([
"pr/formatter/Formatter",
"sap/m/Popover",
"sap/m/Text"
], function(Formatter, Popover, Text) {
"use strict";
return Text.extend("pr.control.TextWithPopover", {
metadata: {
aggregations: {
_popover: {
type: "sap.m.Popover",
multiple: false,
visibility: "hidden"
}
}
},
init: function() {
const popover = new Popover({});
this.setAggregation("_popover", popover);
},
setText: function(text) {
if (this.getProperty("text") !== text) {
// How to make it generic?
const formattedText = Formatter.formatCommaListToNewLine(text);
const contentToAdd = new Text({ text: formattedText });
contentToAdd.addStyleClass("popoverContent");
// ...
}
},
renderer: "sap.m.TextRenderer",
});
});
UI5 introduced the standard type "function" to sap/ui/base/DataType in 1.46(Commit) which allows ManagedObject properties to receive functions as their values.
Control
return ControlToExtend.extend("MyControl", {
metadata: {
properties: {
/**
* This function will contain foo and bar as parameters.
* Applications should return xyz.
*/
doSomethingWith: {
type: "function",
},
},
},
// ...
getXYZ: function(/*...*/) {
const doSomethingWith = this.getDoSomethingWith(); // function from the application
if (typeof doSomethingWith == "function") {
const [foo, bar] = [/*...*/];
return doSomethingWith(foo, bar);
} else {
/*default behavior*/;
}
},
});
Application
<MyControl doSomethingWith=".myControllerMethod" /> <!-- or -->
<MyControl doSomethingWith="some.globally.available.function" /> <!-- or -->
<!-- Since 1.69: -->
<MyControl
xmlns:core="sap.ui.core"
core:require="{
'myRequiredFunction': 'mynamespace/myApplicationFunction'
}"
doSomethingWith="myRequiredFunction"
/>
Note: XMLTemplateProcessor (XML-view / -fragment) supports function properties only as of 1.56. (Commit)
myApplicationFunction: function(foo, bar) {
// create and return xyz however the application wants;
},
This way, the control has no hard dependency to the application while keeping the flexibility to allow changing the default output or behavior.
The above option is one of the many solutions to reduce tight couplings in UI5. Another solution would be to add a control property which can be then manipulated by applications via binding and formatter.
Generally, controls (or control libraries) and control consumers (e.g. applications) should be always developed independently; with an interface in between (e.g. MenagedObjectMetadata) and the controls being still open for extensions without disclosing how they're implemented internally.
I am facing this issue with openUI5 and Leafletjs using a custom control from library.
Error:
"The renderer for class demo.map.SimpleMap is not defined or does not
define a render function! Rendering of __map0 will be skipped!"...
library.js
sap.ui.define([
'jquery.sap.global',
'sap/ui/core/library'],
function(jQuery){
"use strict";
sap.ui.getCore().initLibrary({
name: 'demo.map',
version: '1.0.0',
dependencies: ['sap.ui.core'],
types: [],
interfaces: [],
controls:[
'demo.map.SimpleMap'
],
elements:[]
});
return demo.map;
});
SimpleMap.js
sap.ui.define([
'jquery.sap.global',
'sap/ui/core/Control',
'./library'], function(jQuery, Control, library){
"use strict";
let SimpleMap = Control.extend('demo.map.SimpleMap',{
metadata:{
library: 'demo.map',
properties:{}
}
});
SimpleMap.prototype.drawMap = function(){
this.controlAspect = parseInt(450) / parseInt(350);
let map = L.map('map').setView([39.7166700,-8],8);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap contributors'}).addTo(map);
}
SimpleMap.prototype.onAfterRendering = function(){
this.drawMap();
}
return SimpleMap;
}, true);
SimpleMapRenderer.js
sap.ui.define(['jquery.sap.global'], function(jQuery){
"use strict";
let SimpleMapRenderer = {};
SimpleMapRenderer.renderer = function(oRm, oControl){
oRm.write('<div ');
oRm.writeControlData(oControl);
oRm.write('>');
oRm.write('</div>');
}
return SimpleMapRenderer;
});
Startpage.view.xml
<mvc:View controllerName="sap.ui.demo.fiori.controllers.Startpage" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:layout="sap.ui.layout">
<Page title="Fiori Tile Demo">
<layout:VerticalLayout class="sapUiResponsiveMargin">
<Title titleStyle="H2" text="Launchpad Menu" class="sapUiTinyMarginBegin"/>
<layout:HorizontalLayout allowWrapping="true" id="layout">
</layout:HorizontalLayout>
</layout:VerticalLayout>
</Page>
</mvc:View>
Startpage.controller.js
sap.ui.define(['sap/ui/demo/fiori/controllers/BaseController'], function(Controller){
"use strict";
return Controller.extend('sap.ui.demo.fiori.controller.Startpage',{
onInit:function(){
console.log('Startpage loaded');
let map = new demo.map.SimpleMap();
//console.log(map);
let oLay = this.getView().byId('layout');
oLay.addContent(map);
},
gotoUserList: function(){
this.getRouter().navTo('listUsers');
},
getRouter: function(){
return this.getOwnerComponent().getRouter();
}
});
});
Also, I have tried to add the map object directly from controller without custom control, but i got below error
'Map container not found' error from Leafletjs framework.
Hope someone please help me. I am pretty lost in how to render leaflet using openUI5.
Ok, this is how i could get it work:
SimpleMap.js
sap.ui.define(['jquery.sap.global','sap/ui/core/Control', './library'], function(jQuery, Control, library){
"use strict";
let SimpleMap = Control.extend('demo.map.SimpleMap',{
metadata:{
library: 'demo.map',
properties:{
"width":{type: 'sap.ui.core.CSSSize', defaultValue:'300px'},
"height":{type: 'sap.ui.core.CSSSize', defaultValue:'300px'}
}
}
});
SimpleMap.prototype._drawMap = function(){
this.controlAspect = parseInt(300) / parseInt(300);
/* using: L.map(this.$()).setView(...); << trown an error:
"Cannot read property 'baseVal' of undefined", which seems that is a jQuery object instead of a DOM element. Ref:
https://github.com/Leaflet/Leaflet/issues/2302
I couldn't how to get current DOM element.
*/
let map = L.map('map').setView([39.7166700,-8],8);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap contributors'}).addTo(map);
}
SimpleMap.prototype.onAfterRendering = function(){
this._drawMap();
}
return SimpleMap;
}, true);
SimpleMapRenderer.js
sap.ui.define(['jquery.sap.global'], function(jQuery){
"use strict";
let SimpleMapRenderer = {};
SimpleMapRenderer.render = function(oRm, oControl){
oRm.write('<div ');
oRm.write('id="map"'); // set it hardcoded
oRm.writeControlData(oControl);
oRm.addStyle('height',oControl.getHeight());
oRm.addStyle('width',oControl.getWidth());
oRm.writeStyles();
oRm.write('>');
oRm.write('</div>');
}
return SimpleMapRenderer;
}, true); // notice this: last version i did not export it...
Thanks for all your help.
PS.- Instead of using oRm.write('id="map"'); << and from control how to get dom element using this.$() ?
I have tried: this.$()[0] but nothing...
The error message pretty tell you precisely the problem: Your SimpleMapRenderer.js does not define a function called render. Instead you have called it renderer, which is what it would be called if you left it inside the SimpleMap.js. (Also, see edit below, the SimpleMapRenderer object needs to be exported, i.e. you need to add a true parameter to the define() call.)
The other problem ('Map container not found' error from Leafletjs framework.) will probably reappear once you fix the function name. That happens because your drawMap function directly references an element with id 'map'. OpenUI5 however will use the Control's id. You should change your call to
let map = L.map(this.getDomRef()).setView([39.7166700,-8],8);
That should create the map inside the div created by the ui5 renderer.
Edit: I've built the code in plnkr: http://plnkr.co/edit/BTDfwegfvO4iR4T3Mod0?p=preview
It shows I should have used getDomRef() instead of $(). Fixed above.
It also showed, you forgot to export the render class definition (and I didn't notice). After also adding a height to the div (and loading the css), it now properly draws a map.
I extend a standard fiori application and I would like to extend i18n also.
The structure of the extended app looks as the following:
And in the Component.js file, I notice the extension:
this.i2d.eam.pmnotification.create.s1.Component.extend("i2d.eam.pmnotification.create.s1.ZEAM_NTF_CRES1_EXT.Component", {
metadata: {
manifest: "json",
config: {
"sap.ca.i18Nconfigs": {
bundleName: "i2d.eam.pmnotification.create.s1.ZEAM_NTF_CRES1_EXT.i18n.i18n"
}
}
}
});
but the text still does not get translated.
The content of the i18n.properties file:
ext.createNotification=Create notification
ext.createOrder=Create order
and Buttons, that are using the translation:
<Button press="onCreateWithOrder" text="{i18n>ext.createOrder}" />
<Button press="onSave" text="{i18n>ext.createNotification}"/>
What am I doing wrong?
I only used "sap.ca.i18Nconfigs"with success when translating apps using the old fashion "scaffolding" namespace (sap.ca.scfld).
I would bet that the app you are extending is not based on it.
Then, try to add the following calls to your Component.js file
init: function() {
UIComponent.prototype.init.apply(this, arguments);
var oModel = new sap.ui.model.resource.ResourceModel({
bundleUrl: "../pathToYourParentApp/i18n/i18n.properties"
});
oModel.enhance({
bundleUrl: "i18n/i18n.properties"
});
this.setModel(oModel, "i18n");
sap.ui.getCore().setModel(oModel, "i18n");
}
Also, check the example
https://github.com/fabiopagoti/ui5-extension
The control sap.m.MessagePopover has an attribute _oPopover (containing sap.m.Popover inside).
Using this attribute, I could set the popover width:
messagePopover._oPopover.setContentWidth("450px");
However, as SAP attributes starting from _ should not be used, does anybody know a cleaner way?
As of UI5 version 1.46, a more flexible control sap.m.MessageView can be used instead of the old sap.m.MessagePopover.
There is no need to access internal properties or apply custom CSS style classes to manipulate the width as you can put MessageView anywhere you want (Still, Fiori Guideline recommends to use it only within a responsive popover or a dialog).
const popover = new ResponsivePopover({
contentWidth: "450px",
contentHeight: "450px",
content: [
/*myMessageView*/
],
});
// ...
popover.openBy(...);
Compared to MessagePopover, MessageView can group items and more.
Internally, MessagePopover uses MessageView too.
Another solution would be to use CSS class. However, there is a catch. As you can see from below generated DOM of the message popover, inline styling has been used :( .
Only way to override inline-style is by using !important in CSS which is again not recommended approach. However, considering inline CSS has been used, I would go with using !important keyword. Below is the working code:
XML Code ( for adding Class):
<MessagePopover id='myMP' class='myPopoverClass'>
<items>
<MessagePopoverItem title='Title' subTitle='SubTitle'></MessagePopoverItem>
</items>
</MessagePopover>
CSS:
.myPopoverClass {
width:100rem;
}
.myPopoverClass .sapMPopoverCont {
width:100% !important;
}
You can play around with how much width you need for message Popover.
EDIT: This is from the source code:
MessagePopover.prototype.init = function () {
var that = this;
var oPopupControl;
this._oResourceBundle = sap.ui.getCore().getLibraryResourceBundle("sap.m");
this._oPopover = new ResponsivePopover(this.getId() + "-messagePopover", {
showHeader: false,
contentWidth: "440px",
placement: this.getPlacement(),
showCloseButton: false,
modal: false,
afterOpen: function (oEvent) {
that.fireAfterOpen({openBy: oEvent.getParameter("openBy")});
},
afterClose: function (oEvent) {
that._navContainer.backToTop();
that.fireAfterClose({openBy: oEvent.getParameter("openBy")});
},
beforeOpen: function (oEvent) {
that.fireBeforeOpen({openBy: oEvent.getParameter("openBy")});
},
beforeClose: function (oEvent) {
that.fireBeforeClose({openBy: oEvent.getParameter("openBy")});
}
}).addStyleClass(CSS_CLASS);