Uploaded image for project: 'PUBLIC - Liferay Faces'
  1. PUBLIC - Liferay Faces
  2. FACES-3175

Navigating to page with the same resources but different portlet mode, window state, portlet id, or path via SPA causes certain components to fail

    Details

      Description

      Steps to reproduce:

      1. Replace the contents of the primefaces-applicant-portlet portletViewMode.xhtml h:body tag with the following:
         <p:schedule value="#{applicantModelBean.scheduleModel}" />
        
      1. Replace the contents of the primefaces-applicant-portlet portletEditMode.xhtml h:body tag with the following:
         <p:outputPanel>Test</p:outputPanel>
        
      1. Add the following to ApplicantModelBean.java:
         private ScheduleModel scheduleModel;
         public ScheduleModel getScheduleModel() {
             if (scheduleModel == null) {
                 scheduleModel = new DefaultScheduleModel();
             }
             return scheduleModel;
         }
         public void setScheduleModel(ScheduleModel scheduleModel) {
             this.scheduleModel = scheduleModel;
         }
        
      1. Deploy the portlet and navigate to it.
      2. Click the Preferences link to navigate to Edit mode.
      3. Click the back link to navigate back to View mode.

      If the bug still exists, p:schedule 's markup will not appear and the following error will appear in the logs:

      TypeError: this.jqc.fullCalendar is not a function

      If the bug is fixed, no error will appear in the logs, and the portlet will display p:schedule 's markup.

      Explanation

      This issue occurs because SennaJS reloads jquery.js when the portlet navigates to Edit mode. SennaJS reloads jquery.js (rather than simply allowing the already loaded jQuery instance to be used) because the URL for jquery.js is different in Edit mode (p_p_mode=edit) and in View mode (p_p_mode=view), so SennaJS cannot tell that both resources are the same. The same problem occurs when navigating between window states with SennaJS due to the different p_p_state parameters and navigating between portlets or pages due to the different p_p_id parameters and different paths. Any time the resource URL is even slightly different, SennaJS will reload it and that can cause many issues especially with PrimeFaces.

      Potential Solutions

      1. Provide an option to set the cacheability of resource URLs on a portlet-wide basis (and potentially default to FULL). This would allow developers to set the cacheability to FULL which would cause the URLs to be rendered without the portlet mode, portlet state, or render parameters included in the URL. Then any URLs which request the same resource from the same portlet would be the same and SennaJS would avoid loading them multiple times.
      2. Provide an option (or make it the default and provide an option to disable) to default resource URLs to use friendly URLs (which only include the {instanceId} of the portlet and the resource library and name). This solution is similar to the above in that it simply helps strip unnecessary information off the resource URLs.
      3. Provide an id for each resource which includes the resource id. For example, #{resource['library:name']} would render id="library:name". This solution would also potentially improve navigation between JSF portlet via SennaJS since SennaJS would be able to detect duplicate resources across portlets. However, this could potentially cause issues as well if two different resources (in two different portlets) happen to have the same id but are actually different resources (seems unlikely though).

      Workarounds

      1. The simplest and most robust workaround is to simply implement solution #2 in your portlet by adding friendly resource URLs which exclude the portlet mode, state and other unnecessary parameters:
        <route>
            <!-- pattern cannot include {javax.faces.resource} or {ln} due to FACES-3176 -->
            <pattern>/{instanceId}</pattern> <!-- if your portlet is instanceable=false, then you can remove the {instanceId} from this pattern -->
            <ignored-parameter name="p_p_id" />
            <ignored-parameter name="p_p_state" />
            <ignored-parameter name="p_p_mode" />
            <ignored-parameter name="p_p_cacheability" />
            <ignored-parameter name="p_p_col_id" />
            <ignored-parameter name="p_p_col_count" />
            <implicit-parameter name="p_p_lifecycle">2</implicit-parameter>
        </route>
        
      2. Another workaround is to save off any of the functionality or data before navigating and restore that functionality or data after navigation. Here's an example for the case of p:schedule:
        <h:outputScript target="body">
            $(function() {
                Liferay.on('beforeNavigate', function() {
                    window.pf_schedule_fullCalendar = $.fullCalendar;
                });
        
                Liferay.on('endNavigate', function() {
        
                    if (!$.fullCalendar) {
                        $.fullCalendar = window.pf_schedule_fullCalendar;
                    }
                });
            });
        </h:outputScript>
        

        Attachments

          Activity

            People

            • Assignee:
              kyle.stiemann Kyle Stiemann (Inactive)
              Reporter:
              kyle.stiemann Kyle Stiemann (Inactive)
              Participants of an Issue:
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Packages

                Version Package
                bridge-ext-5.0.3
                bridge-impl-4.1.2
                portal-3.0.3