How to combine scripts like JS and such into a Use

2019-09-05 05:29发布

问题:

Once a time I spended hours and days to found a plain and smooth way to create a WebControl that can handle loading of scripts. Based on server side parameters (like user logged in or not), the control can decide to load script to the client or not.

We call it CombinedScripts.

Yesterday I was commenting a question regarding this topic. Sadly, while I wrote a answer to it this morning, it was deleted by the operator. I was 12 minutes late, said the error-reply. Now I try to re-make the question here, listen to your (the audience) and also put my answer to it within the next 15 minutes in this Q. It's of course free to reply and give ideas, i will accept-mark the best suited answer if someone did some better then mine.

Google says a very big range of things about how to make such things work. There also seem to be some numerous tricky parts on the way. The path to the script, behavior when it not works (because it's easy to compile). There are also a web.config setup to register the component in the project. The question is about try make the control stand-alone as possible, embed the resources and move the control to other project for re-use.

回答1:

This have some technically tricky parts and I try to cover them here. I spent some time on Google until I finally got all pieces. I surely can't help you with all of them. Some of them are very local to the actual scenario. In fact, I did never found any suitable information to my scenario, that didn't lead me to a spaghetti-code, hughe code base and so on.

You create a Composite Control by simply override System.Web.UI.Webcontrols.Webcontrol in a new class. I use CombinedScript as a name due to your interest of it. The name can of course be anything.

public class CombinedScript : System.Web.UI.WebControls.WebControl
{

}

While override that, you have some methods to use. There are several methods then those below, but you have to google on what best suites your need.

protected override void OnPreRender(EventArgs e)
{
}

protected override void Render(HtmlTextWriter writer)
{
}

The point is that the methods are doing things before, while and after the generate of the controls content to client. The OnPreRender occurs before, I.e.

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    ClientScriptManager cs = this.Page.ClientScript;
    cs.RegisterClientScriptResource(typeof(WebControls.CombinedScript), "Project.Script.Common.js");
    cs.RegisterClientScriptResource(typeof(WebControls.CombinedScript), "Project.Script.JQuery.js");
    cs.RegisterClientScriptResource(typeof(WebControls.CombinedScript), "Project.Script.JCharts.js");
}

A tricky thing here is the path to scripts. "Project" is the DLL assembly base name. "Script" is the case sensitive path, where the script files are located (counted from project base path). The last part is the case sensitive name of the file. I'm not sure, but I think the path trick is no question if you leave exact everything at root.

WebControls.CombinedScript is, as you can see, the name of the control. To be used in ASP.NET pages, it has to be registered in web.config.

<pages>
  <controls>
    <add assembly="Project" tagPrefix="myname" namespace="Project.WebControls" />
  </controls>
</pages>

Those RegisterClientScriptResource makes the scripts being loaded as GET /WebResource.axd?d=MTVCK_diZif7wEDQp9UGv-GlqPKP-WXGPNEqr-mq_RnUKWDgSydkLxIuSusdnolRQsR-wpNbTpfJzAVtsonjuwVUqzguotspQFImk6-auSF3N18eVOXbYh_-6NHovgR90&t when the site are loaded. If they don't, there are something wrong. I.e. wrong path, forgotten to include js as embedded resource and so on.

When all above is done, you register the DLL, add the namespace / control tag and then put it on your page with <myname:CombinedScript runat="server" />

You can probably do something like this, add public properties to the Composite Control
public int ScriptType {get;set;}. Put the attribute to the control <myname:CombinedScript ScriptType="1" runat="server" />. and add if (this.ScriptType == 1) { ..evaluate what script to load.. }. Of course that type of evaluation can be altered from clientside (anyone can replace their generated html to another ScriptType ID).

I think you can investigate yourself better by this. You want statements that evaluate what scripts to load. The last sample above is one way and you can of course put any kind of logic between / on the RegisterClientScriptResources.

I was talked about RenderControl and Render.
You can use it like this,

protected override void Render(HtmlTextWriter writer)
{
    HttpResponseSubstitutionCallback callback = new HttpResponseSubstitutionCallback(outputcache_callback);
    Context.Response.WriteSubstitution(callback);
}

public static string outputcache_callback(HttpContext context)
{
    string html = "";

    html += "<script type='text/javascript'>";
    html += " var lang='" + Project.Common.CurrentLang + " ';";
    html += " var applicationPath='" + GetApplicationPath + "'; ";
    html += "</script>";
    return html;
}

public GetApplicationPath { get { ..logic.. }}

That's another kind of technique put scripts into clientside. Good if you which to have user- or instance-specific variables loaded to your scripts. static is for the substitution-sample. Also, if you want to affect the generating of content of the control itself (like if you would make a smarter asp:Label, you can do so,

public override void RenderControl(HtmlTextWriter writer)
{
    base.RenderControl(writer);
    writer.Write("<div id='dvBox'>");
    ....
    base.RenderChildren(writer);
    writer.Write("</div>");
    writer.Write(script);
}

I hope that are information that combine and help several developers out there searching for such way to build controls.