2

Hi I have a VF Page and a controller which works fine on sandbox , But somehow they fails and give the following error on production which is due to view state Maximum view state size limit (135KB) exceeded. Actual view state size for this page was 198.516KB

Below is the view state of the page.

enter image description here

The Visualforce Page :

<script>
    $(document).ready(function() {   

        $('#calendar').fullCalendar({

            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            editable: false,
            events:[
                    <apex:repeat value="{!calendarEvents}" var="e">
                        {
                            title: '{!JSENCODE(calendarEvents[e].title)}',
                            start: '{!calendarEvents[e].startDate}',
                            end: '{!calendarEvents[e].endDate}',
                            url: '{!calendarEvents[e].url}',
                            allDay: {!calendarEvents[e].allDay},
                            color: '{!calendarEvents[e].color}',
                            customerName: '{!JSENCODE(calendarEvents[e].customerName)}',
                            address: '{!JSENCODE(calendarEvents[e].address)}',
                            phone: '{!calendarEvents[e].phone}',
                            owner: '{!calendarEvents[e].owner}',
                            leadSource: '{!JSENCODE(calendarEvents[e].leadSource)}',
                            type: '{!calendarEvents[e].type}',
                        },
                    </apex:repeat>
            ],
            eventClick: function(event){
                if(event.url){
                    window.open(event.url);

                    return false;
                }
            },
            eventRender: function(event, element) {
                element.qtip({
                    content: $('<div>' + event.customerName + '<br/>' + event.address + '<br/>' + event.phone + '<br/><br/>' + event.owner + '<br/>' + event.leadSource + '<br/>' + event.type + '</div>')
                });
            }

        });
    });
</script>
<apex:pageBlock title="Calendar">
    <apex:pageBlockSection columns="1">
        <apex:form >
            <div id="cal-legend">
                <ul>
                    <apex:repeat value="{!usrList}" var="usr">
                        <li><span style="background:{!usrList[usr].Color__c} !important; border-color:{!usrList[usr].Color__c};"></span>{!usrList[usr].Name}</li>
                    </apex:repeat>
                </ul>
                <div style="clear:both;"> <!--fix floats--></div>
            </div>
            <div style="clear:both;"><!--fix floats--></div>
            <div id="calendar"></div>
        </apex:form>
    </apex:pageBlockSection>
</apex:pageBlock>
<apex:includeScript value="{!URLFOR($Resource.fullCalendarJS, 'fullcalendar-2.3.2/lib/moment.min.js')}"/>
<apex:includeScript value="{!URLFOR($Resource.fullCalendarJS, 'fullcalendar-2.3.2/lib/jquery.min.js')}"/>
<apex:includeScript value="{!$Resource.jqueryui}"/>
<apex:includeScript value="{!URLFOR($Resource.qTip,'/jquery.qtip.js')}"/>
<apex:includeScript value="{!URLFOR($Resource.fullCalendarJS, 'fullcalendar-2.3.2/fullcalendar.min.js')}"/>
<style>
    #cal-legend { float:right;}

    #cal-legend ul {margin:0;padding:0;list-style:none;}

    #cal-legend ul li {margin:0;padding:5px;float:left;}

    #cal-legend ul li span {display:block; height:16px; width:16px; margin-right:4px; float:left; border-radius:4px;}

    #calendar {margin-top:20px;}
</style>

And the controller :

public class EventCalendar {

    public Map<Id,User> usrList{get;set;}
    public String events{get;set;}
    public Map<Id,CalEvent> calendarEvents{get;set;}
    set<String>setofeventCalnUser;

