Friday, 9 November 2018

XPages execMode partial removes DOM elements when it's put in rendered

I have a fileUploader on one of my XPages. The problem which I have encountered is when I try to use it with xp.this.rendered property it actually REMOVES a DOM element which it's supposed to update. Without the property it works so well, but sometimes I have to display the fileUploader only when some condition is true:

Here's what I mean:enter image description here

What I did here:

  1. Opened a page with step №1 (default). It's just a table in the main div_main element (input_step is 1)
  2. Uploaded a file there
  3. Cliked the Next step button
  4. It refreshed div_main element and set input_step component value to 2
  5. The 2nd table is displayed (the rendered condition is rendered="#{javascript:getComponent('input_step').getValue()=='2'}")
  6. I tried to upload a file to the second table
  7. Then there's the code which triggers the refresh button (I'll post it below)
  8. It does upload file to the server, but removes the DOM element it's supposed to refresh and execute on
  9. Then just for experement I clicked the "Previous step" which refreshs the entire div_main
  10. BUT! Instead of refreshing div_main it doesn't do anything! It only refreshes itself, but doesn't go into the code which sets input_step back to 1.
  11. When I click again on the "Previous step" it goes back to the first step it works as it should

I absolutely have no IDEA why this happens. It's really irritating I have never come across such an eerie problem in my life.

Here's the div_main

 <xp:div styleClass="doc_list" id="div_main">

        <xp:table style="width:100.0%">
            <xp:tr>
                <xp:td style="width:25.0%" align="center" valign="top"
                    styleClass="background">


                </xp:td>
                <xp:td id="content" styleClass="background_field">

                    <xp:table id="table_nav" style="width:100.0%">
                        <xp:tr>
                            <xp:td style="width:100.0%" align="center"
                                styleClass="background_field">



                                <xp:label id="label152"
                                    styleClass="doc_header_step_title">
