留言板

Rerender alloy:dialog before showing

Ralf Haller,修改在7 年前。

Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi,

I am trying to rerender an alloy:dialog before showing it to the user. However I have not succeeded yet.

Here's what I tried:

- Using render="dialog" in alloy:command:button or f:ajax does not rerender the dialog
- Using render="@form" makes the dialog disappear after rerender (using autoShow didn't work either)
- Using FacesRequestContext.getCurrentInstance().addScript("Liferay.component('dialog').show();") doesn't do anything (alert('test') however works)

What am I doing wrong? How can I serenader the dialog?

Thanks in advance,

Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Hi Ralf,
Please post the shortest simplest example code for what you are trying to do. I think this problem should be easy to solve, but I need to see your code.

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

when using the SSCCE code the rerender seems to work, however the dialog is closed during the rerender.

Here ist my code:

dialog.xhtml
<!--?xml version="1.0"?-->

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:aui="http://liferay.com/faces/aui" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:portal="http://liferay.com/faces/portal" xmlns:alloy="http://liferay.com/faces/alloy" xmlns:up2date="http://kumasoft.de/up2date/jsf/ui">

	<alloy:head />
	<alloy:body>

		<!-- Example 3: Blocking usage with modal="true" -->
		<alloy:form id="form">
			<alloy:outputtext value="#{dialogBackingBean.date}" />
			<alloy:button>
				<alloy:outputtext value="Menu" />
				<alloy:menu>
					<alloy:commandlink onclick="Liferay.component('dialogKey3').show();" actionlistener="#{dialogBackingBean.selectText}" value="text1" action="#{dialogBackingBean.showDialog}" render="dialogKey3">
						<f:attribute name="text" value="text1" />
					</alloy:commandlink>
					<alloy:commandlink onclick="Liferay.component('dialogKey3').show();" actionlistener="#{dialogBackingBean.selectText}" value="text2" action="#{dialogBackingBean.showDialog}" render="dialogKey3">
						<f:attribute name="text" value="text2" />
					</alloy:commandlink>
				</alloy:menu>
			</alloy:button>
			<alloy:dialog clientkey="dialogKey3" id="dialogKey3" headertext="#{i18n['header-text']}" modal="true">
				<alloy:field label="#{i18n['text-field']}">
					<alloy:inputtext value="#{dialogModelBean.text}" />
				</alloy:field>
				<alloy:commandbutton action="#{dialogBackingBean.submit}" render="@form" value="#{i18n['submit']}" />
				<alloy:button onclick="Liferay.component('dialogKey3').hide();" value="#{i18n['cancel']}" />
			</alloy:dialog>
		</alloy:form>
	</alloy:body>
</f:view>


DialogBackingBean:
import java.util.Date;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.event.ActionEvent;

import com.liferay.faces.util.context.FacesRequestContext;
import com.liferay.faces.util.logging.Logger;
import com.liferay.faces.util.logging.LoggerFactory;

@ManagedBean
@RequestScoped
public class DialogBackingBean {

	private static final Logger logger = LoggerFactory.getLogger(DialogBackingBean.class);

	@ManagedProperty(name = "dialogModelBean", value = "#{dialogModelBean}")
	private DialogModelBean dialogModelBean;

	public void submit() {
		logger.info("submit: text = " + dialogModelBean.getText());
	}

	public void setDialogModelBean(DialogModelBean dialogModelBean) {
		this.dialogModelBean = dialogModelBean;
	}
	
	public void selectText(ActionEvent actionEvent) {
		String text = (String) actionEvent.getComponent().getAttributes().get("text");
		logger.info("Selected " + text);
		dialogModelBean.setText(text);
	}
	
	public void showDialog() {
		FacesRequestContext.getCurrentInstance().addScript("Liferay.component('dialogKey3').show();");		
	}
	
	public Date getDate() {
		return new Date();
	}
}


DialogModelBean:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@RequestScoped
@ManagedBean
public class DialogModelBean {
    private String text;
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
}
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Hi Ralf,

The problem is that you are showing the dialog and then rerendering it. The render causes the dialog to revert to its initial state: hidden.

One solution would be to render only the dialog's content (changes are in bold):

<alloy:form id="form">
<alloy:outputText value="#{dialogBackingBean.date}" />
<alloy:button>
<alloy:outputText value="Menu" />
<alloy:menu>
<!-- Note: I also removed onclick="Liferay.component('dialogKey3').show();" since
dialogBackingBean.showDialog has the same effect and occurs after rendering the dialog. -->

<alloy:commandLink
actionListener="#{dialogBackingBean.selectText}" value="text1"
action="#{dialogBackingBean.showDialog}"
render="dialogContent">
<f:attribute name="text" value="text1" />
</alloy:commandLink>
<alloy:commandLink
actionListener="#{dialogBackingBean.selectText}" value="text2"
action="#{dialogBackingBean.showDialog}"
render="dialogContent">
<f:attribute name="text" value="text2" />
</alloy:commandLink>
</alloy:menu>
</alloy:button>
<alloy:dialog clientKey="dialogKey3" id="dialogKey3"
headerText="#{i18n['header-text']}" modal="true">
<alloy:panelGroup id="dialogContent">
<alloy:field label="#{i18n['text-field']}">
<alloy:inputText value="#{dialogModelBean.text}" />
</alloy:field>
<alloy:commandButton action="#{dialogBackingBean.submit}"
render="@form" value="#{i18n['submit']}" />
<alloy:button onclick="Liferay.component('dialogKey3').hide();"
value="#{i18n['cancel']}" />
</alloy:panelGroup>
</alloy:dialog>
</alloy:form>


Alternatively, you could use alloy:dialog's autoShow attribute along with the rendered attribute to show and hide the dialog based on server-side values. However, that would require more changes to your code and would increase the amount work that needs to be done on the client-side for each ajax request since the dialog would need to be recreated on each render, so it is not ideal.

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

the first solution works, thanks. However action="#{dialogBackingBean.showDialog}" has no effect, so I have to use onclick="Liferay.component('dialogKey3').show();". Any ideas why?

BTW, how can I change the dialogs's header text using this solution?

Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Ralf Haller:
the first solution works, thanks. However action="#{dialogBackingBean.showDialog}" has no effect, so I have to use onclick="Liferay.component('dialogKey3').show();". Any ideas why?

Hmm, I'm not sure. It worked for me. Are you getting any error messages in the server logs or the browser?
Ralf Haller:
BTW, how can I change the dialogs's header text using this solution?

Well, if you follow the second approach outlined above, you could change the headerText in the view like any other value. However, with the first approach you will need to use JavaScript to change the text since the whole dialog is never rerendered:

Liferay.component('dialogKey3').set('headerContent', '<span class="alloy-overlay-title">changedHeaderText</span>');

With your specific example, I'd recommend doing something like this:

public void showDialog() {
FacesRequestContext.getCurrentInstance().addScript("Liferay.component('dialogKey3').set('headerContent', '<span class=\"alloy-overlay-title\">" + changedHeaderText + "</span>');Liferay.component('dialogKey3').show();");
}


- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
action="#{dialogBackingBean.showDialog}" is working now, it just was a typo on my end...

Thanks for your help, Kyle

Ralf
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

the rerender ist working now, however I discovered a strange effect that looks like a bug. When I change the size of the dialog, close it and open it again only the top of the dialog is rendered the rest remains white. Resizing the dialog renders then dialog again correctly. It looks like the height of modal-body is set to 0px. When disabling the height in the browser the dialog renders completely.

The same effect occurs when I initially set a width or height.

I tested with Safari and Firefox. Looks like a bug to me...

Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Hi Ralf,
I could not reproduce the resizing problem you mentioned in Firefox with the given example code. Please post more detailed information and any changes that you have made to the example code. The only issue I noticed with resizing the dialog is that the submit button causes it to return to its initial size due to render="@form". So you may want to change that to render="dialogContent". You could add the ids of other components that should be rendered as well. If you change render="@form" to render="dialogContent", you will also need to hide the dialog when the submit button is clicked.

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

when setting wight and height I can reproduce the effect with Safari and Firefox (47.0). Here ist my code:

dialog.xhtml
<!--?xml version="1.0"?-->

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:aui="http://liferay.com/faces/aui" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:portal="http://liferay.com/faces/portal" xmlns:alloy="http://liferay.com/faces/alloy" xmlns:up2date="http://kumasoft.de/up2date/jsf/ui">

	<alloy:head />
	<alloy:body>

		<!-- Example 3: Blocking usage with modal="true" -->
		<alloy:form id="form">
			<alloy:outputtext value="#{dialogBackingBean.date}" />
			<alloy:button>
				<alloy:outputtext value="Menu" />
				<alloy:menu>
					<alloy:commandlink actionlistener="#{dialogBackingBean.selectText}" value="text1" action="#{dialogBackingBean.showDialog}" render="dialogContent">
						<f:attribute name="text" value="text1" />
					</alloy:commandlink>
					<alloy:commandlink actionlistener="#{dialogBackingBean.selectText}" value="text2" action="#{dialogBackingBean.showDialog}" render="dialogContent">
						<f:attribute name="text" value="text2" />
					</alloy:commandlink>
				</alloy:menu>
			</alloy:button>
			<alloy:dialog clientkey="dialog" id="dialog" headertext="#{i18n['header-text']}" modal="true" width="500" height="500">
				<alloy:panelgroup id="dialogContent">
				<alloy:field label="#{i18n['text-field']}">
					<alloy:inputtext value="#{dialogModelBean.text}" />
				</alloy:field>
				<alloy:commandbutton action="#{dialogBackingBean.submit}" render="@form" value="#{i18n['submit']}" />
				<alloy:button onclick="Liferay.component('dialog').hide();" value="#{i18n['cancel']}" />
					</alloy:panelgroup>
			</alloy:dialog>
		</alloy:form>
	</alloy:body>
</f:view>


DoalogBackingBean.java:
import java.util.Date;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.event.ActionEvent;

import com.liferay.faces.util.context.FacesRequestContext;
import com.liferay.faces.util.logging.Logger;
import com.liferay.faces.util.logging.LoggerFactory;

@ManagedBean
@RequestScoped
public class DialogBackingBean {

	private static final Logger logger = LoggerFactory.getLogger(DialogBackingBean.class);

	@ManagedProperty(name = "dialogModelBean", value = "#{dialogModelBean}")
	private DialogModelBean dialogModelBean;

	public void submit() {
		logger.info("submit: text = " + dialogModelBean.getText());
	}

	public void setDialogModelBean(DialogModelBean dialogModelBean) {
		this.dialogModelBean = dialogModelBean;
	}
	
	public void selectText(ActionEvent actionEvent) {
		String text = (String) actionEvent.getComponent().getAttributes().get("text");
		logger.info("Selected " + text);
		dialogModelBean.setText(text);
	}
	
	public void showDialog() {
		FacesRequestContext.getCurrentInstance().addScript("Liferay.component('dialog').show();");		
	}
	
	public Date getDate() {
		return new Date();
	}
}


DialogModelBean.java:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@RequestScoped
@ManagedBean
public class DialogModelBean {
    private String text;
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
}


Here is a screen dump.

Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Hi Ralf,
The bug you are running into is FACES-2394. I had completely forgotten about this issue, but I should have realized this when you said: "[T]he height of modal-body is set to 0px." The issue has already been fixed and alloy:dialog will behave correctly in the next release of Liferay Faces Alloy.

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

is there a workaround for initially setting the dialog's size?

Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Hi Ralf,
The code for the fix is here. You could probably just copy that method into a JS file and call it right after you create your dialog:

<alloy:dialog clientKey="dialog"
<!-- your code here... -->
</alloy:dialog>
<alloy:script target="body">
initDialog(Liferay.component('dialog'));
</alloy:script>


I haven't tested the above code, but try it let us know if it works.

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

I'm getting a JavaScript error in the browser: ReferenceError: Can't find variable: initDialog

Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Hi Ralf,
That's because you need to copy the initDialog() method into your xhtml or into a JS resource in order to use it.

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

now I'm getting: TypeError: undefined is not an object (evaluating 'c.get'), I think it's the command dialog.get('boundingBox') that fails.

	<alloy:outputscript target="body">
		initDialog(Liferay.component('orderDialog'));
	</alloy:outputscript>

	<alloy:outputscript target="body">
		function initDialog(dialog) {

			var boundingBox = dialog.get('boundingBox'),
				boundingBoxEscapedClientId = LFA.escapeClientId(boundingBox.get('id')),
				modalBody = boundingBox.one('#' + boundingBoxEscapedClientId + '_contentBox &gt; .modal-body'),
				onceAfterVisibleChangeEventHandle = dialog.after('visibleChange', function(event) {
	
				if (event.newVal) {
	
					var height = boundingBox.getComputedStyle('height'),
					width = boundingBox.getComputedStyle('width');
	
					boundingBox.setStyle('top', '50%');
					boundingBox.setStyle('left', '50%');
					boundingBox.setStyle('margin-top', LFAI.getCenteredMargin(height));
					boundingBox.setStyle('margin-left', LFAI.getCenteredMargin(width));
	
					// Fix FACES-2394 by setting height to 100% (instead of 0px) and unsetting max-height, so that modal
					// content fills the dialog's body.
					modalBody.setStyle('height', '100%');
					modalBody.setStyle('max-height', null);
					onceAfterVisibleChangeEventHandle.detach();
				}
			});
		}
	</alloy:outputscript>


Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Try:

<alloy:outputScript target="body" use="aui-modal">
initDialog(Liferay.component('orderDialog'));
</alloy:outputScript>


Also make sure your alloy:dialog is defined before the above script is called.

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

an again one step further:

TypeError: LFAI.getCenteredMargin is not a function. (In 'LFAI.getCenteredMargin(f)', 'LFAI.getCenteredMargin' is undefined)

Ralf
thumbnail
Kyle Joseph Stiemann,修改在7 年前。

RE: Rerender alloy:dialog before showing

Liferay Master 帖子: 760 加入日期: 13-1-14 最近的帖子
Hi Ralf,
You need to add the getCenteredMargin() function to your xhtml:

function getCenteredMargin(pixels) {
return (-1 * (parseInt(pixels, 10)/2.0)) + 'px';
}


You can also remove the LFAI. prefix from the LFAI.getCenteredMargin() function call in initDialog().

- Kyle
Ralf Haller,修改在7 年前。

RE: Rerender alloy:dialog before showing

Junior Member 帖子: 54 加入日期: 13-5-13 最近的帖子
Hi Kyle,

after some tweaking I found a solution that works as expected:

<alloy:dialog clientkey="orderDialog" id="orderDialog" headertext="Bestellung" modal="true" width="90%" height="90%"></alloy:dialog>


	<alloy:outputscript target="body">
		function getCenteredMargin(pixels) {
			return (-1 * (parseInt(pixels, 10)/2.0)) + 'px';
		}

		function initDialog(dialog) {

			var boundingBox = dialog.get('boundingBox'),
				boundingBoxEscapedClientId = LFA.escapeClientId(boundingBox.get('id')),
				modalBody = boundingBox.one('#' + boundingBoxEscapedClientId + '_contentBox &gt; .modal-body'),
				onceAfterVisibleChangeEventHandle = dialog.after('visibleChange', function(event) {

				if (event.newVal) {
	
					var height = boundingBox.getComputedStyle('height'),
					width = boundingBox.getComputedStyle('width');
	
					boundingBox.setStyle('top', '50%');
					boundingBox.setStyle('left', '50%');
					boundingBox.setStyle('margin-top', getCenteredMargin(height));
					boundingBox.setStyle('margin-left', getCenteredMargin(width));

					// Fix FACES-2394 by setting height to 100% (instead of 0px) and unsetting max-height, so that modal
					// content fills the dialog's body.
					modalBody.setStyle('height', '100%');
					modalBody.setStyle('max-height', '100%');
					onceAfterVisibleChangeEventHandle.detach();
				}
			});
		}
	</alloy:outputscript>


Thanks for your help...

Ralf