PrimeFaces openDialog not working properly within nested dialogs - primefaces

I’m using PrimeFaces 6.0 and I’ve found a problem using nested dialogs. RequestContext.openDialog is not working properly. It doesn’t throw any exception, but it doesn’t open the dialog.
I built 5 pages (P1 to P5) based on the same Full Page Layout. Each page contains a p:dataTable. The p:dataTable has a p:column with a p:commandButton to open the next page in a new dialog. This is what I’ve found: on some of those pages the button of the first row doesn’t work; the buttons of the rest of the rows work properly.
The problem doesn’t seem to be inherent to the row data. When the button of the first row fails, it fails regardless the row that is being showed there. Rows can be sorted in different ways (so the first one would vary) and the button of the first row will continue failing and the rest of the buttons will continue working. The problem doesn’t seem to be inherent to the page either. All buttons work properly when the page is root (the one that opens the first dialog). The problem only happens within a dialog.
This is the button:
<p:commandButton
icon="fa fa-folder-open"
action="#{ambientePrueba11.openDialog(currentRow)}"
partialSubmit="true"
process="#this"
update="#none">
<p:ajax
event="dialogReturn"
listener="#{ambientePrueba11.onDialogReturn}"
update="dataTable"/>
</p:commandButton>
This is the code in the backing bean (each of the 5 beans have a different outcome, but the rest of the code is the same):
public String openDialog(AmbientePrueba row) {
EventLogger.log(this, "openDialog", getDenominacion(row));
Object identificacion = getIdentificacion(row);
String key = "PaquetePrueba11";
String outcome = FacesUtils.getPageKeyFacesOutcome(key);
Map<String, Object> options = new HashMap<>();
options.put("modal", true);
options.put("resizable", true);
options.put("draggable", true);
options.put("width", 1260);
options.put("height", 860);
options.put("contentWidth", "100%");
options.put("contentHeight", "100%");
options.put("closable", true);
options.put("includeViewParams", true);
options.put("minimizable", true);
options.put("maximizable", true);
Map<String, List<String>> params = new HashMap<>();
params.put(CPP.ID_RECURSO, toList(identificacion));
params.put(CPP.ID_RECURSO_MAESTRO, toList(identificacion));
params.put(Global.PARAMETRO_FRAMEWORK_SESION, toList(getSessionFrame()));
params.put(Global.PARAMETRO_CONDICION_SESION, toList(MODAL));
RequestContext.getCurrentInstance().openDialog(outcome, options, params);
return null;
}
private List<String> toList(Object value) {
List<String> paramValue = new ArrayList<>();
paramValue.add(value + "");
return paramValue;
}
public void onDialogReturn(SelectEvent event) {
Object response = event.getObject();
facesLogger.info(response + "");
}
Has anybody else found a similar problem? Any help to solve or workaround this problem will be very much appreciated.

After some more tests I found a work around. I just gave the button a different id in each page and now all the buttons of all the pages work just fine.
Now the button of page P1 (as its id suggests) looks like this:
<p:commandButton
id=buttonOfPage1
icon="fa fa-folder-open"
action="#{ambientePrueba11.openDialog(currentRow)}"
partialSubmit="true"
process="#this"
update="#none">
<p:ajax
event="dialogReturn"
listener="#{ambientePrueba11.onDialogReturn}"
update="dataTable"/>
</p:commandButton>

Related

Blazor - iterate through EditForm

