2013-06-07 4 views
1

Я работаю над веб-сайтом запроса проекта и создаю фрагмент, в котором сотрудники, работающие над проектом, могут добавлять оценки за количество времени, которое потребуется для завершения области проекта. Я хотел, чтобы эта страница отправила произвольное количество оценок обратно в действие «Редактировать», но я здесь что-то пропустил.Сильно типизированный вид не инициализируется?

В моем представлении я использую Html.BeginCollectionItem от here помощника, чтобы помочь с материалом AJAX-y в Html.RenderPartial("_WorkEstimateEditorRow", item);.

Edit: я борюсь с частями AJAX этого прямо сейчас - я вижу, что значения возвращаются в Request.Form и свойствах совпадают с моим WorkEstimate класса правильно, но даже когда я изменить действие контроллера чтобы просто принять оценки IEnumerable<WorkEstimate>, она равна нулю.

Это выходное значение из одной строки, используя Html.BeginCollectionItem:

<input data-val="true" data-val-number="The field EstimateId must be a number." data-val-required="The EstimateId field is required." id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__EstimateId" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].EstimateId" value="0" type="hidden"> 
<input id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__Estimator" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].Estimator" value="" type="hidden"> 
<span class="editor-label"> 
    <label for="estimates_d32afd89-987e-4d09-a847-abfc33dde220__WorkArea">Work Area</label> 
</span> 
<span class="editor-field"> 
    <input class="text-box single-line" id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__WorkArea" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].WorkArea" value="" type="text"> 
    <span class="field-validation-valid" data-valmsg-for="estimates[d32afd89-987e-4d09-a847-abfc33dde220].WorkArea" data-valmsg-replace="true"></span> 
</span> 

<span class="editor-label"> 
    <label for="estimates_d32afd89-987e-4d09-a847-abfc33dde220__Hours">Hours</label> 
</span> 
<span class="editor-field"> 
    <input class="text-box single-line" data-val="true" data-val-number="The field Hours must be a number." data-val-required="The Hours field is required." id="estimates_d32afd89-987e-4d09-a847-abfc33dde220__Hours" name="estimates[d32afd89-987e-4d09-a847-abfc33dde220].Hours" value="0" type="number"> 
    <span class="field-validation-valid" data-valmsg-for="estimates[d32afd89-987e-4d09-a847-abfc33dde220].Hours" data-valmsg-replace="true"></span> 
</span> 
<a href="#" class="deleteRow">[x]</a> 
<hr> 

name атрибут является то, что попадается в Request.Form на пост действий. Я также попытался изменить действие своего контроллера, поэтому он получает IEnumerable<WorkEstimate> без каких-либо изменений.

Модель

public class EstimationManager 
{ 
    public EstimationManager() 
    { 
     CurrentUser = new WebUser(); 
     Project = null; 

     EstimationData = new WorkEstimateRepository(); 
     Estimates = new List<WorkEstimate>(); 
    } 

    public EstimationManager(ApprovedProject project, WebUser currentUser) 
     : this(project, currentUser, new WorkEstimateRepository()) 
    { } 

    public EstimationManager(ApprovedProject project, WebUser currentUser, IWorkEstimateRepository repository) 
    { 
     Project = project; 
     CurrentUser = currentUser; 

     EstimationData = repository; 

     Estimates = EstimationData.Get(Project); 
    } 

    IWorkEstimateRepository EstimationData { get; set; } 

    public WebUser CurrentUser { get; set; } 
    public ApprovedProject Project { get; set; } 
    public List<WorkEstimate> Estimates { get; set; } 

    public bool CurrentUserHasWorkerAccess 
    { 
     get 
     { 
      return CurrentUser != null 
       && CurrentUser.AccessLevels.HasWorkerAccess 
       && (Project == null || CurrentUser.AccessLevels.WorkerUnit == Project.CurrentWorkerUnit); 
     } 
    } 
} 

Действие контроллера

public class EstimatesController : BaseSessionController 
{ 
    private IProjectRepository _projects; 
    private IWorkEstimateRepository _estimates; 
    EstimationManager manager; 

    public EstimatesController() 
     : this(new WorkEstimateRepository(), new ProjectRepository()) 
    { } 

    public EstimatesController(IWorkEstimateRepository estimates, IProjectRepository projects) 
    { 
     _estimates = estimates; 
     _projects = projects; 
    } 

    // 
    // GET: /Estimates/Edit/5 

    public ActionResult Edit(int id) 
    { 
     ApprovedProject details = _projects.Get(id); 
     manager = new EstimationManager(details, CurrentUser, _estimates); 

     return View(manager); 
    } 

    // 
    // POST: /Estimates/Edit/5 

    [HttpPost] 
    public ActionResult Edit(int id, FormCollection collection) 
    { 
     ApprovedProject details = _projects.Get(id); 
     manager = new EstimationManager(details, CurrentUser, _estimates); 

     if (TryUpdateModel(manager) 
      && _estimates.TrySave(manager)) 
     { 
      return RedirectToAction("Details", new { id = id }); 
     } 
     else 
     { 
      foreach (WorkEstimate item in manager.Estimates) 
      { 
       foreach (RuleViolation currentViolation in item.GetRuleViolations()) 
       { 
        ModelState.AddModelError(item.WorkArea + currentViolation.PropertyName, currentViolation.ErrorMessage); 
       } 
      } 

      return View(manager); 
     } 
    } 
} 

