Привязка модели ASP.Net MVC к JSON в EditorFor
Я хочу иметь возможность передавать информацию из моей модели представления в контроллер через JSON в скрытых текстовых полях. Я работаю с полигонами в API карт Google. Поскольку пользователь редактирует многоугольник, я сохраняю вершины в скрытом вводе через JavaScript.
var p = mypolygon.getPath().getArray();
var s = '';
for (var i = 0; i < p.length; i++)
s += ((i > 0) ? ', ' : '') + '{ lat: ' + p[i].lat() + ', lng: ' + p[i].lng() + ' }';
$('#@Html.IdFor(m => m.GeofencePoints)').val('[' + s + ']');
Это приводит к этому:
<input id="GeofencePoints" name="GeofencePoints" type="hidden" value="[{ lat: 38.221276965853264, lng: -97.6892964859955 }, { lat: 38.21294239796929, lng: -97.68770861825868 }, { lat: 38.2122680083775, lng: -97.67782997884831 }, { lat: 38.220434074436966, lng: -97.67787289419255 }]">
Я хотел бы иметь следующую модель представления, привязанную к представлению:
public class MyMapViewModel
{
public GoogleMapPoint[] GeofencePoints {get;set;}
public string OtherProperty {get;set;}
}
public class GoogleMapPoint
{
public double lat {get;set;}
public double lng {get;set;}
}
Это немного отличается от примеров, которые я видел, так как я хочу, чтобы только одно из моих свойств было опубликовано как Json. Кто-нибудь может указать мне правильное направление? Я знаю, что могу просто передать его как строку и сериализовать / десериализовать самостоятельно. Тем не менее, я надеялся на элегантное решение для покупателя.
ОБНОВИТЬ
Я нашел общее решение на основе этого поста, который нашел: http://mkramar.blogspot.com/2011/05/mvc-complex-model-postback-bind-field.html
public class JsonBindableAttribute : Attribute
{
}
public class MyModelBinder : DefaultModelBinder
{
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
if (propertyDescriptor.Attributes.OfType<Attribute>().Any(x => (x is JsonBindableAttribute)))
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
return JsonConvert.DeserializeObject(value, propertyDescriptor.PropertyType);
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}
В моей модели:
[JsonBindable]
[UIHint("GoogleMapPoints")]
public GoogleMapPoint[] GeofencePoints { get; set; }
А потом в global.asax Application_Start()
ModelBinders.Binders.DefaultBinder = new MyModelBinder();
К сожалению, это только доходит до меня. Это отлично связывает значения формы с моим классом. Однако это не решает рендеринг свойства как Json. Как вы можете видеть, я создал собственный редактор GoogleMapPoints.cshtml, который мне, по сути, нужно воссоздать для каждого класса, который будет иметь jsonbindable.
@model IEnumerable<GoogleMapPoint>
@Html.Hidden("", Newtonsoft.Json.JsonConvert.SerializeObject(Model))
Кто-нибудь знает способ иметь общий настраиваемый редактор, который бы обращал внимание на атрибуты, а не на типы, чтобы EditorFor для свойств, окрашенных с помощью моего атрибута JsonBindable, всегда отображался как Json в скрытом поле независимо от типа / класса?
1 ответ
Вы можете создать связыватель модели для этой конкретной модели. Это расширит связыватель по умолчанию, добавив определенную логику для свойства, которое содержит точки карты, десериализовав json из параметра запроса.
[ModelBinder(typeof(MyMapModelBinder))]
public class MyMapViewModel
{
public List<GoogleMapPoint> GeofencePoints { get; set; }
public string OtherProperty { get; set; }
}
public class GoogleMapPoint
{
public double lat { get; set; }
public double lng { get; set; }
}
public class MyMapModelBinder : DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.Name == "GeofencePoints")
{
var model = bindingContext.Model as MyMapViewModel;
if (model != null)
{
var value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
var jsonMapPoints = value.AttemptedValue;
if (String.IsNullOrEmpty(jsonMapPoints))
return ;
MyMapViewModel mapModel = model as MyMapViewModel;
JavaScriptSerializer serializer = new JavaScriptSerializer();
mapModel.GeofencePoints = (List<GoogleMapPoint>)serializer.Deserialize(jsonMapPoints, typeof(List<GoogleMapPoint>));
return;
}
}
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}