2016-12-21 3 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를 받고없고 이름이 속성을 가지고있다. 두 번째는 도우미가 캐스케이드하는 속성을 대상으로하지만 GetControlName 메서드를 통과 할 때 ExpressionHelper.GetExpressionText (expression)는 속성 이름 대신 빈 문자열을 반환합니다. 내가 잘못 무슨 일이 있었는지 확인하기 위해 "표현"에 시계를 추가, 다음과 같이 그 값은 다음과 같습니다

{model => Convert(model.TopCategoryId)} 

나는 첫 번째 표현식에 대한 속성 이름을지고있어 다음과 같은 값을 얻을 수 있지만 :

{model => model.CategoryId} 

두 표현식 사이에 차이점이 무엇인지 이해하지 못합니다. 내가보기에 도우미를 호출하는 방법을 여기에,의 IT 여하튼 관련이있는 경우 : 여기에 무슨 일이 일어나고 있는지

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

어떤 생각?

+1

당신이 포기하지 않을 것을 깨닫게 않습니다

방식으로

[편집]

, 여기에이 일을 할 수있는 jQuery 코드입니다 . –

+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) 
{ 
    [...] 
} 

누군가를 도울 수있는 희망! 당신이 결합 방식 모델없이 클라이언트 측 유효성 검사를 해결

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