可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
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.
回答2:
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>
};
回答3:
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.
回答4:
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>