I am building a (static) website in Blazor.wasm where the users upload some number of files. My intention is then (after all the files have passed some basic checks) to iteratively present a set of fields which the users are asked to complete. Only after they have submitted all the [Required] information and press submit will the next form show up.
I have included a minimal example below.
if (valid_files == numFiles)
{
for (int counter = 0; counter < num_files; counter++)
{
paramList.Add(new ParamsForm { });
<EditForm Model="#paramList[counter]" OnValidSubmit="#SingleSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
Camera type <br>
<InputText id="cameratype" #bind-Value="#paramList[counter].CameraType" />
</p>
<button type="submit">Submit</button>
</EditForm>
}
<button #onclick="HandleValidSubmit">Upload Data </button>
}
The expected behaviour is that on each iteration, a frech instance of the onbject ParamsForm is added to the list. We then create a form based of that instance and wait for the user to complete the form. Once they press the Submit button the next stage of the for loop begins. Once all the data have been submitted and the for loop is completed, the Upload data button should appear and the users are invited to submit all their data to the server.
Instead, none of the code inside the EditForm ... section is being completed. I.e. - I see no popping up of text boxes, and any code that I put in there (for example #Console.WriteLine("This should show up) does not seem to be executed. The Upload data button does not appear and instead an error is thrown complaining that the index is out of range, which is weird because after the code at the top of the for loop there are no longer any elements being accessed by an index.
I am quite new to interacting between c# and HTML, so I think I can appreciate why what I have shouldn't work, but I don't know how I can go about writing something that will work.
Any advice would be gratefully recieved.
The ways of Blazor are a bit mysterious to me, too-- it takes a while to adjust! I do know that Blazor has an OnAfterRender event, which makes me think that it might not like to have user input in a loop like that. Or it may be that it's enumerating if (valid_files == numFiles) as false because those variables aren't initialized yet when the markup first renders.
I'd try two things:
(1) Throw StateHasChanged() at the end of your loop or after the code that sets valid_files and numFiles and see if that does anything you like.
(2) Probably this anyway: instead of looping in the markup, I'd build the entire List<ParamsForm> paramsList in the FileInput's event handler instead, move the counter to the code block, and add counter++ to the end of the SingleSubmit() method.
It's 5:00 am here, just got up to get a snack and going back to bed. Let me know if things still don't fly, and I'll try a more complete example tomorrow. :D
I don't have much information about your class, where you are getting your file list from, and so on. I recommend passing complete objects rather than individual properties. For example, I'd rather have IBrowserFile File {get; set;} in my ParamsForm class than say string FileName. That way, if I decide-- oh, I want to get this or that property-- it's already there.
Anyway, hope something in here might be useful:
#if (CurrentForm is not null)
{
<EditForm Model="CurrentForm" OnValidSubmit="#SingleSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<p>
Camera type <br>
<InputText id="cameratype" #bind-Value="CurrentForm.CameraType" />
</p>
<button type="submit">Submit</button>
</EditForm>
#if (IsComplete) // Don't show upload button until you're done
{
<button #onclick="DoUploads">Upload Data </button>
}
#DisplayMessage
}
#code {
class ParamsForm { public string FileName; public string CameraType; } // Just a placeholder
List<ParamsForm> ParamsList = new List<ParamsForm>();
ParamsForm CurrentForm { get; set; }
int counter = 0;
List<string> FileNames;
bool IsComplete = false;
string DisplayMessage = "";
void InitializeForms()
{
// I don't know your class, so just an example
foreach (var item in FileNames)
{
bool IsValid = false;
// check file validity
if (IsValid) ParamsList.Add(new ParamsForm() { FileName = item });
}
if(ParamsList.Count > 0)
CurrentForm = ParamsList[0];
}
void SingleSubmit()
{
// Do stuff with CurrentForm
if (++counter >= ParamsList.Count) IsComplete = true;
else CurrentForm = ParamsList[counter];
}
async Task DoUploads()
{
// Do stuff with your ParamsList
int UploadCounter = 0;
foreach (ParamsForm item in ParamsList){
DisplayMessage = "Uploading " + UploadCounter + " of " + ParamsList.Count;
StateHasChanged();
// Do the Upload;
}
DisplayMessage = "Finished.";
}
}

Labels for tooltips on primefaces barchartseries

I have tried to search both the forum and Google extensively, but I have problems understanding how I should make this work:
PrimeFaces6
I have a BarchartModel based on the tutorial in the ShowCase:
CODE: SELECT ALL
private BarChartModel initStatusBarChart() {
BarChartModel model = new BarChartModel();
ChartSeries statusMessages = new ChartSeries();
statusMessages.setLabel("Label"));
statusMessages.set("Some String 1", list1.size());
statusMessages.set("Some String 2", list2.size());
model.addSeries(statusMessages);
return model;
}
The issue is that on render, I get tooltips the format of
"1, 515" and "2, 432", where 515 and 432 are the sizes of list1 and list2, respectively.
How can I replace 1 and 2 with the values "Some String" 1 and 2 ? Have tried extending highlighter and using dataTipFormat, with no success.
I solved the problem using the datatip editor of the chart model (with Primefaces 6.1, by the way). I used this for a stacked bar chart.
I needed to apply this solution at two places: the backing bean and the JSF page.
In the backing bean I had to set a JavaScript function name this way:
barModel.setDatatipEditor("chartDatatipEditor");
I tried to set it using the corresponding tag attribute in the JSF page but to no effect.
In the JSF I inserted this JavaScript code:
<script type="text/javascript">
function chartDatatipEditor(str, seriesIndex, pointIndex, plot) {
//console.log('chartDatatipEditor: '+str+" - "+seriesIndex+" - "+pointIndex);
var point = seriesIndex+','+pointIndex;
#{bean.datatipsJs}
}
</script>
This JS function gets the chart coordinates as parameters. I concat them so that the following JS code gets easier.
seriesIndex is the index of the chart series. pointIndex is the index on the X scale of the diagram.
To find out what are the correct values for your chart you can uncomment the console.log line above.
The inserted JS code is constructed in the backing bean this way:
private Map<String, String> chartDatatips;
public String getDatatipsJs() {
StringBuilder sb = new StringBuilder("switch ( point ) {\n");
for (String point : chartDatatips.keySet()) {
sb.append("case '").append(point).append("': return '").append(chartDatatips.get(point)).append("'; break;\n");
}
sb.append("default: return 'Unknown point'; break; }");
return sb.toString();
}
The map chartDatatips has the coordinate point as key (e.g., "2,1") and the tooltip as value.
During the chart setup you obviously have to fill this map with useful details ;-)
Like this:
chartDatatips.put("2,5", "Label ...");
...
Hope this helps, if you didn't already solved this.
~Alex
Based on Alex's answer I have come up with this. Only requiring javascript - it displays the label and value:
In the backing bean, set a JavaScript function name this way:
barModel.setDatatipEditor("chartDatatipEditor");
In the HTML file:
function chartDatatipEditor(str, seriesIndex, pointIndex, plot) {
return plot.series[seriesIndex].label + ' - ' + plot.data[seriesIndex][pointIndex];
}

