How to reload a component in SAPUI5? - sapui5

I have a SAPUI5 application that uses sap.ui.core.ComponentContainer for loading other applications inside of itself. Something similar to fiori launchpad. But it is amazing that when I remove the component container from the page and try to reload it later it will be added to the HTML page but it will not shown.
var oPage = this.getView().byId("page");
oPage.removeAllContent();
if(!this._aComps[sObjectId]){
this._aComps[sObjectId] = new sap.ui.core.ComponentContainer({ name: sObjectName});
}
oPage.addContent(this._aComps[sObjectId]);
Any idea that what is the reason that it's shown only in initialization time?
While this code always works:
var oPage = this.getView().byId("page");
oPage.removeContent();
oPage.addContent(new sap.ui.core.ComponentContainer({ name: sObjectName}));

After a huge investigation it seems I found a bug in SAPUI5.
What happened after removing a ComponentContainer and add it again is that SAPUI5 removes the style="height: 100%;" from the component container element.
It seems it doesn't return back the height to its default value after adding it again. Also I tested the setVisible function and the problem exist there as well.
So what we have to do is:
var oPage = this.getView().byId("page");
oPage.removeAllContent();
if(!this._aComps[sObjectId]){
this._aComps[sObjectId] = new sap.ui.core.ComponentContainer({ name: sObjectName});
}
oPage.addContent(this._aComps[sObjectId]);
this._aComps[sObjectId].setHeight("100%"); // Set the height to 100% again!
Note: It removes the height: 100%; not only for the component container itself but for some other parent elements as well. Please concentrate on the picture! While this solution works for me, maybe in other use cases it is needed to set the height of some other elements!

Related

How to change layout of sap.uxap.ObjectPage Blocks

Demo
I have 2 + 5 blocks here, in small screen, each panel in blocks are in full width.
But in large screen, blocks are in 3:3:3 or 6:3. I want them all in a single row.
each section is contained in <div class="sapUiRespGridSpanL4 sapUiRespGridSpanM6 sapUiRespGridSpanS12 sapUiRespGridSpanXL3">
How to change it to class="sapUiRespGridSpanL12 sapUiRespGridSpanM12 sapUiRespGridSpanS12 sapUiRespGridSpanXL12" ?
I've tried to add layout in Panel, but not working.
Refrence:
sap.uxap.ObjectPageLayout Documentation
layout of blocks, blocks are in the same color, hard to specify
Finally after 1.5 hours figured out
Reason: Blocks will have to be extended from BlockBase to apply columnLayout.
Extending the BlockBase:
sap.ui.define(["sap/uxap/BlockBase"], function (BlockBase) {
"use strict";
var BlockPanel = BlockBase.extend("sap.uxap.sample.ObjectPageSubSectionSized.blocks.BlockPanel", {
metadata: {
/* no additional views provided */
}
});
return BlockPanel;
});
Then create a view and controller using the above new ui5 extended control. Use that in your page with columnLayout
xmlns:sample="sap.uxap.sample.ObjectPageSubSectionSized.blocks"
...
...
<uxap:blocks>
<sample:BlockPanel columnLayout="4"/>
</uxap:blocks>
columnLayout can't be applied if you don't extend block base. (which is really pathetic design). Nevertheless, values range from 1-4 and "auto".
Created working plnkr here
How to build custom SAPUI control?
You can wrap the target controls up with sap.uxap.BlockBase[API]. BlockBase controls are intended to be used inside sap.uxap.ObjectPageSubSection (hence the name <blocks>) and support customizing the grid spans with the property columnLayout.
Here is a demo: https://embed.plnkr.co/lSrDk9/?show=view%2FHome.view.xml,preview
<uxap:ObjectPageSubSection>
<block:MyBlock columnLayout="4"/>
<block:MyBlock columnLayout="4"/>
</uxap:ObjectPageSubSection>
Provide a not elegant, but very fast way: Overwrite CSS
<uxap:ObjectPageSubSection class="fullWidthPanel">
/* CSS specificity */
.fullWidthPanel .sapUiRespGrid.sapUiRespGridHSpace1 > div {
width: 98.88888889%;
width: -webkit-calc(100% - 1rem);
width: calc(100% - 1rem)
}

Using dojo dom.byId is not getting an element added programmatically

