admin管理员组

文章数量:1391860

We are currently developing an ASP.NET MVC application which makes heavy use of attribute-based metadata to drive the generation of JavaScript.

Below is a sample of the type of methods we are writing:

function string GetJavascript<T>(string javascriptPresentationFunctionName,
                                 string inputId,
                                 T model)
{
    return @"function updateFormInputs(value){
        $('#" + inputId + @"_SelectedItemState').val(value);
        $('#" + inputId + @"_Presentation').val(value);
     }

    function clearInputs(){
        " + helper.ClearHiddenInputs<T>(model) + @"
        updateFormInputs('');
    }

    function handleJson(json){
        clearInputs();
        " + helper.UpdateHiddenInputsWithJson<T>("json", model) + @"
        updateFormInputs(" + javascriptPresentationFunctionName + @"());
        " + model.GetCallBackFunctionForJavascript("json") + @"
    }";
}

This method generates some boilerplace and hands off to various other methods which return strings. The whole lot is then returned as a string and written to the output.

The question(s) I have are:

1) Is there a nicer way to do this other than using large string blocks?

We've considered using a StringBuilder or the Response Stream but it seems quite 'noisy'. Using string.format starts to bee difficult to prehend.

2) How would you go about unit testing this code? It seems a little amateur just doing a string parison looking for particular output in the string.

3) What about actually testing the eventual JavaScript output?

Thanks for your input!

We are currently developing an ASP.NET MVC application which makes heavy use of attribute-based metadata to drive the generation of JavaScript.

Below is a sample of the type of methods we are writing:

function string GetJavascript<T>(string javascriptPresentationFunctionName,
                                 string inputId,
                                 T model)
{
    return @"function updateFormInputs(value){
        $('#" + inputId + @"_SelectedItemState').val(value);
        $('#" + inputId + @"_Presentation').val(value);
     }

    function clearInputs(){
        " + helper.ClearHiddenInputs<T>(model) + @"
        updateFormInputs('');
    }

    function handleJson(json){
        clearInputs();
        " + helper.UpdateHiddenInputsWithJson<T>("json", model) + @"
        updateFormInputs(" + javascriptPresentationFunctionName + @"());
        " + model.GetCallBackFunctionForJavascript("json") + @"
    }";
}

This method generates some boilerplace and hands off to various other methods which return strings. The whole lot is then returned as a string and written to the output.

The question(s) I have are:

1) Is there a nicer way to do this other than using large string blocks?

We've considered using a StringBuilder or the Response Stream but it seems quite 'noisy'. Using string.format starts to bee difficult to prehend.

2) How would you go about unit testing this code? It seems a little amateur just doing a string parison looking for particular output in the string.

3) What about actually testing the eventual JavaScript output?

Thanks for your input!

Share Improve this question edited Apr 13, 2010 at 11:05 Codebrain asked Apr 13, 2010 at 10:44 CodebrainCodebrain 5,6134 gold badges30 silver badges21 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 2

We created a library specifically for the purpose of embedding JavaScript in a fluent-like syntax into our C# code, and then made it open source.

Have a look at Adam.JSGenerator.

I typically try to create a separate .js file for most/all of my javascript code. Usually I will need to have mon bahvior applied to many elements that are dynamically created by ASP controls or server-side code, so I may not be able to code everything into a .js file.

I've found that the main reason that you want to generate javascript on the server is because you won't know the IDs of elements until the page renders. Therefore, I try to condense that dependency down as much as possibly so that I'm generating as little javascript as possible. For example, in traditional ASP.Net (not MVC) if I were rendering a set of forms such as in the example, each with multiple fields, then I would probably have something in the code behind such as this:

protected void FormRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    Control form = e.Item.FindControl("MyForm");
    ClientScript.RegisterStartupScript(this.GetType(), "prepareForm_" + form.ClientID, @"prepareForm('" + form.ClientID + "');", true);
}

A separate .js file would include the definition of the prepareForm function, which would be something like this:

// define a formPresenter "class" that encapsulates the behavior for a given form
function formPresenter(formId) {

    this.setFirstName = function(value) {
        $("#" + formId + "_FirstName").val(value);
    }

    this.setLastName = function(value) {
        $("#" + formId + "_LastName").val(value);
    }

    // create other functions to handle more plicated logic

    // clear fields
    this.clearInputs = function() {
        this.setFirstName("");
        this.setLastName("");
        //...
    }

    // receive Json object
    this.handleJson = function(json) {
        this.clearInputs();

        // populate fields with json object
        this.setFirstName(json.FirstName);
        this.setLastName(json.LastName);
        //...
    }

    // "constructor" logic
}

function prepareForm(formId) {
    // create a new formPresenter object and shove it onto the specified element as the "presenter"
    document.getElementById(formId).presenter = new formPresenter(formId);
}

Now almost all of your actual logic is in its own .js file, which should be much easier to maintain. If you need to access the formPresenter object for a given form, then you just need to get a reference to whatever element is referenced by the formId parameter and access the presenter variable:

"document.getElementById(" + form.ClientID + ").presenter.handleJson(json);"

Note: Since I've been using JQuery, I've found less of a need to even include any javascript generated by the server. Typically I can find the elements that I need by looking for a specific CSS class name (or something to that effect) and perform whatever setup/initialization I need.

We're doing a lot of JS generation in our project as well, and we're using StringBuilder to do it.

StringBuilder sb = new StringBuilder();

sb.Append("some javascript stuff")
  .Append("some more")
  .AppendFormat("formatted stuff {0}", "here");

return sb.ToString();

It's not pretty, but no solution is going to be.

And concerning testing, we don't actually do any unit tests on the generated code. Before release people go and test all the features to make sure they work as expected.

If you don't care about super duper performance you could use a templating language to generate the javascript.

Then for unit testing you would just fill the templates with their appropriate bindings/variables and then run it through a Javascript evaluator like Rhino or whatever the .NET equivalent is to at least test the syntax if not the actual JS code.

Other than that I would seriously question the design of software that is generating Javascript like this. It also looks like you are using JQuery but are referencing the $ directly which may lead to some problems down the line.

If pilers generating Javascript is one thing (ala GWT) but I would separate your client side JS code as much as possible from your .NET code (not to mention your .NET code looks like server side JS talk about confusing).

This in vogue kind of design of separating the client crap from the server is known as SOFEA. I let you google that.

本文标签: Generating JavaScript in C and subsequent testingStack Overflow