c# - class library entity framework core
Pasar la expresión de propiedad del modelo para ver (4)
Creo que he encontrado una solución a su problema: funciona en mi entorno, pero luego tuve que adivinar cómo se verá su código.
public static class ViewPageExtensions
{
public static MvcHtmlString GetIdFor<TViewModel, TProperty>(ViewPage<TViewModel> viewPage, Expression<Func<TViewModel, TProperty>> expression)
{
return viewPage.Html.IdFor(expression);
}
public static MvcHtmlString FocusScript<TViewModel>(this ViewPage<TViewModel> viewPage)
{
if (viewPage.ViewData["FieldToFocus"] == null)
return MvcHtmlString.Empty;
object expression = viewPage.ViewData["FieldToFocus"];
Type expressionType = expression.GetType(); // expressionType = Expression<Func<TViewModel, TProperty>>
Type functionType = expressionType.GetGenericArguments()[0]; // functionType = Func<TViewModel, TProperty>
Type[] functionGenericArguments = functionType.GetGenericArguments(); // functionGenericArguments = [TViewModel, TProperty]
System.Reflection.MethodInfo method = typeof(ViewPageExtensions).GetMethod("GetIdFor").MakeGenericMethod(functionGenericArguments); // method = GetIdFor<TViewModel, TProperty>
MvcHtmlString id = (MvcHtmlString)method.Invoke(null, new[] { viewPage, expression }); // Call GetIdFor<TViewModel, TProperty>(viewPage, expression);
return MvcHtmlString.Create(
@"<script type=""text/javascript"" language=""javascript"">
$(document).ready(function() {
setTimeout(function() {
$(''#" + id + @"'').focus();
}, 500);
});
</script>");
}
}
Tal vez haya una manera más elegante de hacerlo, pero creo que lo que se reduce es intentar lanzar un objeto (es decir, devolver el tipo de ViewData["FieldToFocus"]
) al árbol de Expression<Func<TViewModel, TProperty>>
correcto Expression<Func<TViewModel, TProperty>>
pero como dijiste, no sabes lo que TProperty debería ser.
También para hacer esto, tuve que agregar otro método estático a GetIdFor
porque de momento no estoy muy seguro de cómo invocar un método de extensión. Es solo un contenedor para llamar al método de extensión IdFor
.
Podría hacer que sea menos detallado (pero probablemente menos legible).
object expression = viewPage.ViewData["FieldToFocus"];
MethodInfo method = typeof(ViewPageExtensions).GetMethod("GetIdFor")
.MakeGenericMethod(expression.GetType().GetGenericArguments()[0].GetGenericArguments());
MvcHtmlString id = (MvcHtmlString)method.Invoke(null, new[] { viewPage, expression });
Un pensamiento final: ¿la salida de HtmlHelper.IdFor
alguna vez diferiría de ExpressionHelper.GetExpressionText
? No entiendo completamente IdFor, y me pregunto si siempre te dará una cadena que coincida con el nombre de la propiedad.
ViewData["FieldToFocus"] = ExpressionHelper.GetExpressionText(fieldExpression);
Quiero tener una forma de obtener una vista para enfocarse en una propiedad de modelo particular desde un controlador de una manera genérica.
Lo que tengo hasta ahora es:
Controlador:
// To become an extension/base class method
private void FocusOnField<TModel, TProperty>(Expression<Func<TModel, TProperty>> fieldExpression)
{
ViewData["FieldToFocus"] = fieldExpression;
}
...
FocusOnField((ConcreteModelClass m) => m.MyProperty);
Ver
public static class ViewPageExtensions
{
public static MvcHtmlString FocusScript<TModel>(this ViewPage<TModel> viewPage)
{
if (viewPage.ViewData["FieldToFocus"] != null)
{
return MvcHtmlString.Create(
@"<script type=""text/javascript"" language=""javascript"">
$(document).ready(function() {
setTimeout(function() {
$(''#" + viewPage.Html.IdFor((System.Linq.Expressions.Expression<Func<TModel, object>>)viewPage.ViewData["FieldToFocus"]) + @"'').focus();
}, 500);
});
</script>");
}
else
{
return MvcHtmlString.Empty;
}
}
}
El problema al que me enfrento ahora es que, en el método FocusScript
la vista, no conozco el tipo de devolución de la propiedad en la que enfocarme y el fundido en (System.Linq.Expressions.Expression<Func<TModel, object>>)
falla para cualquier propiedad que no devuelve un objeto.
No puedo agregar un segundo parámetro genérico para la propiedad porque no sé cuál es el tipo de devolución de la propiedad en la que el controlador quiere que me centre.
¿Cómo puedo escribir mi método de extensión de vista FocusScript
de una manera genérica para que pueda ser utilizado con propiedades de diferentes tipos de devolución?
¿Por que hay un problema?
Sé que podría pasar el ID del control en el que quiero centrarme en el controlador y tener JavaScript para leer esa identificación, encontrar el control y enfocarme en él. Sin embargo, no me gusta tener algo que pertenece a la vista (el Id de un control) codificado en el controlador. Quiero decirle al método qué propiedad quiero y debe saber qué Id usar de la misma manera que la vista normalmente obtiene / crea el Id para un control.
Digamos que tengo un modelo:
class MyModel
{
public int IntProperty { get; set; }
public string StringProperty { get; set; }
}
En diferentes lugares en el controlador quiero enfocar un campo diferente:
FocusOnField((MyModel m) => m.IntProperty);
...
FocusOnField((MyModel m) => m.StringProperty);
Ahora, en el primer caso, la expresión es una función que devuelve un entero, en el segundo caso devuelve una cadena. Como resultado, no sé para qué lanzar mi ViewData ["FieldToFocus"] a (para pasarlo a IdFor<>()
) ya que varía según la propiedad.
Es tan simple como usar LambdaExpression. No es necesario ir al modo Expression<Func<TModel, TProperty>>
.
public static class HtmlExtensions
{
public static string IdFor(
this HtmlHelper htmlHelper,
LambdaExpression expression
)
{
var id = ExpressionHelper.GetExpressionText(expression);
return htmlHelper.ViewData.TemplateInfo.GetFullHtmlFieldId(id);
}
public static MvcHtmlString FocusScript(
this HtmlHelper htmlHelper
)
{
if (htmlHelper.ViewData["FieldToFocus"] != null)
{
return MvcHtmlString.Create(
@"<script type=""text/javascript"">
$(document).ready(function() {
setTimeout(function() {
$(''#" + htmlHelper.IdFor((LambdaExpression)htmlHelper.ViewData["FieldToFocus"]) + @"'').focus();
}, 500);
});
</script>");
}
else
{
return MvcHtmlString.Empty;
}
}
}
luego en tu controlador:
public ActionResult Index()
{
FocusOnField((MyModel m) => m.IntProperty);
return View(new MyModel());
}
y en su opinión:
@model MyModel
@Html.FocusScript()
Dicho esto, me voy sin comentar el hecho de que una acción del controlador está fijando un foco .
Puede aprovechar su Metadata de fábrica para proporcionarle el Id de la propiedad, etc.
Y luego en tus extensiones:
public static MvcHtmlString FocusFieldFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
var fullPropertyName = html.ViewData.TemplateInfo.GetFullHtmlFieldId(metadata.PropertyName);
var jsonData =
@"<script type=""text/javascript"">
$(document).ready(function() {
setTimeout(function() {
$(''#" + fullPropertyName + @"'').focus();
}, 500);
});
</script>";
return MvcHtmlString.Create(jsonData);
}
Tal vez me falta algo, pero ya que está pasando el nombre del campo a su función FocusOnField, ¿no podría simplemente pasar también la cadena del nombre del campo que será el ID predeterminado en la vista y luego establecer un valor de ViewData? ? Entonces tu javascript podría hacer algo como ...
<script type="text/javascript">
onload = focusonme;
function focusonme() {
var element = document.getElementById(@ViewData["FieldToFocus"]);
element.focus();
}
</script>
Quizás esto no sea exactamente lo que buscas, pero el JS definitivamente funcionará de esta manera ...