2016-12-21 6 views
0

AJAXを使用して自分自身を埋めるカスケードドロップダウンリストを作成できるヘルパーを作成しています。ヘルパーメソッドは次のようになります。ExpressionHelperで式からコントロール名を取得できません

public static MvcHtmlString AjaxSelectFor<TModel, TProperty>(
    this HtmlHelper<TModel> html, 
    Expression<Func<TModel, TProperty>> expression, 
    Expression<Func<TModel, TProperty>> cascadeFrom, 
    string sourceUrl, 
    bool withEmpty = false) 
{ 
    string controlFullName = html.GetControlName(expression); 
    string cascadeFromFullName = html.GetControlName(cascadeFrom); 

    var selectBuilder = GetBaseSelect(controlFullName.GetControlId(), controlFullName, sourceUrl, withEmpty); 
    selectBuilder.Attributes.Add("data-selected-id", html.GetValue(expression)); 
    selectBuilder.Attributes.Add("data-cascade-from", "#" + cascadeFromFullName.GetControlId()); 

    return new MvcHtmlString(selectBuilder.ToString()); 
} 

private static TagBuilder GetBaseSelect(string controlId, string controlName, string sourceUrl, bool withEmpty) 
{ 
    var selectBuilder = new TagBuilder("select"); 
    selectBuilder.Attributes.Add("id", controlId); 
    selectBuilder.Attributes.Add("name", controlName); 
    selectBuilder.Attributes.Add("data-toggle", "ajaxSelect"); 
    selectBuilder.Attributes.Add("data-source-url", sourceUrl); 
    selectBuilder.Attributes.Add("data-with-empty", withEmpty.ToString()); 
    selectBuilder.AddCssClass("form-control"); 
    return selectBuilder; 
} 

internal static string GetControlName<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression) 
{ 
    string controlName = ExpressionHelper.GetExpressionText(expression); 
    return html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(controlName); 
} 

internal static string GetControlId(this string controlName) 
{ 
    return TagBuilder.CreateSanitizedId(controlName); 
} 

最初の式は、コントロールにバインドされ、私は何の問題それのためにidとname属性を取得していませんプロパティを対象としています。 2番目はヘルパーがカスケードするプロパティをターゲットにしますが、GetControlNameメソッドを実行すると、ExpressionHelper.GetExpressionText(expression)はプロパティ名の代わりに空の文字列を返します。私が間違って何が起こっていたかをチェックする「表現」に時計を追加し、次のようにその値は次のとおりです。

{model => Convert(model.TopCategoryId)} 

私は最初の式のプロパティ名を取得していたときに、私は次の値を取得している間:

{model => model.CategoryId} 

2つの表現に違いがあるのはなぜか分かりません。

@Html.AjaxSelectFor(model => model.CategoryId, model => model.TopCategoryId, "/api/Categories/GetSelectList", true) 

ここで何が起こっているすべてのアイデア:ここでは、私は場合には、それはとにかく関連ですが、私の見解でヘルパーを呼び出す方法ですか?

+1

あなたは、これはあなたが2ウェイモデルは、結合、およびなしクライアント側の検証を修正与えることはありませんことを実現します。 –

+0

いいえ、私はしませんでした。私は実際に自分のデータバインドされたヘルパーを作成する経験はあまりありません。あなたはその件に関していくつかの有益な文書を持っていますか? – ZipionLive

+1

まず、 'expression'の' TProperty'はメソッドシグネチャ(OK)の 'TProperty'と同じですが、' cascadeFrom'の 'TProperty'は解決できないという問題があります。 –

答えて

0

しばらくの間ハックのある回避策を使用した後、私はそれを最終的に考え出しました。 Stephen Muecke氏は、 "expression"と "cascadeFrom"の両方にTProperty型を使用して問題が発生したと指摘しました。誰かを助けるかもしれない

public static MvcHtmlString AjaxSelectFor<TModel, TProperty, TCascadeProperty>(
    this HtmlHelper<TModel> html, 
    Expression<Func<TModel, TProperty>> expression, 
    Expression<Func<TModel, TCascadeProperty>> cascadeFrom, 
    string sourceUrl, 
    bool withEmpty = false) 
{ 
    [...] 
} 

希望:だから、ここで適切に(まあ、一種の)この問題を解決する方法です!

[編集]

ところでが、ここでこの作品を作るためのjQueryのコードがあります:

var common = {}; 

$(document).ready(function() { 
    common.bindAjaxSelect(); 
}) 

common.bindAjaxSelect = function() { 
    $('[data-toggle="ajaxSelect"]').each(function() { 
     common.clearSelect($(this)); 
    }); 
    $('[data-toggle="ajaxSelect"]').not('[data-cascade-from]').each(function() { 
     common.fillAjaxSelect($(this)); 
     $(this).on('change', function() { 
      common.bindAjaxSelectCascade('#' + $(this).attr('id')); 
     }); 
    }); 
}; 

common.bindAjaxSelectCascade = function (selector) { 
    $('[data-toggle="ajaxSelect"][data-cascade-from="' + selector + '"]').each(function() { 
     common.fillAjaxSelect($(this), selector); 
     $(this).unbind('change'); 
     $(this).on('change', function() { 
      common.bindAjaxSelectCascade('#' + $(this).attr('id')); 
     }); 
    }); 
}; 

common.fillAjaxSelect = function (select, cascadeFromSelector) { 
    var controlId = select.attr('id'); 
    var sourceUrl = select.attr('data-source-url'); 
    var withEmpty = select.attr('data-with-empty'); 
    var selectedId = select.attr('data-selected-id'); 
    var parentId = $(cascadeFromSelector).val(); 
    var emptyCheck = withEmpty ? 1 : 0; 

    $('[data-toggle="ajaxSelect"][data-cascade-from="#' + select.attr('id') + '"]').each(function() { 
     common.clearSelect($(this)); 
    }); 

    var requestParameters = parentId === undefined 
     ? { ajax: true, withEmpty: withEmpty } 
     : { ajax: true, parentId: parentId, withEmpty: withEmpty }; 

    $.getJSON(sourceUrl, requestParameters, function (response) { 
     if (response.Success === true) { 
      if (response.Data.length > emptyCheck) { 
       var options = []; 
       $.each(response.Data, function (key, item) { 
        if (selectedId !== undefined && item.Id === selectedId) { 
         options.push('<option value="' + item.Id + '" selected>' + item.Value + '</option>'); 
        } else { 
         options.push('<option value="' + item.Id + '">' + item.Value + '</option>'); 
        } 
       }); 
       select.html(options.join('')); 
       select.enable(); 

       if (selectedId !== undefined && selectedId !== '') { 
        common.bindAjaxSelectCascade('#' + controlId); 
       } 
      } else { 
       common.clearSelect(select); 
      } 
     } else { 
      common.clearSelect(select); 
      //TODO : append error message to page. 
     } 
    }); 
}; 

common.clearSelect = function (select) { 
    select.disable(); 
    select.html(''); 
    $('[data-toggle="ajaxSelect"][data-cascade-from="' + select.attr('id') + '"]').each(function() { 
     common.clearSelect($(this)); 
    }); 
}; 
関連する問題