How to keep filter and current page when updating PrimeFaces dataTable

I have a data table which has paginator and a filter. When the table is filtered and not at the first page and I try to remove a row, I want to keep both the filter and the current page. So I tried something like this:
try {
List<Vector> filteredData = incomeTable.getFilteredValue();
Map<String, Object> filterValue = incomeTable.getFilters();
if (filteredData == null) {
filteredData = lstData;
}
int index = filteredData.indexOf(selectedRow);
lstData.remove(selectedRow);
filteredData.remove(selectedRow);
if (filteredData.size() > index) {
selectedRow = filteredData.get(index);
} else {
selectedRow = filteredData.get(index - 1);
}
onRowSelect();
incomeTable.setFilteredValue(filteredData);
incomeTable.setFilters(filterValue);
incomeTable.setFirst(getFirstRecordShow(filteredData));
} catch (Exception e) {
reportException(e);
}
After this function is processed, I update the table on the client side:
update="#([id$=incomeTable])"
I was able to keep the current page, list of filtered data and display the selected row correctly. But the filter I used on the header rows was cleared. I already tried
incomeTable.setFilters(filterValue);
to set the value again, but it's still not working.
Does anyone know how to keep both filter and the current page in this case?
My PrimeFaces version is 5.3.
After some testing, the result turn out to be rather simple.
Just like toggle column visible while using paging (using boolean[]), create a String[] which will hold all the filter value
private String[] colVisible = new String[] {"", "", "", "", ...};
Add filterValue attribute to every column:
<p:column headerText="Header text"
filterBy="#{item[1]}" filterMatchMode="contains"
visible="#{bean.colVisible[1]}"
filterValue="#{bean.filterValue[1]}">
<h:outputText value="#{item[1]}" />
</p:column>
This way the filter value will be keep even after update. Then when i need to clear filter, i just need to reset all of them back to blank.

Primefaces: Growl is displayed without the icon

When growl is displayed (in the example below, showing the message 'Chart updated') the icon does not appear. There are no error messages in the console. This happens in a single page, the rest of the application works fine. What could be wrong?
UPDATE: the page is loaded as an iframe
This is how the growl is declared:
<p:growl life="2000" id="messages" for="msg1" showDetail="true"/>
and the java:
FacesContext context = FacesContext.getCurrentInstance();
FacesMessage fm = new FacesMessage("Chart updated", "");
context.addMessage("msg1", fm);
You need to add the Severity of your FacesMessage. You can use the following constructor:
FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_INFO,
"Message header", "Message detail")
Cheers.

Open primefaces tagcloud links in a new window

I'm using primefaces tagcloud. But when i clicked on the links given on the tagcloud, it opened in that same window.But on clicking the tagcloud links it should open in a new window not on the same window.
I want a new window will open when i click on the tagcloud links.
Can anyone please help me about this topics ???
JavaScript solution:
One possible approach could be JavaScript:
<p:tagCloud model="#{tagCloudBean.model}" onclick="preventDefault(event)">
<p:ajax event="select" update="msg" listener="#{tagCloudBean.onSelect}" />
</p:tagCloud>
onclick triggers a JavaScript function which prevents the link from getting opened.
function preventDefault(e) {
e.preventDefault();
}
Also, by adding an ajax event you can call a listener in your managed bean.
public void onSelect(SelectEvent event) {
TagCloudItem item = (TagCloudItem) event.getObject();
String url = item.getUrl();
String script = "window.open('" + url + "');"
RequestContext.getCurrentInstance().execute(script);
}
There you can access the url of the TagCloudItem and execute a JavaScript statement which should open a new browser window.
This possible solution is untested, but just give it a try.