<xp:this.value><![CDATA[#{javascript:var step=getComponent('input_step').getValue()

switch (step) {

case "1":
  return('Step1')
  break;
case "2":
  return('Step2')
  break;
case "3":
  return('Step3')
  break;
case "4":
  return('Step4')
  break;
case "":

  return('Step1')
  break;

}}]]></xp:this.value>
                                </xp:label>
                            </xp:td>
                        </xp:tr>
                    </xp:table>

                    <xc:User_request_new_step_1></xc:User_request_new_step_1>

                    <xc:User_request_new_step_2></xc:User_request_new_step_2>

                    <!-- <xc:User_request_step_3></xc:User_request_step_3> -->

                    <xp:table id="table_step4" style="width:100.0%"
                        rendered="#{javascript:getComponent('input_step').getValue()=='4'}">
                        <xp:tr>
                            <xp:td style="width:50.0%" align="center">
                                <xc:Doc2_Tree_Structure
                                    syselem="gecho_directory">
                                </xc:Doc2_Tree_Structure>
                            </xp:td>
                        </xp:tr>
                    </xp:table>
                    <xp:table id="table_step5" style="width:100.0%"
                        rendered="false">
                        <xp:tr id="tr_sign">
                            <xp:td id="td_sign">
                                <xc:Event2_cryptopro></xc:Event2_cryptopro>
                            </xp:td>
                        </xp:tr>
                        <xp:tr id="tr_sign_button">
                            <xp:td id="td_sign_button" align="center">
                                <xp:inputHidden id="gechoSign"
                                    value="#{doc_source.gechoSign}">
                                </xp:inputHidden>
                                <xp:table style="width:1.0%">
                                    <xp:tr>
                                        <xp:td style="width:1.0%"
                                            styleClass="doc_field_select" id="td10">
                                            <xp:text
                                                id="cf_create_button"
                                                value="#{javascript:return('Sign')}" escape="false">
                                            </xp:text>
                                            <xp:eventHandler
                                                event="onclick" submit="true" refreshMode="partial"
                                                refreshId="table_step5">
                                                <xp:this.action><![CDATA[#{javascript:getComponent('inputBase64').setValue(generateSignData(doc_source.getDocument()));

var tprint=getComponent('inputThumbprint').getValue()
var base64id=getComponent('inputBase64').getClientId(facesContext)
var resid=getComponent('gechoSign').getClientId(facesContext)

var csjs="signCryptoPro('"+tprint+"','"+base64id+"','"+resid+"')"
//print(csjs)
view.postScript(csjs)}]]></xp:this.action>
                                            </xp:eventHandler>
                                        </xp:td>
                                    </xp:tr>
                                </xp:table>
                            </xp:td>
                        </xp:tr>
                    </xp:table>
                    <xp:table id="table_nav_bottom"
                        style="width:100.0%">
                        <xp:tr>
                            <xp:td style="width:25.0%" align="right">
                                <xp:table>
                                    <xp:tr>
                                        <xp:td
                                            styleClass="doc_field_select" id="td1">
                                            <xp:this.rendered><![CDATA[#{javascript:var step=getComponent('input_step').getValue()

if (step=="" | step=="1") {return(false)}
return(true)}]]></xp:this.rendered>
                                            <xp:text escape="false"
                                                id="computedField1"
                                                value="#{javascript:return(texticon('arrow-31-left',20,20,'Previous step',false))}">
                                            </xp:text>

                                            <xp:eventHandler
                                                event="onclick" submit="true" refreshMode="partial"
                                                refreshId="div_main" disableValidators="true">
                                                <xp:this.action><![CDATA[#{javascript:var step=getComponent('input_step').getValue()
print("step value before is " + getComponent('input_step').getValue());
switch (step) {

case "1":
    getComponent('input_step').setValue('1')
    break;
case "2":
    getComponent('input_step').setValue('1')
    break;
case "3":
    getComponent('input_step').setValue('2')
    break;
case "4":
    getComponent('input_step').setValue('3')
    break;
case "":
    getComponent('input_step').setValue('1') 
    break;


}
print("step value after is " + getComponent('input_step').getValue());
}]]></xp:this.action>
                                            </xp:eventHandler>
                                        </xp:td>
                                    </xp:tr>
                                </xp:table>
                            </xp:td>
                            <xp:td style="width:50.0%">

                            </xp:td>
                            <xp:td style="width:25.0%" align="left">
                                <xp:table>
                                    <xp:this.rendered><![CDATA[#{javascript:var step=getComponent('input_step').getValue()
if (step=="" | step=="4") {return(false)}
return(true)
}]]></xp:this.rendered>
                                    <xp:tr>
                                        <xp:td
                                            styleClass="doc_field_select" id="NextStep">
                                            <xp:text escape="false"
                                                id="computedField2">
                                                <xp:this.value><![CDATA[#{javascript:var step=getComponent('input_step').getValue()

if (step=="" | step=="4") {return(texticon('check-mark-5-icon',20,20,'Save',false))}
return(texticon('arrow-31',20,20,'Next step',false))

}]]></xp:this.value>
                                            </xp:text>

                                            <xp:eventHandler
                                                event="onclick" submit="true" refreshMode="partial"
                                                refreshId="div_main">
                                                <xp:this.action><![CDATA[#{javascript:var step=getComponent('input_step').getValue()

switch (step) {

case "1":
    getComponent('input_step').setValue('2')
    break;
case "2":    
    getComponent('input_step').setValue('3')
    break;
case "3":
    getComponent('input_step').setValue('4')
    break;
case "4":
    getComponent('input_step').setValue('4')
    break;
case "":
    getComponent('input_step').setValue('4')
    break;

}}]]></xp:this.action>

                                                <xp:this.script><![CDATA[var files='#{id:Files_from_pers_files_repeat}'
var hidden='#{id:inputText64}'

if (!!document.getElementById(files)){  
    var filesHtml=document.getElementById(files).innerHTML
    if( filesHtml !=="" && filesHtml!=="\n" ){
        document.getElementById(hidden).value="file"
    }else{
        document.getElementById(hidden).value=""
    }
}]]></xp:this.script>
                                            </xp:eventHandler>
                                        </xp:td>
                                    </xp:tr>
                                </xp:table>
                            </xp:td>
                        </xp:tr>
                    </xp:table>

                </xp:td>
                <xp:td style="width:15.0%" styleClass="background">

                </xp:td>
            </xp:tr>
        </xp:table>
        <xp:div styleClass="navigation_step">
            <xp:table style="width:100.0%">
                <xp:tr>
                    <xp:td style="width:15.0%;white-space:nowrap;"
                        styleClass="background">
                        <xp:inputHidden id="input_step" defaultValue="1"
                            value="#{doc_source.InputStep}">

                        </xp:inputHidden>
                        <xc:Field2_select_nav_readonly resultid="input_step"
                            refreshid="div_main" multiselect="false" icon="checkbox-12-icon"
                            icon_deselected="checkbox-19-icon">
                            <xc:this.valueslist><![CDATA[#{javascript:var arr = new Array();
arr.push('Purpose|1')
arr.push('Client|2')
arr.push('Conditions|3')
arr.push('Documentation|4')
//arr.push('Signing|5')
return(arr)}]]></xc:this.valueslist>



                        </xc:Field2_select_nav_readonly>
                    </xp:td>
                    <xp:td style="width:85.0%"></xp:td>
                </xp:tr>
            </xp:table>
        </xp:div>
    </xp:div>