I'm creating a dom element programatically using dojo and I can "see" it in the dom with its id, but when I attempt a dom.byId("myId") it returns null.
I have a similar jsfiddle that is actually working (so it doesn't reproduce my problem, but it gives an idea of what I'm trying to do): if you click the button (ignore the lack of styling) in the run output panel, it alerts the content of the element retrieved by dom.byId. But similar code within my dojo widget is not working. Here's the code:
var content = lang.replace(selectFilterTemplate, {
"layer-id": layer.id,
"layer-index": idx,
"filter-name": filter.name
}); // this gets template HTML code similar to what's in the HTML panel of the jsfiddle, only it has placeholder tags {} instead of literals, and the tags are replaced with the attributes of the layer, idx, and filter objects here
// Use dojo dom-construct to create a div with the HTML from above
var node = domConstruct.create("div", { "innerHTML": content });
// put the new div into a dojo ContentPane
var filterPanel = new ContentPane({
"id": layer.id + "-filter-" + idx + "-panel",
"content": node,
"style": "width: 200px; float: left;"
});
// Get the dom element:
var mstag = dom.byId(layer.id + "-filter-" + idx + "-ms-tag")
// this is the same as the "var ms = dom.byId("IssuePoints-filter-1-ms-tag")" in the jsfiddle, but this one returns null. If I view the contents of the 'node' variable in the browser debugging console at this point, I can see the <select> tag with the id I'm referencing.
Why would I be getting null in my dom.byId() if I can see that element in the dom in the debugging console?
It seems that the element is added to the dom at a later point. You may see it with the debugger but it is not yet available the moment you call byId().
In the code you posted you create the filterPanel element but you do not place it in the dom. I assume this happens at a later stage. In contrast, the jsfiddle places the Button element with placeAt() directly after constructing it.

Using jquery-ias with async-loaded content

I've somewhat successfully integrated the jQuery Infinite Ajax Scroll plugin into my development site - it is used twice, first on the thread list on the left, second when you load up an individual topic - but I'm having trouble with the second ias instance here.
Basically the content of a topic is loaded via $.get and then rendered into the page, and then I trigger setupThreadDetailDownwardScroll() in JS which creates an instance of ias:
var iasDetail = jQuery.ias({
container: "#reply-holder",
item: ".post",
pagination: ".threaddetail-pagination",
next: ".load-next-inner-link a",
delay: 2000,
});
if (iasDetail.extension) {
iasDetail.extension(new IASPagingExtension());
iasDetail.extension(new IASTriggerExtension({
text: 'More Replies',
html: '<div class="scroll-pager"><span>{text}</span></div>',
offset: 10,
}));
iasDetail.extension(new IASNoneLeftExtension({html: '<div class="scroll-message"><span>No more replies</span></div>'}));
iasDetail.extension(new IASHistoryExtension({
prev: '.load-previous-inner-link a',
}));
}
iasDetail.on('load', function() {
$('#reply-holder').append(scrollLoading);
})
iasDetail.on('rendered', function() {
$('.scroll-loading').remove();
iasDetail.unbind();
})
But the problem is that this only works with whatever the first topic you load is - you'll get working pagination in the first thread, but then it'll fallback to anchor links when you open the next thread.
So I figured that I needed to rebind ias once this new content is inserted, and this is why I have added the unbind() call in rendered, and then I re-call setupThreadDetailDownwardScroll() whenever another thread is loaded. This doesn't work either though.
Is there a correct procedure here?
You are using jQuery.ias(...) which binds to the scroll event of $(window). In your case you probably want to bind to an overflow div. Therefor you should specify the scrollContainer like this:
$('#scrollContainer').ias(...)
Edit:
Based on you comment I took another look at it and might have found an answer. When you call jQuery.ias({...}); IAS gets setup and waits for $(document).ready to initialize. You say you want to initialize IAS in your setupThreadDetailDownwardScroll function. You can try to initialize IAS yourself with the following code
iasDetail.initialize();

TinyMCE: Get orignial textarea reference within initialization

