10

我有一个HtmlHelper将javascript回调函数作为参数的扩展方法。例如:

@Html.SomethingCool("containerName", "jsCallbackFunction")

<script type="javascript">
    function jsCallbackFunction(e, i) {
        alert(e.target.name + ' / ' + i);
    }
</script>

如您所见,javascript 回调函数名称被传递给HtmlHelper扩展方法。这导致开发人员不得不重新参考文档来确定jsCallbackFunction函数需要哪些参数。

我更喜欢这样的东西:

@Html.SomethingCool("containerName", New SomethingCoolCallbackDelegate(Address Of jsCallbackFunction))

<OutputAsJavascript>
Private Sub jsCallbackFunction(e, i)
    '    SOMETHING goes here.  some kind of html dom calls or ???
End Sub

SomethingCoolCallbackDelegate将为目标函数提供代码契约。然后编译器会将 jsCallbackFunction 编译为 MVC 页面上的 javascript。

.NET 4 / ASP.NET MVC 4 / Razor 2 中是否有类似的东西?或者任何其他可以实现类似功能的技术?

示例在 VB 中,但 C# 中的解决方案也完全可以接受。

澄清:

@gideon:注意它jsCallbackFunction有两个参数e, 和i. 但是,HtmlHelper 扩展方法只是要求一个字符串(javascript 回调函数的名称),并没有指明该函数可能采用哪些参数。我要解决的问题有两个。

  • 首先,缺少参数提示。代替“javascript 回调名称”字符串传递的 .NET 委托类型将完成此操作。我愿意接受其他解决方案来实现这一点。我知道 XML 注释。它们并不是真正的解决方案。

  • 其次,试图让页面程序员使用单一语言工作。在 javascript 和 VB(或 js 和 C#)之间切换需要(至少对我而言)昂贵的上下文切换。我的大脑并没有快速过渡。让我在 VB 或 C# 中工作更有效率和成本效益。因此,能够在 ASP.NET MVC/razor 视图的上下文中用 .NET 语言编写函数并将其编译为 javascript,这就是我所追求的。

@TyreeJackson:SomethingCoolHtmlHelper我编写的一种扩展方法,可以输出 html 和 javascript。部分 javascript 输出需要调用用户(程序员)提供的函数来做出一些决定。将其想象为类似于您提供给 ajax 调用的成功或失败函数。

4

3 回答 3

5

虽然我不能为您提供完整的转译器/编译器选项,因为这将是大量工作,但我可以建议以下内容来协助智能感知支持和函数和调用的发出。

这是基础设施代码。您需要完成 getArgumentLiteral 和 getConstantFromArgument 函数来处理您提出的其他情况,但这是一个不错的起点。

public abstract class JavascriptFunction<TFunction, TDelegate> where TFunction : JavascriptFunction<TFunction, TDelegate>, new()
{
    private static  TFunction   instance    = new TFunction();
    private static  string      name        = typeof(TFunction).Name;
    private         string      functionBody;

    protected JavascriptFunction(string functionBody) { this.functionBody = functionBody; }

    public static string Call(Expression<Action<TDelegate>> func)
    {
        return instance.EmitFunctionCall(func);
    }

    public static string EmitFunction()
    {
        return "function " + name + "(" + extractParameterNames() + ")\r\n{\r\n    " + instance.functionBody.Replace("\n", "\n    ") + "\r\n}\r\n";
    }

    private string EmitFunctionCall(Expression<Action<TDelegate>> func)
    {
        return name + "(" + this.extractArgumentValues(((InvocationExpression) func.Body).Arguments) + ");";
    }

    private string extractArgumentValues(System.Collections.ObjectModel.ReadOnlyCollection<Expression> arguments)
    {
        System.Text.StringBuilder   returnString    = new System.Text.StringBuilder();
        string                      commaOrBlank    = "";
        foreach(var argument in arguments)
        {
            returnString.Append(commaOrBlank + this.getArgumentLiteral(argument));
            commaOrBlank    = ", ";
        }
        return returnString.ToString();
    }

    private string getArgumentLiteral(Expression argument)
    {
        if (argument.NodeType == ExpressionType.Constant)   return this.getConstantFromArgument((ConstantExpression) argument);
        else                                                return argument.ToString();
    }

    private string getConstantFromArgument(ConstantExpression constantExpression)
    {
        if (constantExpression.Type == typeof(String))  return "'" + constantExpression.Value.ToString().Replace("'", "\\'") + "'";
        if (constantExpression.Type == typeof(Boolean)) return constantExpression.Value.ToString().ToLower();
        return constantExpression.Value.ToString();
    }

    private static string extractParameterNames()
    {
        System.Text.StringBuilder   returnString    = new System.Text.StringBuilder();
        string                      commaOrBlank    = "";

        MethodInfo method = typeof(TDelegate).GetMethod("Invoke");
        foreach (ParameterInfo param in method.GetParameters())
        {
            returnString.Append(commaOrBlank  + param.Name);
            commaOrBlank = ", ";
        }
        return returnString.ToString();
    }
}

public abstract class CoreJSFunction<TFunction, TDelegate> : JavascriptFunction<TFunction, TDelegate>
    where TFunction : CoreJSFunction<TFunction, TDelegate>, new()
{
    protected CoreJSFunction() : base(null) {}
}

以下是标准函数支持包装器的示例:

public class alert : CoreJSFunction<alert, alert.signature>
{
    public delegate void signature(string message);
}

以下是几个示例 Javascript 函数支持包装器:

public class hello : JavascriptFunction<hello, hello.signature>
{
    public delegate void signature(string world, bool goodByeToo);
    public hello() : base(@"return 'Hello ' + world + (goodByeToo ? '. And good bye too!' : ''") {}
}

public class bye : JavascriptFunction<bye, bye.signature>
{
    public delegate void signature(string friends, bool bestOfLuck);
    public bye() : base(@"return 'Bye ' + friends + (bestOfLuck ? '. And best of luck!' : ''") {}
}

这是一个控制台应用程序,展示了它的用法:

public class TestJavascriptFunctions
{
    static void Main()
    {
        // TODO: Get javascript functions to emit to the client side somehow instead of writing them to the console
        Console.WriteLine(hello.EmitFunction() + bye.EmitFunction());

        // TODO: output calls to javascript function to the client side somehow instead of writing them to the console
        Console.WriteLine(hello.Call(func=>func("Earth", false)));
        Console.WriteLine(bye.Call(func=>func("Jane and John", true)));
        Console.WriteLine(alert.Call(func=>func("Hello World!")));

        Console.ReadKey();
    }
}

这是控制台应用程序的输出:

function hello(world, goodByeToo)
{
    return 'Hello ' + world + (goodByeToo ? '. And good bye too!' : ''
}
function bye(friends, bestOfLuck)
{
    return 'Bye ' + friends + (bestOfLuck ? '. And best of luck!' : ''
}

hello('Earth', false);
bye('Jane and John', true);
alert('Hello World!');

更新

您可能还想查看JSIL。我不隶属于该项目,无法谈论它的稳定性、准确性和有效性,但它听起来很有趣,也许可以帮助你。

于 2015-07-31T21:49:28.087 回答
0

这并不完全相同,但 WebMethod 和 PageMethod 属性可以帮助这变得更易于管理恕我直言。

另请参阅如何使用 javascript 调用 ASP.NET c# 方法

您还可以使用WebBrowser控件来创建您的对象实现,然后这在大多数情况下变得就像这样Node.js

另请参阅从 Web 浏览器控件中读取 Javascript 变量

于 2015-07-31T21:59:22.683 回答
0

这是我的 SignalR 实现测试(please read the comments in the question):

聊天中心类:

 Public Class ChatHub : Inherits Hub
    Public Sub MyTest(ByVal message As String)
        Clients.All.clientFuncTest("Hello from here, your message is: " + message)
    End Sub
 End Class

客户端:

 $(function () {
        // Reference the auto-generated proxy for the hub.
        var chat = $.connection.chatHub;
        //reference to Clients.All.clientFuncTest
        chat.client.clientFuncTest = function(messageFromServer){
            alert(messageFromServer);
        }

        // Start the connection.
        $.connection.hub.start().done(function () {
            //Reference to Public Sub MyTest
            chat.server.myTest("this is a test");
        });
  });

这会在我的站点中产生以下输出:

在此处输入图像描述

于 2015-08-03T16:44:12.480 回答