Charger dynamiquement un controle a l'ouverture d'une modalpopup
Suite à mon post expliquant le PostBackControl, voici un cas concret d'utilisation de ce contrôle grâce à une question que l'on m'a déjà posé plusieurs fois.
Question :
J'ai une page avec plusieurs liens qui ouvrent différentes ModalPopupExtender. Actuellement j'ai créé autant de ModalPopupExtender que de lien, ça fonctionne mais la page est très lourde à charger, j'ai regardé le code HTML et le contenu de mes différents ModalPopup sont chargé dès le début. Existe-t-il une solution pour ne pas charger le contenu des modalpopup dès la première requête mais seulement lors de l'ouverture de celui-ci ?
Réponse :
Oui ! c'est tout à fait possible. Pour cela on va utiliser le contrôle PostBackControl que j'ai expliqué ici : PostBackControl - UpdatePanel et la communication client / serveur.
Pourquoi la page est lourde ? 2 raisons : tout d'abord vous créez autant d'instance du ModalPopupExtender qu'il y a de possibilité d'affichage. La création d'une instance est relativement couteuse et ralenti le chargement de la page. Il est pourtant inutile d'avoir plusieurs instances du ModalPopupExtender puisqu'une seule est utilisée en même temps. L'autre problème est que le contenu HTML de toutes les modalpopup sont chargé côté client ce qui est inutile, là encore on ne va afficher qu'une seule modalpopup et de plus seulement lorsqu'on l'ouvre : inutile de le charger dès le début.
L'idée est donc d'utiliser qu'un seul ModalPopupExtender et lors de son ouverture, demander à JavaScript de charger le contenu du ModalPopup par l'intermédiaire d'un UpdatePanel.
Voici donc le code de l'idée général :
<asp:LinkButton ID="lbTest" runat="server" Text="show popup" style="display:none;" />
<ajaxtoolkit:ModalPopupExtender runat="server" ID="mpePopup"
TargetControlID="lbTest" PopupControlID="pnlPopup" />
<asp:Panel runat="server" ID="pnlPopup" CssClass="modalPopup">
<asp:LinkButton runat="server" ID="lbClose">Fermer</asp:LinkButton>
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:multiview runat="server" Visible="false" ID="mvPopup">
<asp:View runat="server">
XXX
</asp:View>
<asp:View runat="server">
YYYY
</asp:View>
</asp:multiview>
<cs:PostBackControl runat="server" ID="pbcTest" OnCallBack="pbcTest_CallBack" />
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
protected void pbcTest_CallBack(object sender, CallBackEventArgs e)
{
mvPopup.Visible = true;
int activeViewIndex = 0;
int.TryParse((e.CallBackArgument??String.Empty).ToString(), out activeViewIndex);
mvPopup.ActiveViewIndex = activeViewIndex;
}
Comment cela fonctionne ? Une fonction JavaScript va afficher le modalpopup puis déclenché un AsyncPostback grâce au PostBackControl . Côté serveur l'événement CallBack rendra visible le multiview et sélectionnera la bonne vue grâce à l'argument passé depuis JavaScript. Ainsi il n'y a qu'une seule instance du modalpopup et son contenu est chargé en Ajax seulement lorsqu'on affiche celui-ci.
Voici ce que cela donne au final :
<button id="btn1">Je suis un button</button>
<button id="btn2">Je suis un 2ème button</button>
<cs:Script runat="server">
window.pageLoad = function(){
$addHandler($get('btn1'), 'click', function(e){
<%=pbcTest.GetCallBackFunction(String.Empty)%>
$find('<%=mpePopup.ClientID%>').show();
e.preventDefault();
});
$addHandler($get('btn2'), 'click', function(e){
var activeViewIndex = 1;
<%=pbcTest.GetCallBackFunction("activeViewIndex")%>
$find('<%=mpePopup.ClientID%>').show();
e.preventDefault();
});
}
</cs:Script>
<%-- ' malheureusement on est obligé de spécifier un button pour le modalpopupextender ...
' Comme on est développeur JavaScript on en a pas besoin. On préfére gérer nous même
' quand on affiche le modalpopup
--%>
<asp:LinkButton ID="lbTest" runat="server" Text="show popup" style="display:none;" />
<ajaxtoolkit:ModalPopupExtender runat="server" ID="mpePopup" TargetControlID="lbTest"
PopupControlID="pnlPopup" BackgroundCssClass="modalBackground"
CancelControlID="lbClose" />
<asp:Panel runat="server" ID="pnlPopup" CssClass="modalPopup">
<%-- ' Attention! il ne faut pas mettre le cancelControlID ou le okControlID à l'intérieur
' de l'updatepanel. En effet, il y a un bug (un de plus) dans les toolkits : en mettant
' le (ok|cancel)ControlID dans l'updatepanel, le lien sera inactif après MAJ
' de l'updatepanel, l'instance du button sera recréer et le modalpopupextender
' ne se réabonne pas sur le click de ce nouveau bouton après MAJ des updatepanels
--%>
<asp:LinkButton runat="server" ID="lbClose">Fermer</asp:LinkButton>
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<ContentTemplate>
<%-- On peut aussi utiliser un MultiView si on a différents contenu --%>
<asp:multiview runat="server" Visible="false" ID="mvPopup">
<asp:View runat="server">
Ce contenu sera chargé seulement lors de l'ouverture du modalpopup
</asp:View>
<asp:View runat="server">
Contenu N°2
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<ContentTemplate>
<%=DateTime.Now.ToLongTimeString() %>
<asp:Button runat="server" Text="Refresh" />
</ContentTemplate>
</asp:UpdatePanel>
</asp:View>
</asp:multiview>
<cs:PostBackControl runat="server" ID="pbcTest" OnCallBack="pbcTest_CallBack" />
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
Les contrôles des toolkits permettent de ne pas toucher une ligne de JavaScript mais au détriment des performances. Si vous utilisez plusieurs contrôles toolkits et que vous voulez une bonne expérience utilisateur il faut impérativement mettre les mains dans le JavaScript. Vous serez alors déchainer des contraintes d'ASP.net en mesure de personnaliser les toolkits et faire beaucoup plus de choses côté client que ne permet ASP.net ...