Problem
Width of the <textarea> is defined by CSS class, for ex.: wMax or wDefault. In first case it is 100%, in the second, lets say 200px. By default TinyMCE converts everything to fixed width in pixels. Ofcourse I can set width:100% inside tinyMCE.init(), but that will not cover textarea's with wDefault / fixed with.
What I need
I need TinyMCE width to behave the same as original, % or px depending on it's CSS class.
If I could find a reference to the original textarea element within tinyMCE.init() procedure, then I could read CSS class from it, and set width: (textarea.hasClass('wMax') ? '100%' : null) or something like that
I am aware of the getElement() function, which gets me exactly that textarea. But where do I run it from? tinyMCE.activeEditor is null within init().
I'm currently still using TinyMCE 3, but it would be nice if you could answer this also for 4.x version, if there is any difference ofcourse...
Found the solution myself. Sharing.
Answering my own question in the title: It's not possible to refer to the textarea within init() procedure directly, because it does not run for each tinyMCE instance. It runs only once. But: TinyMCE has a customizable setup function, which does run for every instance and has all required references to solve the mentioned problem.
With the following code:
tinyMCE.init({
// ... your settings here ...
setup: function(ed){
if(ed.getElement().hasClass('wMax')){
ed.settings.width = '100%';
}
}
});
Any textarea with CSS class 'wMax' (replace with your own) will be replaced by TinyMCE instance having 100% width. All others will have a fixed width, equal to the width of the textarea at the moment of initialization. You can expand this approach with any width, like wHalf width:50% etc.
Note: .hasClass() function is a part of Mootools JS library. Replace with another if you use a different library.
I don't know if this will lead you into the right direction. I use this code to adjust the iframe height to fit the entered content. You could tweak it a bit to adjust its height and width to your needs (you will need to get the textarea by $('#' + ed.id) onInit.
Here is the function. Basically it changes the style attributes explicitly of the editor iframe
resizeIframe: function(frameid) {
var frameid = frameid ? frameid : this.editor.id+'_ifr';
var currentfr=document.getElementById(frameid);
if (currentfr && !window.opera){
currentfr.style.display="block";
if (currentfr.contentDocument && currentfr.contentDocument.body.offsetHeight) { //ns6 syntax
currentfr.height = currentfr.contentDocument.body.offsetHeight + 26;
}
else if (currentfr.Document && currentfr.Document.body.scrollHeight) { //ie5+ syntax
currentfr.height = currentfr.Document.body.scrollHeight;
}
styles = currentfr.getAttribute('style').split(';');
for (var i=0; i<styles.length; i++) {
if ( styles[i].search('height:') ==1 ){
styles.splice(i,1);
break;
}
};
currentfr.setAttribute('style', styles.join(';'));
}
},

Create jQuery ui resizable instance using selector added to DOM by jQuery

I'm trying to start a jquery ui resizable instance, but using a selector added to the DOM by jquery itself. This is a basic example of my script:
HTML:
<div class='lyr'></div>
jQuery:
// Add class
$('lyr').addClass('fixed');
// Resizable
$('.fixed').resizable({
aspectRatio: true,
handles: 'all'
});
I've thought about using something along the lines of live() or bind() but I have no event to bind to. Any help appreciated.
I have used the LiveQuery plugin - http://brandonaaron.net/code/livequery/docs in the past to be able to target elements added to the dom, like in your case.
If I've got this right, you want anything on the page which has the class "fixed" to be resizable, even if the class is added after the page has loaded? You're right that live, bind and delegate won't help here.
I can think of two possibilities, neither lovely.
First, set up a live "mouseenter" event which will make the element resizable if it wasn't before:
$(body).delegate(".fixed", "mouseenter", function(ev) {
var target = $(ev.target);
if (target.data("resizable")) return;
target.resizable({
aspectRatio: true,
handles: 'all'
});
})
This gets us round the problem of having no event to bind to.
Alternatively, you could monkeypatch jQuery.fn.addClass:
var classRe = new RegExp(c + className + \b);
._addClass = jQuery.fn.addClass;
jQuery.fn.addClass = function(className) {
if (classRe.test(classname)) {
if (this.data("resizable")) return;
this.resizable({
aspectRatio: true,
handles: 'all'
});
}
jQuery.fn._addClass.apply(this, arguments);
}
Of course this will only work if the class is added through the addClass method.
Also in your example,
$('lyr').addClass('fixed');
Should probably be:
$('.lyr').addClass('fixed');