3

How can I serialize a client function to a json object? (similar to how kendo controls work)

This is what I have so far...

View:

@Html.TestControl(@<text>function(){ alert("test"); }</text>)

Control Helper:

public static HtmlString TestControl<TModel>(this HtmlHelper<TModel> helper, Func<object, object> onSubmit)

{
    var obj = new {onSubmit = onSubmit.Invoke(null) };
    var jsonObj = new JavaScriptSerializer().Serialize(obj);
    return new HtmlString(string.Format("<script>var obj = {0};</script>", jsonObj));
}

Output:

<script>var obj = {"onSubmit":{}};</script>

Desired Output:

<script>var obj = {"onSubmit": function(){ alert("test"); }};</script>

I can see that the value of obj.onSubmit in the helper is the function... but how can I get the function to serialize and appear in the json object (as a function)?

UPDATE:

Using @<text> to define the anonymous function inline is preferred. We use Kendo controls with this syntax and the goal is to keep the code consistent.

Here is an example on the syntax for kendo controls: http://docs.kendoui.com/api/wrappers/aspnet-mvc/Kendo.Mvc.UI.Fluent/UploadEventBuilder

ryan
  • 481
  • 7
  • 11

4 Answers4

2

I spent more time searching around and found similar posts but didn't see a solution:

Serializing a function as a parameter in json using C#

JSON serializing an object with function parameter

Finally got it to work using the Json.net library. Using the JRaw class will generate a json object with the onSubmit property defined as a function.

Json.net documentation: http://james.newtonking.com/projects/json/help/html/SerializeRawJson.htm

Updated Control Helper:

public static HtmlString TestControl<TModel>(this HtmlHelper<TModel> helper, Func<object, object> onSubmit)
{
    var obj = new { onSubmit = new JRaw(onSubmit.Invoke(null).ToString()) };
    var jsonObj = JsonConvert.SerializeObject(obj);
    return new HtmlString(string.Format("<script>var obj = {0};</script>", jsonObj));
}

Output:

<script>var obj = {"onSubmit":function(){alert("test");}};</script>

Now I can call obj.onSubmit() on the client to call the function.

Community
  • 1
  • 1
ryan
  • 481
  • 7
  • 11
1

Based on @ryan's answer, I upgrade a little bit to more like kendoUI.

public class TextBox : BaseControl
{
    [JsonProperty("onChange", NullValueHandling = NullValueHandling.Ignore)]
    [JsonConverter(typeof(JsFunctionConverter))]
    public Func<object, object> OnChange { get; set; }

    public override MvcHtmlString Render()
    {
        // Create html
        // <input />
        _tagBuilder = new TagBuilder("input");

        // Add script
        StringBuilder builder = new StringBuilder(base.Render().ToString());
        string script = @"<script>var {0} = {{name: '{0}', scope : angular.element($('#{0}')).scope(), options: {1}}}; textBox({0});</script>";

        BuildOptions();
        builder.AppendFormat(script, Name, JsonConvert.SerializeObject(this));
        return MvcHtmlString.Create(builder.ToString());
    }

Here is JsFunctionConverter:

public class JsFunctionConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof (string) || objectType == typeof (Func<object,object>);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JRaw jRawData;

        if (value.GetType() == typeof(Func<object, object>))
        {
            jRawData = new JRaw(((Func<object,object>)value).Invoke(null));
        }
        else
        {
            jRawData = new JRaw(value);
        }

        jRawData.WriteTo(writer);
    }
}

And you can do it like KendoUI

var textBox = new TextBox
{
    OnChange = @<text>
                    function(e){

                            return e;
                    }
                </text>
};
0

Just make this onSubmit argument being a string and use its value as is. You will no longer need the text tags when calling the helper.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • on the client side, I would like to call the function (ex: obj.onSubmit())... if the value is a string the I can't call onSubmit as a function – ryan May 24 '13 at 17:03
  • I think yoy are confusing a function on the server with function on the client. Make the parameter a syring and don't serialize it on the server, just output it as is to the client so that you could call it as you want. – Darin Dimitrov May 25 '13 at 09:54
  • I don't think I'm confusing the two. The function being defined in the @ block is a javascript function. The control helper (server side) is simply building the js code needed to create a json object that I can later evolve into defining options for jquery methods. – ryan May 28 '13 at 14:08
0

You are confusing a function on the server with a function on the client. You're actually executing onSubmit on the server and the result of that is being put into your object which you serialize into json.

Instead:

Make your 2nd parameter to TestControl a string. Also, don't serialize an object. Instead, create your json manually.

public static HtmlString TestControl<TModel>(this HtmlHelper<TModel> helper, string onSubmit)
{
  string jsonObj = String.Format("{{ \"onSubmit\": {0} }}", onSubmit);
  return new HtmlString(string.Format("<script>var obj = {0};</script>", jsonObj));
}

Then you can use:

@Html.TestControl("function(){ alert('test'); }")

Your jsonObj will be:

{ "onSubmit": function(){ alert('test'); } }

and finally, your TestControl() method will return

<script>var obj = { "onSubmit": function(){ alert("test"); } };</script>
Matt Houser
  • 33,983
  • 6
  • 70
  • 88
  • We use Kendo controls with the @ syntax. I'd like to use @ to keep the syntax consistent between kendo controls and custom controls. Can you provide an example using @? From what I can see from the kendo signatures, this is accomplished by making the param a Func. – ryan May 28 '13 at 14:17