2013-08-18 2 views
26

Я не могу понять, как отправить весь ViewModel весь контроллер на функцию «Проверить и сохранить».MVC 4 ViewModel не отправляется обратно контроллеру

Вот мой контроллер:

[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel transaction) 
{ 
} 

Вот форма в виде:

<li class="check"> 
    <h3>Transaction Id</h3> 
    <p>@Html.DisplayFor(m => m.Transaction.TransactionId)</p> 
</li> 
<li class="money"> 
    <h3>Deposited Amount</h3> 
    <p>@Model.Transaction.Amount.ToString() BTC</p> 
</li> 
<li class="time"> 
    <h3>Time</h3> 
    <p>@Model.Transaction.Time.ToString()</p> 
</li> 


@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { transaction = Model })) 
{ 

@Html.HiddenFor(m => m.Token); 
@Html.HiddenFor(m => m.Transaction.TransactionId); 

    @Html.TextBoxFor(m => m.WalletAddress, new { placeholder = "Wallet Address", maxlength = "34" }) 
    <input type="submit" value="Send" />  

    @Html.ValidationMessage("walletAddress", new { @class = "validation" }) 
} 

Когда я нажимаю на представить, то Conroller содержит правильное значение поля walletAddress но transaction.Transaction.Time, transaction.Transaction.Location, transaction.Transaction.TransactionId пустые.

Есть ли способ, которым я мог бы передать всю модель обратно контроллеру?

Edit:

Когда я даже не получить walletAddress в контроллере. Все становится нулевым! Когда я удаляю только эту строку: @Html.HiddenFor(m => m.Transaction.TransactionId); это работает, и я получаю свойство Token на контроллере, но когда я его добавляю, все свойства объекта transaction на контроллере равны NULL.

Вот BitcoinTransactionViewModel:

public class BitcoinTransactionViewModel 
    { 
     public string Token { get; set; } 
     public string WalletAddress { get; set; } 
     public BitcoinTransaction Transaction { get; set; } 
    } 

public class BitcoinTransaction 
    { 
     public int Id { get; set; } 
     public BitcoinTransactionStatusTypes Status { get; set; } 
     public int TransactionId { get; set; } 
     public decimal Amount { get; set; } 
     public DateTime Time { get; set; } 
     public string Location { get; set; } 
    } 

Любые идеи?

EDIT: Я понял это, его в маркированной ответ ниже ...

ответ

32

ОК, я уже много работал над чем-то еще и с тем же бампедом. Только на этот раз я понял, как заставить его работать!

Вот ответ для тех, кто может быть заинтересован:

По-видимому, существует соглашение об именовании. Обратите внимание:

Это не работает:

// Controller 
[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel transaction) 
{ 
} 

