Vaadin's Window extends Panel. A call to Window.setCaption() simply calls Panel.setCaption() which does support HTML. When I call Window.setCaption(), however, HTML is not supported. Not sure what is going on.
The client side (gwt) code for the caption handling is different. The VWindow class uses Util.escapeHTML() on the caption VPanel does not.
VWindow.java:
public void setCaption(String c, String icon) {
String html = Util.escapeHTML(c);
if (icon != null) {
icon = client.translateVaadinUri(icon);
html = "<img src=\"" + Util.escapeAttribute(icon)
+ "\" class=\"v-icon\" />" + html;
}
DOM.setInnerHTML(headerText, html);
}
VPanel.java
public void setCaption(String text) {
DOM.setInnerHTML(captionText, text);
}
Related
I am creating a custom button component which involves an label and button. I can set the displaytext and other properties using XAML after defining them in the attrs.xml (like
<UButton displayText="Hello" ... />
). but i need to allow the Click event of this control to be handled by the user by defining it in the XAML like
mclick="button_click"
. but i am not able to find a documentation for handling this. Can you please guide me.
Let's look Button's source code.
In this link (Android attrs.xml source code), search onClick, you will find:
<!-- Name of the method in this View's context to invoke when the view is
clicked. This name must correspond to a public method that takes
exactly one parameter of type View. For instance, if you specify
<code>android:onClick="sayHello"</code>, you must declare a
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity). -->
<attr name="onClick" format="string" />
That why when you use :
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="onClick"/>
it will find the onClick method.
Here, you will see how Android handle the android:onClick="onClick", in it's View(Context context, AttributeSet attrs, int defStyleAttr) method, android:onClick="onClick" is correspond to R.styleable.View_onClick:
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new OnClickListener() {
private Method mHandler;
public void onClick(View v) {
if (mHandler == null) {
try {
mHandler = getContext().getClass().getMethod(handlerName,
View.class);
} catch (NoSuchMethodException e) {
int id = getId();
String idText = id == NO_ID ? "" : " with id '"
+ getContext().getResources().getResourceEntryName(
id) + "'";
throw new IllegalStateException("Could not find a method " +
handlerName + "(View) in the activity "
+ getContext().getClass() + " for onClick handler"
+ " on view " + View.this.getClass() + idText, e);
}
}
try {
mHandler.invoke(getContext(), View.this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not execute non "
+ "public method of the activity", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Could not execute "
+ "method of the activity", e);
}
}
});
}
break;
You will find, it find the method by reflection.
So, I guess you have forgot to handle the mclick="button_click". As usually, we don't use this to add click listener, it is a little complex. We use View's setOnClickListener.
is there a way to inject HTML without filtering the src attribute in Dart (no security needed in context), I've tried setInnerHtml, but it doesn't let src pass it...
here's the div I'm adding html in:
<div id="output">
<!--here goes messages-->
</div>
here's my dart code:
import 'dart:html';
import 'dart:async';
InputElement input = querySelector("#textInput");
var output = querySelector("#output");
var buttonSend = querySelector("#send");
var buttonImg = querySelector("#url");
var buttonVideo = querySelector("#video");
Future<Null> main() async {
//send custom message
buttonSend.onClick.listen((MouseEvent event) async{
addContent(input.value);
});
//send img
buttonImg.onClick.listen((MouseEvent event) async{
addContent(getPrefabImg(input.value));
});
//send video
buttonVideo.onClick.listen((MouseEvent event) async{
addContent(getPrefabVideo(input.value));
});
}
//if user use a prefab img
String getPrefabImg(url) {
return "<img class='prefabImg' src='" + url + "'>";
}
//if user use a prefab video
String getPrefabVideo(url) {
return "<iframe class='prefabVideo'' src='" + url + "' frameborder='0' allowfullscreen></iframe>";
}
//reset input and add content
void addContent(value){
output.setInnerHtml(value + output.innerHtml);
input.value = null;
}
To create and inject HTML into the DOM without a NodeTreeSanitizer, you will need to switch from using HTML Strings to using Dart's Node objects. Picking a couple of functions from your code as an example, you can change them to something like;
ImageElement getPrefabImg(String url) {
return new ImageElement(src: url)..classes.add('prefabImage');
}
void addContent(Node node) {
output.nodes.insert(0, node);
input.value = null;
}
But with your current code you can easily add a NodeTreeSanitizer like so;
void addContent(String value) {
output.insertAdjacentHtml(
'afterBegin', value, treeSanitizer: NodeTreeSanitizer.trusted);
input.value = null;
}
I have problems with the Multi-Window Handling in my appliacation.
I currently using Conversation Scope to enable multi window / tab handling but in case the user opens a link (button) in a new tab the conversation is shared between the old and new tab.
Apache Deltaspike has a solution for that (http://deltaspike.apache.org/documentation/#_module_overview) but I already using Seam 3 (and JSF 2.1) and don't want to migrate to Deltaspike.
So I'm searching for an alternative solution without Deltaspike or is it possible to use Deltaspike AND Seam 3?
I build a solution with p:remoteCommand and this answer: In javascript, how can I uniquely identify one browser window from another which are under the same cookiedbased sessionId
I added this JS to my template which creates a unique id for each browswer tab an stores it in the window.name. Then it calls a p:remoteCommand to check the guid:
$(window).load(function() {
// ----------------------
var GUID = function() {
// ------------------
var S4 = function() {
return (Math.floor(Math.random() * 0x10000 /* 65536 */
).toString(16));
};
return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
};
if (!window.name.match(/^GUID-/)) {
window.name = "GUID-" + GUID();
}
if ($('#guid_form\\:server_guid').text().length == 0 ||
$('#guid_form\\:server_guid').text() != window.name) {
checkGuid([{name:'guid', value:window.name}]);
}
})
Added a Primefaces remoteCommand to my template which is called by the script above.
<h:form id="guid_form">
<h:outputText value="#{checkTabAction.guid}" id="server_guid"/>
<p:remoteCommand name="checkGuid" actionListener="#{checkTabAction.checkGuid}" process="#this" partialSubmit="true" />
</h:form>
And added a check action which validateds the current browser tab / window by comparing the guid's:
#ConversationScoped
#Named(value = "checkTabAction")
public class CheckTabAction implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private Logger log;
private String guid = null;
public void checkGuid() {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String guid = params.get("guid").toString();
if (this.guid == null) {
this.guid = guid;
}
if (!StringUtils.equals(this.guid, guid)) {
log.info("New tab detected!");
throw new NonexistentConversationException("New tab detected!");
}
}
public String getGuid() {
return guid;
}
}
Thanks in advance for your attention.
I've created a JSF 2.0 custom component to render Google GeoCharts. So, the renderer just writes some javascript and a div at the end.
The component works fine doing that. But my requirement is that the GeoChart should change its values using an ajax request. To do that, i'm using a primefaces command button putting in its attribute "update" the id of the geoChart.
The problem is that the GeoChart is not being updated when the comand button is clicked.
I'm using a maven archetype (myfaces-archetype-jsfcomponents20) allowing the automatic generation of some configuration files. So, I just need to write to classes and annotations. Those classes are the UIComponent and the Renderer.
I'm running this code in GlassFish 3.1 and Mojarra 2.1.6
The code of those classes:
UIComponent:
#JSFComponent(
name = "processum:geochart",
clazz = "org.processum.component.gchart.GoogleChart",
tagClass = "org.processum.component.gchart.GoogleChartTag")
abstract class AbstractGoogleChart extends UIComponentBase {
public static final String COMPONENT_TYPE = "org.processum.GoogleChart";
public static final String DEFAULT_RENDERER_TYPE = "org.processum.GoogleChartRenderer";
public static final String COMPONENT_FAMILY = "javax.faces.Output";
/**
*
* GoogleChartModel
*/
#JSFProperty
public abstract GoogleGeoChartModel getModel();
}
Renderer:
#JSFRenderer(
renderKitId = "HTML_BASIC",
family = "javax.faces.Output",
type = "org.processum.GoogleChartRenderer")
public class GoogleChartRenderer extends Renderer {
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
GoogleChart chart = (GoogleChart) component;
String divId = (String) component.getClientId() + "chartDiv";
GoogleGeoChartModel model = chart.getModel();
ResponseWriter writer = context.getResponseWriter();
writer.write("<script type='text/javascript'>"
+ "google.load('visualization', '1', {'packages': ['geochart']});"
+ "google.setOnLoadCallback(initGeoMap);"
+ "function initGeoMap() {"
+ "var data = google.visualization.arrayToDataTable(["
+ "[");
StringBuilder data = new StringBuilder();
for (String header : model.getHeaders()) {
data.append("\'").append(header).append("\'").append(",");
}
data.deleteCharAt(data.length() - 1).append("],");
for (String[] value : model.getValues()) {
data.append("[");
for (int i = 0; i < value.length; i++) {
if (i == 0) {
data.append("\'").append(value[i]).append("\'");
} else {
data.append(value[i]);
}
data.append(",");
}
data.deleteCharAt(data.length() - 1).append("],");
}
data.deleteCharAt(data.length() - 1);
writer.write(data.toString());
writer.write("]);");
writer.write("var options = {"
+ "region: 'CO',"
+ "displayMode: 'markers'"
+ "};"
+ "var chart = new google.visualization.GeoChart(document.getElementById(\'" + divId + "\'));"
+ "chart.draw(data, options);"
+ "};"
+ "</script>"
+ "<div id=\"" + divId + "\"/>");
}
}
XHTML:
<h:form>
<div id="anotherContent" style="width: 900px; height: 500px;">
<h:panelGroup id="chartCont" layout="block">
<processum:geochart id="chart" model="#{chartBackBean.model}"/>
</h:panelGroup>
</div>
<p:commandButton id="change" value="Cambio" actionListener="#{chartBackBean.change}" update="chart"/>
</h:form>
Some debugging:
The ajax request is triggered adequately. The managed bean updates the GoogleGeoChartModel with new values.
Next, the control flow goes to the Renderer which "encodes" the new values of the chart (that means that the encondeEnd is called again). But, this new "enconding" is not painted in the browser.
The html has two JSF components. My custom component and a h:panelGroup. If in the "update" attribute on primefaces button I put the id of the chart's div and then I trigger the ajax request, the chart does not change. But instead I put the id of the h:panelGroup, the div of the chart disappears.
I want to use to generate a pdf dataexporter, use the method preprocessor to insert some content. By giving the type letter size page assimilates well as formats of texts. Then make a page break to put the chart on a new page, right there is the problem that generates the second page with other size and also find a way to change the font size of the text of the exported table.
<h:commandLink>
<p:graphicImage value="/images/pdf.png"/>
<p:dataExporter type="pdf" target="dataTableAddDetalles" fileName="pdf" preProcessor="#{serviciosMB.preProcessPDF}"/>
</h:commandLink>
backing bean
public void preProcessPDF(Object document) throws Exception {
try {
Document pdf = (Document) document;
pdf.open();
pdf.setPageSize(PageSize.LETTER);
ServletContext servletContext = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
String logo = servletContext.getRealPath("") + File.separator + "images" + File.separator + "header.gif";
// pdf.add(Image.getInstance(logo));
pdf.add(new Paragraph("EMNI", FontFactory.getFont(FontFactory.HELVETICA, 22, Font.BOLD, new Color(0, 0, 0))));
SimpleDateFormat formato = new SimpleDateFormat("dd/MM/yyyy");
pdf.add(new Phrase("Fecha: " + formato.format(new Date())));
pdf.newPage();
} catch (Exception e) {
//JsfUtil.addErrorMessage(e, e.getMessage());
}
}
You can't do what you want using dataexporter, you need to change your code to:
<h:commandLink actionListener="#{serviciosMB.createPDF}">
<p:graphicImage value="/images/pdf.png" />
</h:commandLink>
And your managed bean:
public void createPDF() {
try { //catch better your exceptions, this is just an example
FacesContext context = FacesContext.getCurrentInstance();
Document document = new Document();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter.getInstance(document, baos);
if (!document.isOpen()) {
document.open();
}
PdfPTable pdfTable = exportPDFTable();
document.add(pdfTable);
//Keep modifying your pdf file (add pages and more)
document.close();
String fileName = "PDFFile";
writePDFToResponse(context.getExternalContext(), baos, fileName);
context.responseComplete();
} catch (Exception e) {
//e.printStackTrace();
}
}
exportPDFTable method:
private PdfPTable exportPDFTable() {
int numberOfColumns = 1;
itemOfList item = null;
PdfPTable pdfTable = new PdfPTable(numberOfColumns);
pdfTable.setWidthPercentage(100);
BaseFont helvetica = null;
try {
helvetica = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.EMBEDDED);
} catch (Exception e) {
//font exception
}
Font font = new Font(helvetica, 8, Font.NORMAL);
pdfTable.addCell(new Paragraph("columnName", font));
for (int i = 0; i < lstPdfTable.size(); i++) { //lstPdfTable is the list from your datatable. A List of "itemOfList" type
item = new itemOfList();
item = lstPdfTable.get(i);
//pdfTable.addCell(new Paragraph('any_string_field', font));
pdfTable.addCell(new Paragraph(item.getStringField(), font));
}
return pdfTable;
}
and writePDFToResponse method is:
private void writePDFToResponse(ExternalContext externalContext, ByteArrayOutputStream baos, String fileName) {
try {
externalContext.responseReset();
externalContext.setResponseContentType("application/pdf");
externalContext.setResponseHeader("Expires", "0");
externalContext.setResponseHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
externalContext.setResponseHeader("Pragma", "public");
externalContext.setResponseHeader("Content-disposition", "attachment;filename=" + fileName + ".pdf");
externalContext.setResponseContentLength(baos.size());
OutputStream out = externalContext.getResponseOutputStream();
baos.writeTo(out);
externalContext.responseFlushBuffer();
} catch (Exception e) {
//e.printStackTrace();
}
}
The primefaces documentation (as of 4.0) does not mention any ability to write a custom data exporter, only pre & post processors, which in the case of PDF prevents you from doing extensive modifications to data, etc.
But what you can do is create a package in your project called
org.primefaces.component.export
and copy ExporterFactory.java from primefaces source.
You can then replace the original PDFExporter call with your own implementation.
The exporter implementation is fairly simple. It uses iText library (although an outdated version) and you can easily extend it to your needs.
An obvious problem with this approach is that you may have to be extra careful when (and if) you are updating your primefaces library in the future.