Можно ли пометить функцию С# или VB для вывода в виде Javascript в ASP.NET MVC4?

У меня есть метод расширения 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 как javascript на странице MVC.

Есть ли что-то вроде встроенного в .NET 4/ASP.NET MVC 4/Razor 2? Или любая другая технология, которая может достичь чего-то подобного?

Примеры приведены в VB, но решения в С# вполне приемлемы.

Разъяснение:

@gideon: обратите внимание, что jsCallbackFunction принимает два параметра e и i. Однако метод расширения HtmlHelper просто запрашивает строку (имя функции обратного вызова javascript) и не указывает, какие параметры может выполнять эта функция. Проблема, которую я пытаюсь решить, вдвойне.

  • Во-первых, недостающие подсказки параметров. Тип делегирования .NET, переданный вместо строки "имя обратного вызова javascript", выполнит это. Я открыт для других решений для этого. Я знаю комментарии XML. Они на самом деле не являются решением.

  • Во-вторых, старайтесь, чтобы программист страниц работал на одном языке. Переключение между javascript и VB (или js и С#) требует (по крайней мере для меня) дорогого переключателя контекста. Мой мозг не делает переход быстро. Ведение моей работы в VB или С# является более продуктивным и экономически эффективным. Таким образом, возможность написать функцию на языке .NET и скомпилировать ее в javascript в контексте представления ASP.NET MVC/бритвы - вот что я здесь делаю.

@TyreeJackson: SomethingCool - это метод расширения HtmlHelper, который я бы написал, который выводит html и javascript. Часть выхода javascript должна вызывать функцию пользователя (программиста), используемую для принятия некоторых решений. Подумайте, что это похоже на функцию успеха или сбоя, которую вы подаете на вызов ajax.

Ответ 1

Хотя я не могу дать вам полную опцию transpiler/compiler, так как это будет огромная работа, я могу предложить следующее, чтобы помочь с поддержкой intellisense и испускать функции и вызовы.

Вот код инфраструктуры. Вам нужно будет заполнить функции 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. Я не связан с проектом и не могу говорить с ним о стабильности, точности и эффективности, но это звучит интересно и может помочь вам.

Ответ 2

Это не совсем то же самое, но атрибуты WebMethod и PageMethod могут помочь сделать это намного более управляемым IMHO.

См. также как вызвать метод ASP.NET С# с помощью javascript

Вы также можете использовать элемент управления WebBrowser для создания реализаций вашего объекта, и это походит на Node.js по большей части.

См. также Чтение переменной Javascript из управления веб-браузером

Ответ 3

Вот мой тест SignalR (please read the comments in the question):

Класс ChatHub:

 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");
        });
  });

Это приводит к следующему выводу на моем сайте:

введите описание изображения здесь