    public EventCalendar(){
        setofeventCalnUser = new set<string>();
        for(string str : System.Label.EventCal.split(',')){
            setofeventCalnUser.add(str);
        }
        if(setofeventCalnUser.size()>0){
            usrList = new Map<Id,User>([SELECT Id,Color__c,Name FROM User WHERE Profile.Name = 'Sales User' OR Username IN :setofeventCalnUser ]);
        }
        else {
          usrList = new Map<Id,User>([SELECT Id,Color__c,Name FROM User WHERE Profile.Name = 'Sales User']);
        }


        String dtFormat = 'EEE, d MMM yyyy HH:mm:ss z';

        calendarEvents = new Map<Id,CalEvent>();

        Map<Id,Id> evtOppMap = new Map<Id,Id>();

        for(Event evt : [SELECT Id, Subject, isAllDayEvent, Owner.Name, OwnerId, StartDateTime, EndDateTime, Location, WhatId FROM Event WHERE OwnerId IN: usrList.keySet()]){
            evtOppMap.put(evt.Id,evt.WhatId);
            calendarEvents.put(evt.Id,new CalEvent(evt.Subject + (evt.Location == null?'':' - ' + evt.Location),evt.isAllDayEvent,evt.StartDateTime.format(dtFormat),evt.EndDateTime.format(dtFormat),'/' + evt.Id,usrList.get(evt.OwnerId).Color__c,evt.Owner.Name));
        }

        Map<Id,Opportunity> oppMap = new Map<Id,Opportunity>([SELECT Id, Name, Account.BillingStreet, Account.BillingCity, Account.BillingState, Account.BillingPostalCode,
                                                                Account.BillingCountry, Account.Phone, LeadSource, Type FROM Opportunity WHERE Id IN: evtOppMap.values()]);

        for(Id evtId : calendarEvents.keySet()){
            Opportunity opp = oppMap.get(evtOppMap.get(evtId));
            if(opp != null){
                calendarEvents.get(evtId).customerName = opp.Name;
                calendarEvents.get(evtId).address = opp.Account.BillingStreet + ',<br/>' + opp.Account.BillingCity + ', ' + opp.Account.BillingState + ' ' + opp.Account.BillingPostalCode + ',<br/>' + opp.Account.BillingCountry;
                calendarEvents.get(evtId).phone = opp.Account.Phone;
                calendarEvents.get(evtId).leadSource = opp.LeadSource;
                calendarEvents.get(evtId).type = opp.Type;
            }
        }
    }

    public class CalEvent{

        public String title{get;set;}
        public Boolean allDay{get;set;}
        public String startDate{get;set;}
        public String endDate{get;set;}
        public String url{get;set;}
        public String color{get;set;}
        public String customerName{get;set;}
        public String address{get;set;}
        public String phone{get;set;}
        public String leadSource{get;set;}
        public String type{get;set;}
        public String owner{get;set;}

        public CalEvent(String title, Boolean allDay, String startDate, String endDate, String url, String color, String owner){
            this.title = title;
            this.allDay = allDay;
            this.startDate = startDate;
            this.endDate = endDate;
            this.url = url;
            this.color = color;
            this.owner = owner;
        }
    }
}

Let me know what can be changes , I have checked the best practice But transient usage I am unable to understand and how the error can be resolved in production

  • Is that be possible to change fullCalendar() method where you are iterating all the event on the fly inside jQuery. That could be probable cause. – Santanu Boral Jun 30 '16 at 17:35
2

Presumably the problem is that your production org has a lot more data - 1849 objects based on your screen shot - than your sandbox. The only fix for production is to modify the code keeping those objects out of the view state and re-deploy the code to production.

(Fields being in the view-state by default is an unfortunate default. I would guess that a lot of CPU cycles and time is wasted because of this. Typically very little needs to be in the view state.)

If there is no update logic in your page leaving out the apex:form should avoid the view state problem...

The quick change to transient that Ishwar suggests I think will fail to compile with the error:

Methods cannot be marked transient

because the Apex properties syntax is a syntactic convenience that gets translated into getX and setX methods. Separating the variable name and the get/set property name can work-around that but its not very pretty.

A simple way to not put the data in the view state (if the data is only referenced once in the page - if it was referenced multiple times the re-creation would be a waste) is to just write a getter method. In this case the page just really needs an array for the apex:repeat to iterate over so the method would be:

