Regrouper plusieurs fichiers javascript grace au ToolkitScriptManager
Depuis quelques temps déjà, les dernières versions des Ajax Control Toolkit intègre un nouveau contrôle qui hérite du ScriptManager : le ToolkitScriptManager. Ce contrôle permet de combiner plusieurs fichiers JavaScript en un seul. Lorsque vous utilisez plusieurs toolkits sur la même page le client doit télécharger de nombreux fichiers JavaScript ce qui entraine des performances plutôt mauvaise. De plus le ToolkitScriptManager permet de compacter et gzipper les fichiers JavaScript. J'en ai aussi parlé ici : Réduire la taille des fichiers JavaScript
Pour le faire fonctionner, il suffit de remplacer le classique ScriptManager par le ToolkitScriptManager.
<ajaxtoolkit:ToolkitScriptManager runat="server" />
Automatiquement, tous les fichiers JavaScript ajoutés par les toolkits seront combinés en un seul. Par défaut l'url du fichier JavaScript final est :
CurrentPage.aspx?_TSM_HiddenField_=ctl00_SampleContent_ScriptManager1_HiddenField&_TSM_...
Oui ! Vous avez bien lu, le fichier résultat pointe sur la page en cours. Cela implique 2 choses :
- Dès qu'on change de page, le fichier sera différent, il n'y aura aucun cache client pour ce fichier;
- La page va être exécutée deux fois : Page_Load, ...
Bref, par défaut le comportement est stupide. Heureusement il est possible de spécifier l'adresse d'un handler qui s'occupera de combiner les différents fichiers.
Voici ce que vous pouvez mettre dans un fichier CombineScriptsHandler.ashx afin d'avoir qu'une seule url pour tout le site.
<%@ WebHandler Language="C#" Class="CombineScriptsHandler" %>
// CombineScriptsHandler.ashx
using System;
using System.Web;
using AjaxControlToolkit;
public class CombineScriptsHandler : IHttpHandler
{
/// <summary>
/// ProcessRequest implementation outputs the combined script file
/// </summary>
/// <param name="context"></param>
public void ProcessRequest(HttpContext context)
{
if (!ToolkitScriptManager.OutputCombinedScriptFile(context))
{
throw new InvalidOperationException("Combined script file output failed unexpectedly.");
}
}
/// <summary>
/// IsReusable implementation returns true since this class is stateless
/// </summary>
public bool IsReusable
{
get { return true; }
}
}
Il vous suffit ensuite de renseigner la propriété CombineScriptHandlerUrl du ToolkitScriptManager
<ajaxToolkit:ToolkitScriptManager runat="server"
CombineScriptsHandlerUrl="~/CombineScriptsHandler.ashx" />
Astuce : Si vous avez mis le ScriptManager dans toutes vos pages plutôt qu'uniquement dans le masterpage, vous pouvez utiliser du TagMapping pour remplacer tous les ScriptManager par des ToolkitScriptManager et spécifier la propriété CombineScriptsHandlerUrl au niveau du fichier de skin. Pour en savoir plus sur le TagMapping : TagMapping : comment changer les comportements des controles web en quelques lignes
Le mieux reste bien sur de mettre le ScriptManager au niveau du masterpage et si besoin, se servir d'un ScriptManagerProxy dans vos pages pour ajouter des scripts précis.
Jusque là tout va bien, mais malheureusement le contrôle possède un bug. En effet le ScriptManager permet de rajouter des scripts manuellement, je m'en sers souvent pour utiliser les Animations des toolkits sans devoir passer par le contrôle serveur. Pour cela j'utilise un ScriptReference.
<ajaxtoolkit:ToolkitScriptManager ID="SC1" runat="server" ScriptMode="Release"
CombineScriptsHandlerUrl="~/CSScriptHandler.ashx"
EnableScriptGlobalization="true" EnableScriptLocalization="true">
<Scripts>
<asp:scriptreference assembly="AjaxControlToolkit"
name="AjaxControlToolkit.Compat.Timer.Timer.js"/>
<asp:scriptreference assembly="AjaxControlToolkit"
name="AjaxControlToolkit.Common.Common.js"/>
<asp:scriptreference assembly="AjaxControlToolkit"
name="AjaxControlToolkit.Animation.Animations.js"/>
<asp:scriptreference assembly="AjaxControlToolkit"
name="AjaxControlToolkit.ExtenderBase.BaseScripts.js"/>
<asp:scriptreference assembly="AjaxControlToolkit"
name="AjaxControlToolkit.Animation.AnimationBehavior.js"/>
</Scripts>
</ajaxtoolkit:ToolkitScriptManager>
Mais malheureusement en faisant comme cela, tous les fichiers ajoutés manuellement seront ajoutés deux fois ! Une fois par le combinaison liée aux contrôles des toolkits et une fois manuellement, cela entraine bien sur de nombreuses erreurs. J'ai signalé le bug ici : ToolkitScriptManager add some script twice when added manually
Pour corriger ce problème il faut modifier le fichier ToolkitScriptManager.cs de la sorte puis recompiler :
/// <summary>
/// ADDED : contains a cache with the partial name of an assembly and his full name
/// </summary>
private static Dictionary<String, String> _assemblyFullName = new Dictionary<string, string>();
/// <summary>
/// OnResolveScriptReference override to track combinable scripts and update the script references
/// </summary>
/// <param name="e">event args</param>
protected override void OnResolveScriptReference(ScriptReferenceEventArgs e)
{
base.OnResolveScriptReference(e);
// If combining scripts and this is a candidate script
if (_combineScripts && !String.IsNullOrEmpty(e.Script.Assembly) && !String.IsNullOrEmpty(e.Script.Name))
{
// Initialize
ScriptReference scriptReference = e.Script;
// ADDED : get and set the fullname of the assembly
if (!_assemblyFullName.ContainsKey(scriptReference.Assembly))
_assemblyFullName[scriptReference.Assembly] = Assembly.Load(scriptReference.Assembly).FullName;
scriptReference.Assembly = _assemblyFullName[scriptReference.Assembly];
ScriptEntry scriptEntry = new ScriptEntry(scriptReference);
if (IsScriptCombinable(scriptEntry))
{
// ADDED : check if there is already a script with the same name and same assembly name
if (!_scriptEntries.Exists(delegate(ScriptEntry entry)
{
if (!String.IsNullOrEmpty(scriptEntry.Assembly))
{
return entry.Name == scriptEntry.Name && entry.Assembly == scriptEntry.Assembly;
}
else
{
return entry.Name == scriptEntry.Name;
}
}))
{
// Haven't seen this script yet; add it to the list and invalidate the Url
_scriptEntries.Add(scriptEntry);
_combinedScriptUrl = null;
}
}
}
}
Pour en savoir plus sur le ToolkitScriptManager :
PS : bizarre à chaque fois que j'utilise un truc des toolkits je tombe sur des bugs et je suis obligé de bidouiller ...