7

一般的な方法で、コントローラーから特定のモデルプロパティに焦点を当てるビューを取得する方法が必要です。

私がこれまでに持っているものは次のとおりです。


コントローラ:

// 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);

意見

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

私が今直面している問題は、ビューのFocusScriptメソッドで、フォーカスするプロパティの戻りタイプがわからないことと(System.Linq.Expressions.Expression<Func<TModel, object>>)、オブジェクトを返さないプロパティに対してキャストが失敗することです。

コントローラーがフォーカスしたいプロパティの戻りタイプがわからないため、プロパティの2番目のジェネリックパラメーターを追加することはできません。

ビュー拡張メソッドFocusScriptを一般的な方法で記述して、さまざまな戻り値タイプのプロパティで使用できるようにするにはどうすればよいですか?


なぜ問題があるのですか?

コントローラーでフォーカスしたいコントロールのIDを渡すだけで、JavaScriptにそのIDを読み取らせ、コントロールを見つけてフォーカスさせることができます。ただし、ビューに属するもの(コントロールのID)をコントローラーにハードコーディングするのは好きではありません。メソッドに必要なプロパティを伝えたいので、ビューが通常コントロールのIDを取得/作成するのと同じ方法で使用するIDを知っている必要があります。

私がモデルを持っているとしましょう:

class MyModel
{
    public int IntProperty { get; set; }
    public string StringProperty { get; set; }
}

コントローラのさまざまな場所で、1つの異なるフィールドに焦点を当てたいと思います。

FocusOnField((MyModel m) => m.IntProperty);
...
FocusOnField((MyModel m) => m.StringProperty);

ここで、前者の場合、式は整数を返す関数であり、後者の場合、式は文字列を返します。結果として、ViewData ["FieldToFocus"]を(に渡すためにIdFor<>())何にキャストするかがわかりません。これは、プロパティによって異なるためです。

4

4 に答える 4

5

私はあなたの問題の解決策を思いついたと思います-それは私の環境で機能しますが、それから私はあなたのコードがおそらくどのように見えるかを推測しなければなりませんでした。

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>");
    }
}

ViewData["FieldToFocus"]もっとエレガントな方法があるかもしれませんが、結局のところ、オブジェクト(つまりreturn type of )を正しい式ツリーにキャストしようとしているのですExpression<Func<TViewModel, TProperty>>が、あなたが言ったように、TPropertyが何であるかわからないと思いますする必要があります。

また、これを機能させるには、別の静的メソッドを追加する必要GetIdForがありました。現時点では、拡張メソッドを呼び出す方法がよくわからないためです。IdFor拡張メソッドを呼び出すための単なるラッパーです。

冗長性を減らすことができます(ただし、おそらく読みにくくなります)。

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 });

最後に考えたのは、の出力はHtmlHelper.IdForこれまでとは異なるExpressionHelper.GetExpressionTextでしょうか?IdForを完全には理解していないので、プロパティ名と一致する文字列が常に表示されるのではないかと思います。

ViewData["FieldToFocus"] = ExpressionHelper.GetExpressionText(fieldExpression);
于 2012-07-17T06:07:33.610 に答える
2

LambdaExpressionを使用するのと同じくらい簡単です。道を行く必要はありません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;
        }
    }
}

次に、コントローラーで:

public ActionResult Index()
{
    FocusOnField((MyModel m) => m.IntProperty);
    return View(new MyModel());
}

そしてあなたの見解では:

@model MyModel

@Html.FocusScript()

これは、コントローラーのアクションがフォーカスを設定しているという事実をコメントなしで残していると言われています。

于 2012-07-17T06:41:25.063 に答える
1

すぐに使用できるメタデータを利用して、プロパティIDなどを提供できます。

そして、あなたの拡張機能で:

      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);

    }
于 2012-07-17T13:18:51.973 に答える
-1

何かが足りないかもしれませんが、FocusOnField関数にフィールド名を渡しているので、ビューのデフォルトIDとなるフィールド名の文字列も渡してViewData値を設定することはできませんか? ?次に、JavaScriptは次のようなことを行うことができます...

<script type="text/javascript">

    onload = focusonme;
    function focusonme() {
        var element = document.getElementById(@ViewData["FieldToFocus"]);
        element.focus();
    }

</script>

たぶんこれはあなたが求めているものではないかもしれませんが、JSは間違いなくこのように機能します...

于 2012-07-16T16:02:53.203 に答える