Here's the fileUploader itself:

<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:this.resources>
        <xp:script src="/fileUploader.js" clientSide="true">
        </xp:script>
    </xp:this.resources>

    <xp:div id="${javascript:compositeData.ID+'refresh'}">
        <xp:messages id="messages1"></xp:messages> 
        <!-- <xp:   message id="message1" for="${javascript:compositeData.ID+'refresh'}"></xp:message>  -->
        <xp:table>
            <xp:this.rendered><![CDATA[#{javascript:currentDocument.isEditable() && (!context.getUserAgent().isIE(6,9))
}]]></xp:this.rendered>
            <xp:tr>
                <xp:td id="td1" styleClass="doc_field_select">
                    <xp:text escape="false" id="cf_add">


                        <xp:this.value><![CDATA[#{javascript:return(texticon('plus-5-icon',25,25,'Add',false))

}]]></xp:this.value>

                    </xp:text>
                    <xp:eventHandler event="onclick" submit="false"
                        disableValidators="true">
                        <xp:this.script><![CDATA[document.getElementById("#{javascript:compositeData.ID+'_files_input'}").click();]]></xp:this.script>

                    </xp:eventHandler>
                </xp:td>
                <xp:td styleClass="doc_field_select" id="td2">
                    <xp:text escape="false" id="cf_deleteall">
                        <xp:this.value><![CDATA[#{javascript:return(texticon('x-mark-4-icon',25,25,'Delete all',false))
        }]]></xp:this.value>

                    </xp:text>
                    <xp:eventHandler event="onclick" submit="true"
                        refreshMode="complete" disableValidators="true">
                        <xp:this.script><![CDATA[var id='#{javascript:
getClientId(compositeData.ID+"_files_upload")}';
var tst=document.getElementById(id);
tst.value='';]]></xp:this.script>
                        <xp:this.action>
                            <xp:actionGroup>
                                <xp:executeScript>
                                    <xp:this.script><![CDATA[#{javascript:
var doc:NotesDocument=doc_source.getDocument(true);
if (doc==null)
{
return(null);
}
if (!doc.hasItem(compositeData.FieldName))
{
return(null);
}
var rit1:NotesRichTextItem=doc.getFirstItem(compositeData.FieldName);
if (rit1==null)
{
return(null);
}
try
{
var arr=rit1.getEmbeddedObjects();
}
catch(e)
{
return(null);
}

for(var i = 0; i < arr.length; i++)
{
    doc_source.removeAttachment(compositeData.FieldName, arr[i].getName());
}

return;

var doc:NotesDocument=doc_source.getDocument(true);
var rit:NotesRichTextItem=doc.getFirstItem(compositeData.FieldName);
if (rit==null)
{
return('');
}
var arr=rit.getEmbeddedObjects()
if (arr==null)
{
return('');
}
res=[]
for (var i = 0; i < arr.length; i++)
{
var att:NotesEmbeddedObject=arr[i];
//res.push(att.getName())
arr[i].remove();
}
//doc.save()
doc=doc_source.getDocument(true);
print('has RichText');
print(doc.hasItem(compositeData.FieldName));
return;}]]></xp:this.script>
                                </xp:executeScript>
                                <!-- <xp:saveDocument></xp:saveDocument>  -->

                            </xp:actionGroup>
                        </xp:this.action>
                    </xp:eventHandler>
                </xp:td>
            </xp:tr>
        </xp:table>

        <div style="height:0px;overflow:hidden">
            <input type="file"
                id="${javascript:compositeData.ID+'_files_input'}"
                onchange="#{javascript:
      var currentCustomID = compositeData.ID; 

      var filesInput = '\'' + currentCustomID + '_files_input' + '\'';
      var filesUpload = '\'' + currentCustomID + '_files_upload' + '\'';
      var filesButton = '\'' +  currentCustomID + '_files_button' + '\'';
      var filesProgress = '\'' +  currentCustomID + '_files_progress' + '\'';

      return 'files_onchange(' + filesInput + ',' + filesUpload + ',' + filesButton + ',' + filesProgress + ')';
      }"
                multiple="true" uploadOnSelect="true" name="uploadedfile"/>

            <xp:fileUpload
                id="${javascript:compositeData.ID+'_files_upload'}"
                useUploadname="true">
                <xp:this.value><![CDATA[#{doc_source[compositeData.FieldName]}]]></xp:this.value>
            </xp:fileUpload>

<xp:button value="Refresh"
        id="${javascript:compositeData.ID+'_files_button'}">
        <xp:eventHandler event="onclick"
            disableValidators="true"
          refreshMode="partial"
          refreshId="#{javascript:compositeData.ID+'refresh'}" execMode="partial"
          execId="#{javascript:compositeData.ID+'refresh'}" submit="true">
          <xp:this.action>


            <xp:actionGroup>
              <xp:actionGroup>


                <xp:saveDocument></xp:saveDocument>
                <xp:executeScript>

                  <xp:this.script><![CDATA[#{javascript:
if (compositeData.postUpload!=null)
{
compositeData.postUpload.getScript().invoke(facesContext, null)
}
}]]></xp:this.script>
                </xp:executeScript>
              </xp:actionGroup>

            </xp:actionGroup>
          </xp:this.action>

          <xp:this.script>
            <xp:executeClientScript
              script="console.log('The refresh button has just been clicked')">
            </xp:executeClientScript>
          </xp:this.script>
        </xp:eventHandler>
      </xp:button>
        </div>

        <xp:repeat id="${javascript:compositeData.ID+'_files_repeat'}"
            rows="30" var="rowData" indexVar="rowIndex">
            <xp:this.value><![CDATA[#{javascript:
try
{
    var doc:NotesDocument=doc_source.getDocument(true);
}
catch(e)
{
    print(e);
    var oss=new OsnovaSession();
    oss.CreateError("Загрузка файлов", "Некорректное имя файла");
}

if (doc==null)
{
return(null);
}
if (!doc.hasItem(compositeData.FieldName))
{
return(null);
}
var rit1:NotesRichTextItem=doc.getFirstItem(compositeData.FieldName);
if (rit1==null)
{
return(null);
}
try
{
var arr=rit1.getEmbeddedObjects()
}
catch(e)
{
return(null);
}
return(arr)
}]]></xp:this.value>
            <xp:table>
                <xp:tr>
                    <xp:td styleClass="doc_field_select" id="td4">
                        <xp:text escape="false" id="cf_file">


                            <xp:this.value><![CDATA[#{javascript:
if (rowData==null)
{
return('');
}
var siz=(rowData.getFileSize()/1024).toFixed(1);
siz=siz.replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, '$1 ');
return(texticon('download-9-icon',compositeData.IconSize,compositeData.IconSize,rowData.getName()+' ('+siz+' KB) ',false));

}]]></xp:this.value>
                            <xp:this.style><![CDATA[#{javascript:
    if(compositeData.IconColor==null)
    {
    return('')
    }
    else
    {
    return('fill:'+compositeData.IconColor+';')
    }}]]></xp:this.style>
                        </xp:text>
                        <xp:link escape="true" text="Link"
                            id="link_test" target="_blank" style="display:none">
                            <xp:this.value><![CDATA[#{javascript:var oss=new OsnovaSession()
                        try
                        {
                            var db:NotesDatabase=session.getDatabase(null,null);
                            db.openByReplicaID(session.getCurrentDatabase().getServer(),compositeData.ReplicaID);
                            var doc=db.getDocumentByUNID(compositeData.DocumentUNID);
                        }
                        catch(e)
                        {
                        var doc=null;
                        }

                        if(doc==null)
                        {
                            var doc:NotesDocument=doc_source.getDocument();
                            var db=database;
                        }

                    if (doc==null)
                    {
                    return(null);
                    }

//http(s)://[yourserver]/[application.nsf]/[viewname|0]/[UNID| ViewKey]/$File/[AttachmentName]?Open

var res=oss.ServerURL()+'/';
res+=db.getFilePath().replace(/\\/g,'/');
res+='/0/'+doc.getUniversalID()+'/$File/'+rowData.getName()+'?Open';
return(res);

//Old version
var res=oss.ServerURL()+'/'
res+=db.getFilePath().replace(/\\/g,'/')
res+='/xsp/.ibmmodres/domino/OpenAttachment/'
res+=db.getFilePath().replace(/\\/g,'/')+'/'
res+=doc.getUniversalID()+'/$File/'+rowData.getName()+'?Open'
return(res)
}]]></xp:this.value>
                        </xp:link>
                        <xp:eventHandler event="onclick" submit="true"
                            refreshMode="norefresh" disableValidators="true">
                            <!-- <xp:this.action><![CDATA[#{javascript:/*
                                var res=oss.ServerURL()+'/'
                                res+=database.getFilePath().replace(/\\/g,'/')
                                res+='/xsp/.ibmmodres/domino/OpenAttachment/'
                                res+=database.getFilePath().replace(/\\/g,'/')+'/'
                                res+=doc.getUniversalID()+'/$File/'+rowData.getName()+'?Open'
                                facesContext.getExternalContext().redirect(res)
                                //view.postScript("window.open('" + res + "'),'_blank'")

                                */}]]></xp:this.action> -->
                            <xp:this.script><![CDATA[
                        var linkID = '#{javascript:getClientId("link_test")}';
                        document.getElementById(linkID).click();

                        /*var refreshButton = '#{javascript:compositeData.ID+'_files_button'}';
                        console.log('а хули, увы ' + refreshButton);

                        document.querySelector('[id$=' + refreshButton + ']').click(); */
                        ]]>
                            </xp:this.script>
                        </xp:eventHandler>
                    </xp:td>

                    <xp:td styleClass="doc_field_select" id="td3">
                        <xp:text escape="false" id="cf_del">
                            <xp:this.value><![CDATA[#{javascript:
                        return(texticon('minus-5-icon',compositeData.IconSize,compositeData.IconSize,'Delete',false));
                        }]]>
                            </xp:this.value>
                            <xp:this.style><![CDATA[#{javascript:
                        if(compositeData.IconColor==null)
                        {
                            return('');
                        }
                        else
                        {
                            return('fill:'+compositeData.IconColor+';');
                        }}]]></xp:this.style>
                        </xp:text>
                        <xp:eventHandler event="onclick" submit="true"
                            refreshMode="complete" disableValidators="true">
                            <xp:this.action>
                                <xp:actionGroup>
                                    <xp:executeScript>
                                        <xp:this.script><![CDATA[#{javascript:

                                    doc_source.removeAttachment(compositeData.FieldName, rowData.getName()) 

                                    }]]>
                                        </xp:this.script>
                                    </xp:executeScript>
                                    <xp:save></xp:save>
                                    <xp:executeScript>
                                        <xp:this.script><![CDATA[#{javascript:
                                    if (compositeData.postDelete!=null)
                                    {
                                    compositeData.postDelete.getScript().invoke(facesContext, null);
                                    }}]]></xp:this.script>
                                    </xp:executeScript>

                                </xp:actionGroup>
                            </xp:this.action>
                            <xp:this.script><![CDATA[XSP.allowSubmit();]]></xp:this.script>
                        </xp:eventHandler>
                    </xp:td>

                </xp:tr>
            </xp:table>

        </xp:repeat>
        <span id="${javascript:compositeData.ID+'_files_progress'}">
        </span>
