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.TestCo
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>
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>
};
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.
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.