// View 
@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { transaction = Model })) 
{ 

@Html.HiddenFor(m => m.Token); 
@Html.HiddenFor(m => m.Transaction.TransactionId); 
. 
. 

Это работает:

// Controller 
[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel **RedeemTransaction**) 
{ 
} 

// View 
@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { **RedeemTransaction** = Model })) 
{ 

@Html.HiddenFor(m => m.Token); 
@Html.HiddenFor(m => m.Transaction.TransactionId); 
. 
. 

Другими словами - ошибка именования! Существовала именованная неоднозначность между свойством Model.Transaction и моим полем transaction формы + контроллером. Unvelievable.

Если у вас возникли те же проблемы, убедитесь, что имя параметра вашего контроллера уникально - попробуйте переименовать его в MyTestParameter или что-то вроде этого ...

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

+1

Мне нравится этот ответ, и это имеет смысл, но, к сожалению, не работает для меня. Кажется, я застрял в тупике, где бы я ни старался, я не могу заставить под-объект вернуться в контроллер. Такое разочарование. –

+0

У меня была такая же проблема. У меня была ViewModel с именем FileViewModel, и мой параметр action был назван «file». Я изменил его на «_file», и это сработало для меня. –

+0

Life saver, спасибо. – Chris

11

Это не MVC специфичны. Форма HTML будет отправлять только значения, содержащиеся в элементах формы внутри формы. Ваш пример не входит ни в форму, ни в элемент формы (например, скрытые входы). Вы должны это сделать, поскольку MVC не полагается на состояние просмотра. Положите скрытые поля внутри форму:

@Html.HiddenFor(x => x.Transaction.Time) 
// etc... 

спросить себя, хотя .. если пользователь не обновляет эти значения .. это ваш метод действия требуют от них?

+2

Спасибо за ответ. Теперь у меня другая проблема: когда я создаю скрытое поле для свойства на первом уровне (x => x.Token), оно работает, но когда я делаю это для внутреннего объекта (X => x.Transaction.InnerField), это не работает! вдруг все свойства объекта транзакции в контроллере равны нулю! У вас есть идея, почему это происходит? –

+1

Он требует их, потому что, когда проверка не выполняется, я хочу отобразить ту же форму с ошибками проверки и не выбирать объект из базы данных (но использовать тот, который у меня уже есть) –

+0

Вам нужно показать нам обновленный код, чтобы ответить на новую ошибку .. –

9

Модель привязки гидратирует вашу модель взгляда в действии вашего контроллера через опубликованные значения формы. Я не вижу никаких элементов управления формы для ваших вышеупомянутых переменных, поэтому ничего не будет опубликовано назад. Вы видите, есть ли у вас такая радость?

@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post, new { transaction = Model })) 
{ 
    @Html.TextBoxFor(m => m.WalletAddress, new { placeholder = "Wallet Address", maxlength = "34" }) 
    @Html.Hidden("Time", Model.Transaction.Time) 
    @Html.Hidden("Location", Model.Transaction.Location) 
    @Html.Hidden("TransactionId", Model.Transaction.TransactionId) 
    <input type="submit" value="Send" />  

    @Html.ValidationMessage("walletAddress", new { @class = "validation" }) 
} 
+0

Когда я создаю скрытое поле для внутренней собственности, внезапно все свойства в принимающем контроллере становятся нулевыми! –

+0

Обновлено, можете ли вы дать ему еще один вихрь? –

+0

Это не значит, что Транзакция не является массивом. Это просто объект, пожалуйста, просмотрите обновленный вопрос. –

22

Подпись отправляющего метода, что форма проводки к имеет параметр с именем транзакции, который, кажется, путает модель связующего. Измените имя параметра, чтобы быть что-то не соответствует имени свойства на модели:

[HttpPost] 
public ActionResult Send(BitcoinTransactionViewModel model) 
{ 
} 

Кроме того, удалите параметр htmlAttributes из вашего BeginForm вызова, так что это ничего полезного не делает. Она становится:

@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post)) 

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

+1

Мой вопрос заключается в том, как опубликовать весь объект, а не только его уникальный идентификатор. Это потому, что я НЕ хочу запускать выделение против моей базы данных или управлять кешем данных на стороне сервера. –

+0

Чтобы опубликовать весь объект, вам нужно сделать то, что выбрали остальные, и создать скрытые входы для каждого объекта. Переименование параметра метода «Отправить» устранит проблему, когда ваша модель будет равна нулю. –

+0

Слово предупреждения это НЕ чувствительно к регистру, – Stonedecroze

0

Помещенный все поля внутри формы

@using (Html.BeginForm("Send", "DepositDetails", FormMethod.Post)) 

и убедитесь, что модель

BitcoinTransactionViewModel 

включены в виду или нет?

0

Можете ли вы просто объединить эти 2 модели у вас? Вот как я делаю это с одной моделью для каждого вида ... 1. Я использую Display Templates из представления для просмотра, чтобы я мог передать всю модель, а также оставить данные зашифрованными. 2. Настройте свой основной вид следующим образом .. .