// Just having a getter means nothing in the view state
public CalEvent[] getCalendarEvents() {
    // Local variable so not in view state
    Map<Id, CalEvent> m = new Map<Id, CalEvent>();
    // Populate map in here...
    return m.values();
}

with a corresponding simplification of the Visualforce.

  • I have found that transient can be used with the simplified version {get;set;} but cannot be used when get and set are in the longer form. Not that that helps the OP but a point of clarification that I recently ran into – Eric Jul 1 '16 at 4:09
  • @Eric Yeah I wasn't very clear: the problem occurs when the getter has some logic in it (which is quite common) so the first thing you try - adding the transient - doesn't work which is off-putting. – Keith C Jul 1 '16 at 7:47
2

Geeky solution for this page only.

As you are not using any apex action in apex:CommandButton or apex:actionFunction, you can just remove <apex:form> tag from your page.

Your view state will be reduced to zero and will never go up.

1
public Map<Id,User> usrList{get;set;}
public String events{get;set;}
public Transient Map<Id,CalEvent> calendarEvents{ get; set;}
set<String>setofeventCalnUser;

public EventCalendar(){
    setofeventCalnUser = new set<string>();
    for(string str : System.Label.EventCal.split(',')){
        setofeventCalnUser.add(str);
    }
    if(setofeventCalnUser.size()>0){
        usrList = new Map<Id,User>([SELECT Id,Color__c,Name FROM User WHERE Profile.Name = 'Sales User' OR Username IN :setofeventCalnUser ]);
    }
    else {
      usrList = new Map<Id,User>([SELECT Id,Color__c,Name FROM User WHERE Profile.Name = 'Sales User']);
    }

    calendarEvents = populdatecalendarEvents)();
}

public Map<Id,CalEvent> populdatecalendarEvents() {
    String dtFormat = 'EEE, d MMM yyyy HH:mm:ss z';

    calendarEvents = new Map<Id,CalEvent>();

    Map<Id,Id> evtOppMap = new Map<Id,Id>();

    for(Event evt : [SELECT Id, Subject, isAllDayEvent, Owner.Name, OwnerId, StartDateTime, EndDateTime, Location, WhatId FROM Event WHERE OwnerId IN: usrList.keySet()]){
        evtOppMap.put(evt.Id,evt.WhatId);
        calendarEvents.put(evt.Id,new CalEvent(evt.Subject + (evt.Location == null?'':' - ' + evt.Location),evt.isAllDayEvent,evt.StartDateTime.format(dtFormat),evt.EndDateTime.format(dtFormat),'/' + evt.Id,usrList.get(evt.OwnerId).Color__c,evt.Owner.Name));
    }

    Map<Id,Opportunity> oppMap = new Map<Id,Opportunity>([SELECT Id, Name, Account.BillingStreet, Account.BillingCity, Account.BillingState, Account.BillingPostalCode,
                                                            Account.BillingCountry, Account.Phone, LeadSource, Type FROM Opportunity WHERE Id IN: evtOppMap.values()]);

    for(Id evtId : calendarEvents.keySet()){
        Opportunity opp = oppMap.get(evtOppMap.get(evtId));
        if(opp != null){
            calendarEvents.get(evtId).customerName = opp.Name;
            calendarEvents.get(evtId).address = opp.Account.BillingStreet + ',<br/>' + opp.Account.BillingCity + ', ' + opp.Account.BillingState + ' ' + opp.Account.BillingPostalCode + ',<br/>' + opp.Account.BillingCountry;
            calendarEvents.get(evtId).phone = opp.Account.Phone;
            calendarEvents.get(evtId).leadSource = opp.LeadSource;
            calendarEvents.get(evtId).type = opp.Type;
        }
    }

    return calendarEvents;
}
}
  • This will produce a compile error (See Keiths answer) – Eric Jul 1 '16 at 4:09
  • Its updated now – Ishwar Mete Jul 1 '16 at 4:27

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.