Посмотреть

@model ProjectRequests.Web.Models.Project.Estimates.EstimationManager 

@{ 
    ViewBag.Title = "Edit Estimate Details for " + Model.Project.Name; 
} 

<h2>Edit Estimate Details for @Model.Project.Name</h2> 

@using (Html.BeginForm()) { 
    @Html.AntiForgeryToken() 
    @Html.ValidationSummary(true) 

    <fieldset> 
     <legend>Estimation Manager</legend> 
     <span id="editorRows"> 
      @foreach (ProjectRequests.Web.Models.Project.Estimates.WorkEstimate item in Model.Estimates) 
      { 
       if (Model.CurrentUser == item.Estimator) 
       { 
        Html.RenderPartial("_WorkEstimateEditorRow", item); 
       } 
       else 
       { 
        Html.DisplayFor(modelItem => item); 
       } 
      } 
     </span> 

     @if (Model.CurrentUserHasWorkerAccess) 
     { 
      @Html.ActionLink("Add another estimate.", "BlankEstimateRow", null, new { id = "addItem" }) 

      <p> 
       <input type="submit" value="Save" /> 
      </p> 
     } 
    </fieldset> 
} 

<div> 
    @Html.ActionLink("Back to List", "Index") 
</div> 

@section Scripts { 
    @Scripts.Render("~/bundles/jqueryval") 

    <script type="text/javascript"> 
     $().ready(function() { 
      $("#addItem").click(function() { 
       $.ajax({ 
        url: this.href, 
        cache: false, 
        success: function (html) { 
         $("#editorRows").append(html); 
        } 
       }); 
       return false; 
      }); 

      $("#editorRows").on("click", ".deleteRow", function() { 
       $(this).closest(".editorRow").remove(); 
       return false; 
      }); 
     }); 
    </script> 

} 

Когда подпись моей Редактировать действие, я s public ActionResult Edit(int id, FormCollection collection), представление возвращает соответствующие данные, но TryUpdateModel фактически не обновляет свойство Estimates. И наоборот, public ActionResult Edit(int id, EstimationManager newManager) не устанавливает Estimates. Идея вручную просеивать через FormCollection, чтобы вытащить значения из страницы, сильно пахнет запахом большого кода, и это оставляет мне ощущение, что я должен решать эту проблему по-другому.

ответ

0

У меня была аналогичная проблема. Чтобы получить MVC для работы с коллекциями, я нашел, что лучше всего использовать цикл for вместо цикла в представлении. Когда вы используете помощник Html, получите доступ к значениям с помощью индексатора в своем выражении, это приведет к тому, что Razor будет использовать этот индекс для имени элемента управления формой. На вашем контроллере, примите коллекцию (массив наверняка, вы можете уйти с IEnumerable<>), и он должен работать.

Поскольку вы используете класс как свою модель, а не коллекцию напрямую, вам может потребоваться либо использовать параметр BindAttribute для вашего параметра действия, чтобы MVC знал, что данные запроса будут иметь префикс, или использовать другой класс, который имеет свойство с тем же именем, что и свойство в вашем классе EstimationManager в качестве параметра действия.

Просто посмотрите источник на выходе из MVC, как только вы начнете получать доступ с помощью индексатора, и я думаю, что имена форм помогут вам понять, что происходит.

+0

Часть 'foreach' действительно даже не выполняется. Я просто обрабатываю раздел AJAX прямо сейчас (что неясно в вопросе). Я попытался использовать 'BindAttribute', как вы предложили, но мне не повезло. Я обновлю вопрос с помощью некоторых из моего HTML-кода. – jwiscarson

0

Похоже, вы перекручивание через Estimates коллекции модели, и принимая ценность одного WorkEstimate имущества от каждого Estimate, то либо рендеринг частичного вида с этой WorkEstimate или сделать ярлык с ним.

Я предполагаю, что, поскольку в представлении нет конкретных Estimate элементов с индексами на нем, связующее устройство модели не может понять, что они должны быть связаны с коллекцией Estimates, в примере, где вы находитесь используя public ActionResult Edit(int id, EstimationManager newManager).

Кроме того, очевидно, что значения индекса важны для предметов коллекции; она работает лучше использовать for, а не foreach и шаговые для элементов коллекции в представлении, так как:

@for (int i = 0, i < Model.Estimates.Count; i++) 
{ 
    Html.EditorFor(m => m.Estimates[i].WorkEstimate); 
} 

В качестве грубого примера.

+0

Я только что обновил вопрос - я буду помнить ваш совет, но прямо сейчас у меня возникли проблемы с возвратом данных формы AJAX к моему контроллеру. – jwiscarson

+0

@jwiscarson Я также заметил, что вы не устанавливаете 'type' на вызов AJAX. По умолчанию для 'type' используется GET. Я думаю, вы хотите сделать POST там, верно? Может быть, это наносит неправильный контроль над контроллером (GET вместо POST). – bcr

+0

Нет, у меня есть частичное действие в моем контроллере, которое я пропустил, когда я вставлял в свой код, который возвращает «новый WorkEstimate()» в частичный вид, а частичное представление затем добавляется к таблице. – jwiscarson

Смежные вопросы