</xp:div>
</xp:view>

Just take a look at the Refresh button and hidden div besides these elements nothing should be wrong

The code which does the upload is here:

function files_onchange(filesInput, filesUpload, filesButton, filesProgress) 
{
    var urfiles = document.getElementById(filesInput).files;
    files_upload(filesInput, filesUpload, urfiles, 0, filesButton, filesProgress);
}           

function files_upload(filesInput, uploadID, files, counter, refreshID, filesProgress)
{
    var url = window.location.href;
    var formData = new FormData();
    var file = null;
    var form = XSP.findForm(filesInput);
    if (!files) return;
    var numberOfFiles = files.length;
    file = files[counter];
    var max = files.length;
    if (counter >= max) return;


    formData.append(document.querySelector('[id$=' + uploadID + ']').id, file);
    formData.append("$$viewid", dojo.query("input[name='$$viewid']")[0].value);
    formData.append("$$xspsubmitid", dojo.query("input[name='$$xspsubmitid']")[0].value);
    formData.append("$$xspsubmitvalue", dojo.query("input[name='$$xspsubmitvalue']")[0].value);
    formData.append("$$xspsubmitscroll", dojo.query("input[name='$$xspsubmitscroll']")[0].value);
    formData.append(form.id, form.id);

    console.log(form.id);

    var xhr = new XMLHttpRequest();


    /* event listners */

      xhr.upload.addEventListener("progress", function(e) 
      {
          if (e.lengthComputable)

           {
                var percentComplete = Math.round(e.loaded * 100 / e.total);
                document.getElementById(filesProgress).innerHTML = percentComplete.toString()+'%, ( '+(counter+1).toString()+' / '+numberOfFiles.toString()+' )';
           }
              else 
              {
                document.getElementById(filesProgress).innerHTML = '...';
              } 
      }, false);


      xhr.addEventListener("load", function()
      {
          counter++; 
          if (counter >= max)
          {
              document.getElementById(filesInput).value = "";

              if (refreshID) 

              {
                  document.querySelector('[id$=' + refreshID + ']').click(); // Here's where the refresh button is triggered. It DOES work. Always
              }

          } 
        else 
              { 
                files_upload(filesInput, uploadID, files, counter, refreshID, filesProgress) 
              }

      }, false);


     xhr.addEventListener("error", function(e) 
     {
         document.getElementById(filesProgress).innerHTML = "Error: "+e;

     }, false);

     xhr.addEventListener("abort", function()
     {
         document.getElementById(filesProgress).innerHTML = "Upload cancelled.";

     }, false);


      xhr.open("POST", url, true);
      xhr.send(formData);

      document.querySelector('[id$=' + uploadID + ']').value = '';

}

So, when document.querySelector('[id$=' + refreshID + ']').click(); is triggered it refreshes the element only if xp.this.rendered has been true since the beginning of the page. Otherwise, it removes the DOM element which it's supposed to refresh and I have to reload page or click the "Previous step" button in order to see the files which I've just uploaded.

This case is so esoteric, I don't even no what to do and why this happens. Hope you'll help. Thanks in advance.



from XPages execMode partial removes DOM elements when it's put in rendered

No comments:

Post a Comment