@model IEnumerable<LecExamRes.Models.SelectionModel.GroupModel> 
<div id="container"> 
<div class="selectLabel">Select a Location:</div><br /> 
@foreach (var item in Model) 
{   
    @Html.DisplayFor(model=>item) 
} 
</div> 

3. Создайте папку DisplayTemplates в общем виде. Создайте представление, называя его, как ваша модель, которую вы хотите передать, потому что DisplayFor ищет шаблон отображения, названный в честь используемой вами модели, я вызываю свой GroupModel. Подумайте о шаблоне отображения в качестве экземпляра объекта вашего перечисления. Groupmodel Похоже, я просто назначаю группу кнопке.

@model LecExamRes.Models.SelectionModel.GroupModel 
@using LecExamRes.Helpers 
@using (Html.BeginForm("Index", "Home", null, FormMethod.Post)) 
{ 
<div class="mlink"> 
    @Html.AntiForgeryToken() 
    @Html.EncryptedHiddenFor(model => model.GroupKey) 
    @Html.EncryptedHiddenFor(model => model.GroupName) 
    <p> 
     <input type="submit" name="gbtn" class="groovybutton" value=" @Model.GroupKey   "> 
    </p> 
</div> 
}  

4. Вот контроллер. * GET & POST *

public ActionResult Index() 
    { 
     // Create a new Patron object upon user's first visit to the page. 
     _patron = new Patron((WindowsIdentity)User.Identity); 
     Session["patron"] = _patron;    
     var lstGroups = new List<SelectionModel.GroupModel>(); 
     var rMgr = new DataStoreManager.ResourceManager(); 
     // GetResourceGroups will return an empty list if no resource groups where found. 
     var resGroups = rMgr.GetResourceGroups(); 
     // Add the available resource groups to list. 
     foreach (var resource in resGroups) 
     { 
      var group = new SelectionModel.GroupModel(); 
      rMgr.GetResourcesByGroup(resource.Key); 
      group.GroupName = resource.Value; 
      group.GroupKey = resource.Key; 
      lstGroups.Add(group); 
     } 
     return View(lstGroups); 
    } 

    [ValidateAntiForgeryToken] 
    [HttpPost] 
    public ActionResult Index(SelectionModel.GroupModel item) 
    { 
     if (!ModelState.IsValid) 
      return View(); 

     if (item.GroupKey != null && item.GroupName != null) 
     {    
      var rModel = new SelectionModel.ReserveModel 
      { 
       LocationKey = item.GroupKey, 
       Location = item.GroupName 
      }; 

      Session["rModel"] = rModel; 
     }   
//So now my date model will have Group info in session ready to use 
     return RedirectToAction("Date", "Home"); 
    } 

5. Теперь, если у меня есть много соображений, различные модели, я обычно использую модель, относящуюся к представлению, а затем сеанс OBJ, которая захватывает данные из каждая модель, поэтому, в конце концов, у меня есть данные для отправки.

1

Приобрести форму и приобретите значение как. Я думаю, что это может сработать.

public ActionResult Send(FormCollection frm) 
{ 
    var time = frm['Transaction.Time']; 
} 
3

Попробуйте петли с на следующие заявления не с FOREACH

<table> 
    @for (var i = 0; i < Model.itemlist.Count; i++) 
    { 
     <tr> 
      <td> 
       @Html.HiddenFor(x => x.itemlist[i].Id) 
       @Html.HiddenFor(x => x.itemlist[i].Name) 
       @Html.DisplayFor(x => x.itemlist[i].Name) 
      </td> 
     </tr> 
    } 
</table> 
0

Имя действия, к которым данные будут опубликованы, должны быть такими же, как имя действия, из которого данные будут опубликованы. Единственное различие должно состоять в том, что второе действие, в котором должны быть отправлены данные, должно быть [HttpPost], а метод Posting должен обслуживать только запросы Get.