0

I am trying to use bootstrap tabs in order to navigate in a single page application. By clicking on a tab I want to change the view (switch to the selected tab) and save the data to the database.

I created the tabs as apex:commandLinksand added them to my form and was able to save and navigate.

The problem occurred later when I navigated to a tab with apex:inputFile attribute and got this error:

apex:inputFile can not be used in conjunction with an action component, apex:commandButton or apex:commandLink that specifies a rerender or oncomplete attribute.

I decided to separate the tabs from the rest of the form so I created another form.

Now I have a page with two forms and the navigation works and also I don't get the error above anymore but, I'm unable to save anymore, no errors and no indication why.

I didn't manage to capture any error in the debug log nor in chrome debugger.

VF Page Snippet:

<div class="container-fluid">
    <div class="row">
        <apex:form id="headerForm"> <-----First Form
            <div class="col-sm-12 text-center"><br></br>
                <ul class="nav nav-tabs" id="sidebarUList">
                    <li class="active">
                        <apex:commandLink action="{!changePage}" reRender="secondform">tab 1
                            <apex:param value="1" assignTo="{!pageNumber}" />
                        </apex:commandLink>
                    </li>
                    <li>
                        <apex:commandLink action="{!changePage} reRender="secondform"">tab 2
                            <apex:param value="2" assignTo="{!pageNumber}" />
                        </apex:commandLink>
                    </li>
                </ul>
            </div>
        </apex:form>

        <div class="col-sm-12">
            <apex:form id="secondform"> <-----Second Form
                <apex:outputPanel rendered="{!sm.CurrentStep==1}">
                    <apex:inputField value="{!pa.Server_name__c}" />
                    <apex:outputField value="{!pa.Server_name__c}" />
                </apex:outputPanel>
            </apex:form>
        </div>
    </div>
</div>

Controller:

public void changePage() {
 try {
   sm.CurrentStep=pageNumber;
   save();
   } catch(Exception e) {}
 } 

public pageReference save() {
 Savepoint sp = Database.setSavepoint();
 try {
   update pa;
   } catch (Exception e) {
     Database.rollback(sp);
     return null;
   }

   return new pageReference('/apex/visualforceTes);
 }
0

Firstly - you're not using the tabs very well to display your tab data - you can do this easily without the heavy weighted components and use HTML components and a little remoting.

Controller...

@RemoteAction
public static Boolean changePage( Integer pageNum ) {

    try{
        sm.currentStep = pageNum;
        save();
    }
    catch( Exception ex ){
        System.debug( 'Error: ' + ex.getMessage() );
        return false;
    }
    return true;
}

Page...

<apex:page controller="TestChildClass">


    <script src="https://code.jquery.com/jquery-2.0.2.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"></link>
    <script>
        var j$ = jQuery.noConflict();

        j$( document ).ready( function(){
            // do some stuff here ...
        });
        // send your data here to be updated
        function saveData( data ){
            var deferred = j$.Deferred();

            Visualforce.remoting.Manager.invokeAction( '{!$RemoteAction.TestChildClass.changePage}', data, function( result, event ){
                if( event.status ){
                    deferred.resolve( result );
                }
                else{
                    deferred.reject( event );
                }
            });

            return deferred.promise();
        }

        function evalChanges( data ){

            j$.when( saveData( data )).done( function( result ){
                // do something with your result here
                console.log( 'My result is...' );
                console.log( result );
            });
        }
    </script>
    <div class="container-fluid">
        <div class="row">
                <div class="col-sm-12 text-center"><br></br>
                    <ul class="nav nav-tabs" id="sidebarUList">
                        <li class="active">
                            <a href="#firstTab"  data-toggle="tab" onclick="evalChanges(1);" id="1">Tab 1</a>
                        </li>
                        <li>
                            <a href="#secondTab" data-toggle="tab"  onclick="evalChanges(2);" id="2">tab 2</a>
                        </li>
                    </ul>
                </div>
            <div class="tab-content">
                <div class="tab-pane" id="firstTab">
                    Render my Tab 1 Content
                </div>

                <div class="tab-pane" id="secondTab">
                    Render my Tab 2 content
                </div>
            </div>
        </div>
    </div>
</apex:page>

Having all of this and it being completely light weight you don't need to worry for things like ViewState as well as issues on rerendering. You can now host your objects/elements/data inside here and you can send your data back to the controller.

I used just an integer as your controller is just storing the page number when you change tabs - you can grab your data anyway you choose from the page but if you're using apex tags and variables on the page then the variables should already be there.

  • Thank you again Ronnie, the problem with JS Remoting is that I need to update two objects every time and with JS Remoting I have to query them each time and only then update. won't it reach a high number of SOQL queries? I have 7 tabs in my form – Json Sep 9 '17 at 8:08
  • @Dante the queries don't stack the entire time they're on the page/application. Only when they create an action that uses DML, as long as each instance of a save doesn't exceed the limit you should be fine. So each remoting call has a fresh set of queries, just consider that what you can do is store the data from the application in is variables too and apply them at the end or before navigating away from the page to another page – Ronnie Sep 9 '17 at 15:49
  • I am doing something similar to what you are doing already with a single page application and tabs for different steps along the way, I wrote also a simple function that will switch the tabs upon step completion and the data is stored pending the final completion where I will then save my changes. Would an addition like that help resolve your issue or would you absolutely need to perform an update with every tab change to these objects?@Dante – Ronnie Sep 11 '17 at 22:33
  • It seems that there is no other way for me but to store the data (similar to what you did) until the form is submitted. – Json Sep 13 '17 at 6:09
  • Ok - i will send an updated addition to this this evening that will help you outline an easy way to do that and maintain the ability to navigate and track progress @Dante – Ronnie Sep 13 '17 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.