2016-12-21 2 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); 
} 

первое выражение цели свойство, которое будет связано в контроле и у меня нет проблем получать идентификатор и имя атрибуты для него. Второе задает свойство, из которого будет выполняться хелпер, но когда я получаю метод GetControlName, выражение ExpressionHelper.GetExpressionText (выражение) возвращает пустую строку вместо имени свойства. Я добавил часы на «выражение», чтобы проверить, что происходит не так, и его значение следующим образом:

{model => Convert(model.TopCategoryId)} 

В то время как я получаю следующее значение, когда я получаю имя свойства для первого выражения:

{model => model.CategoryId} 

Я действительно не понимаю, почему существует разница между этими двумя выражениями. Вот как я называю помощника на мой взгляд, в случае, если это отношение так или иначе:

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

Любая идея, что здесь происходит?

+1

Вы понимаете, что это никогда не даст вам исправить 2 способ модель связывания, и никакой проверки на стороне клиента , –

+0

Нет, я этого не сделал. На самом деле у меня нет большого опыта создания собственных помощников по созданию баз данных. Есть ли у вас какая-то полезная документация по этому вопросу? – ZipionLive

+1

Во-первых, проблема заключается в том, что 'TProperty' в' expression' совпадает с 'TProperty' в сигнатуре метода (OK), но' TProperty' в 'cascadeFrom' не так, что он не может быть разрешен. –

ответ

0

После использования хакерского обходного пути в течение некоторого времени, я, наконец, понял это. Как отметил Стивен Мюке, проблема возникла из-за использования типа TProperty как для «выражения», так и для «cascadeFrom». Итак, вот как правильно (ну, вроде) решить эту проблему:

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)); 
    }); 
}; 
Смежные вопросы