how do i pass values from foreach so as to create a new list of model? - asp.net-mvc

#model IEnumerable<OnlineExam.Models.CandidateExam.CandidateExam>
#{
ViewData["Title"] = "Index";
var questionList = JsonConvert.DeserializeObject<List<OnlineExam.Models.AdminQuestionModel.QuestionAndAnswers>>(TempData["questionList"].ToString());
var data = questionList as IEnumerable<OnlineExam.Models.AdminQuestionModel.QuestionAndAnswers>;
TempData.Keep();
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
#{
int count = 0;
}
#foreach (var item2 in Model)
{
#foreach (var item in data)
{
count = count + 1;
<table class="table" style="background-color:lavender">
<tbody>
<tr class="border-light"><td>Question #count : #item.Question</td></tr>
<tr class="bg-light"><td>Options : </td></tr>
<tr class="border-light"><td><input asp-for="#item2.OptionSelected" type="radio" name="counter" value="#item.OptionTwo" /> a) #item.OptionOne</td></tr>
<tr class="bg-light"><td><input asp-for="#item2.OptionSelected" type="radio" name="counter" value="#item.OptionTwo" /> b) #item.OptionTwo</td></tr>
<tr class="border-light"><td><input asp-for="#item2.OptionSelected" type="radio" name="counter" value="#item.OptionThree" /> c) #item.OptionThree</td></tr>
<tr class="bg-light"><td><input asp-for="#item2.OptionSelected" type="radio" name="counter" value="#item.OptionFour" /> d) #item.OptionFour</td></tr>
</tbody>
</table>
<br />
}
}
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
.................................................................................................
Basically i want to load data from item to item 2 ie: I want to supply values from one foreach to model(IEnumerable). How do i do tht?

You cannot bind the foreach item with the posted back items because it messes up the naming convention MVC relies on to post the data back. You will have to use For loop for this purpose.
#for(int i=0; i< Model.Count(); i++)
{
for (int j=0; i< data.Count(); j++)
{
// here you can do like this to bind your element
#Html.RadioButtonFor(x=> data[j].propertyName)
}
}

Here is a working demo:
1.Model:
public class CandidateExam
{
public int Id { get; set; }
public string OptionSelected { get; set; }
}
public class QuestionAndAnswers
{
public int Id { get; set; }
public string Question { get; set; }
public string OptionOne { get; set; }
public string OptionTwo { get; set; }
public string OptionThree { get; set; }
public string OptionFour { get; set; }
}
2.View(Note that you set the optionOne with wrong value #item.OptionTwo):
#model IEnumerable<QuestionAndAnswers>
#{
int count = 0;
int i = 0;
}
<form method="post">
#foreach (var item in Model)
{
count = count + 1;
<table class="table" style="background-color:lavender">
<tbody>
<tr class="border-light"><td>Question #count : #item.Question</td></tr>
<tr class="bg-light"><td>Options : </td></tr>
<tr class="border-light"><td><input asp-for="#item.OptionOne" type="radio" name="counter[#i]" value="#item.OptionOne" /> a) #item.OptionOne</td></tr>
<tr class="bg-light"><td><input asp-for="#item.OptionTwo" type="radio" name="counter[#i]" value="#item.OptionTwo" /> b) #item.OptionTwo</td></tr>
<tr class="border-light"><td><input asp-for="#item.OptionThree" type="radio" name="counter[#i]" value="#item.OptionThree" /> c) #item.OptionThree</td></tr>
<tr class="bg-light"><td><input asp-for="#item.OptionFour" type="radio" name="counter[#i]" value="#item.OptionFour" /> d) #item.OptionFour</td></tr>
</tbody>
</table>
i++;
<br />
}
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</form>
3.Controller:
public class QuestionAndAnswersController : Controller
{
private readonly YourContext _context;
public QuestionAndAnswersController(YourContext context)
{
_context = context;
}
// GET: QuestionAndAnswers
public async Task<IActionResult> Index()
{
return View(await _context.QuestionAndAnswers.ToListAsync());
}
[HttpPost]
public async Task<IActionResult> Index(string[] counter)
{
foreach(var item in counter)
{
var data = new CandidateExam()
{
OptionSelected = item
};
_context.Add(data);
await _context.SaveChangesAsync();
}
//do your stuff...
}
4.Result:

#model OnlineExam.Models.CandidateExam.CandidateItem
#{
ViewData["Title"] = "Index";
var questionList = JsonConvert.DeserializeObject<List<OnlineExam.Models.AdminQuestionModel.QuestionAndAnswers>>(TempData["questionList"].ToString());
//var data = questionList as IEnumerable<OnlineExam.Models.AdminQuestionModel.QuestionAndAnswers>;
var data2 = questionList as List<OnlineExam.Models.AdminQuestionModel.QuestionAndAnswers>; //new stackov
int examAttemptId = Convert.ToInt32(TempData["examAttemptId"].ToString());
TempData.Keep();
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<div>
<form enctype="multipart/form-data" asp-action="AnswerExam">
#{
int counter = 0;
}
#for (var i = 0; i < data2.Count(); i++)
{
counter = counter + 1;
<input type="hidden" asp-for="candidateExamsList[i].ExamId" value="#data2[i].ExamId" />
<input type="hidden" asp-for="candidateExamsList[i].QuestionId" value="#data2[i].QuestionId" />
<input type="hidden" asp-for="candidateExamsList[i].ExamAttemptId" value="#examAttemptId" />
<span>Question #counter) </span>#data2[i].Question.ToString()<br />
<span>Options : </span><br />
<span>a) </span>#Html.RadioButtonFor(x => x.candidateExamsList[i].OptionSelected, data2[i].OptionOne) #data2[i].OptionOne;<br />
<span>b) </span>#Html.RadioButtonFor(x => x.candidateExamsList[i].OptionSelected, data2[i].OptionTwo) #data2[i].OptionTwo;<br />
<span>c) </span>#Html.RadioButtonFor(x => x.candidateExamsList[i].OptionSelected, data2[i].OptionThree) #data2[i].OptionThree;<br />
<span>d) </span>#Html.RadioButtonFor(x => x.candidateExamsList[i].OptionSelected, data2[i].OptionFour) #data2[i].OptionFour;<br /><br />
}
<div class="form-group">
<button type="submit" value="Submit" class="btn btn btn-primary" />
</div>
</form>
</div>
This is how i cud fix my issue..i used a for loop instead of foreach loop to bind data from the list to the model.

Related

MVC ModelState.IsValid not working

I am using below code to validate my model, but when I click on the 'Save Employee' button on the CreateEmployee view, the page is returning to CreateEmployee view without a validation messages.
Model
public class Employee
{
[Key]
public int EmployeeId { get; set; }
[Required(ErrorMessage="Enter First Name")]
public string FirstName { get; set; }
[Required(ErrorMessage="Enter Last Name")]
[StringLength(6,ErrorMessage="Last Name should not contain more than 6 characters")]
public string LastName { get; set; }
public int Salary { get; set; }
}
View
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
#*<meta name="viewport" content="width=device-width" />*#
<title>CreateEmployee</title>
<script type="text/javascript">
function ResetForm() {
document.getElementById("txtFirstName").value = "";
document.getElementById("txtLastName").value = "";
document.getElementById("txtSalary").value = "";
}
</script>
</head>
<body>
<div>
<form action="SaveEmployee" method="post">
#Html.ValidationSummary()
<table>
<tr>
<td>First Name:</td>
<td>
<input type="text" id="txtFirstName" name="FirstName" value="" /></td>
</tr>
<tr>
<td colspan="2" align="right">#Html.ValidationMessage("FirstName")</td>
</tr>
<tr>
<td>Last Name:</td>
<td>
<input type="text" id="txtLastName" name="LastName" value="" /></td>
</tr>
<tr>
<td colspan="2" align="right">#Html.ValidationMessage("LastName")
</td>
</tr>
<tr>
<td>
<input type="submit" value="SaveEmployee" />
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
Controller
public ActionResult SaveEmployee(Employee e,string BtnSubmit)
{
switch (BtnSubmit)
{
case "Save Employee":
if (ModelState.IsValid)
{
EmployeeBuisnessLayer objEBL = new EmployeeBuisnessLayer();
objEBL.SaveEmployee(e);
return RedirectToAction("Index");
}
else
{
return RedirectToAction("CreateEmployee");
}
case "Cancel":
return RedirectToAction("Index");
}
return new EmptyResult();
}
Don't redirect. You will lose modelstate. You need to let the method run to completion and pass the model back to the view.
Controller
[HttpPost]
public ActionResult CreateEmployee(Employee e, string BtnSubmit) {
switch (BtnSubmit) {
case "SaveEmployee":
if (ModelState.IsValid) {
EmployeeBuisnessLayer objEBL = new EmployeeBuisnessLayer();
objEBL.SaveEmployee(e);
return RedirectToAction("Index");
}
case "Cancel":
return RedirectToAction("Index");
default:
return new EmptyResult();
}
return View(e);
}
Your Form also needs to post to the CreateEmployee
View
<form action="CreateEmployee" method="post">

fiil a list with values of a table

I'm new in learning asp.net MVC. I am writing because I am stubborn to a problem. Indeed, I have an application that should allow me to create an XML file that will be added to a database. At this point, I created my Model, and my view that allows me to create my XML tags.
I saw on this site that could add lines in my table via Javascript. What I have done just as you can see in the code.
I can not recover what is the value of each line that I can insert. Passing my view a list I created myself. I can recover both inputs I inserted in my controller.
My question is, there's another way to create a dynamic lines via javascript, then all the entries that the user has entered the recover and fill in my list? Then I know myself how I can play with my list. But I just want to recover all the different lines that my user has inserted. I am new in ASP.NET MVC. Any help , please
This is my code.
Model
public class XMLFile
{
public string TypeDoc { get; set; }
public string Type { get; set; }
public string Contenu { get; set; }
public string DocName { get; set; }
}
This is my controller :
public class XMLFileController : Controller
{
List<XMLFile> file = new List<XMLFile>();
[HttpGet]
public ActionResult Save()
{
file.AddRange( new XMLFile[] {
new XMLFile (){Type = "Titre", Contenu = "Chef de Service"},
new XMLFile (){Type = "Item", Contenu="Docteur Joel"}
});
return View(file);
}
[HttpPost]
public ActionResult Save(List<XMLFile> formCollection)
{
try
{
if (formCollection == null)
{
return Content("la liste est nulle");
}
else
{
return RedirectToAction("Create", "Layout");
}
}
catch
{
return View();
}
}
}
My view with a script for adding a new Row :
#using (Html.BeginForm("Save", "XMLFile", FormMethod.Post,new { #class = "form-horizontal", #role = "form", #id = "FormCreateXML" }))
{
<table class="table table-bordered" id="XMLFileTable">
<thead>
<tr>
<th>Type</th>
<th>Contenu</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i<Model.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(model=>model[i].Type, new {#class="form-control help-inline", #placeholder="type" })</td>
<td> #Html.TextBoxFor(model=>model[i].Contenu, new {#class="form-control help-inline", #placeholder="contenu" })</td>
<td> <input type="button" class="BtnPlus" value="+" /> </td>
<td> <input type="button" class="BtnMinus" value="-" /> </td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td> <button type="submit" class="btn btn-success" >Save</button> </td>
</tr>
</tfoot>
</table>
}
</body>
<script type="text/javascript">
$(document).ready(function () {
function addRow() {
var html = '<tr>' +
'<td><input type="text" class="form-control" placeholder="type"></td>' +
'<td> <input type="text" class="form-control" placeholder="contenu"></td>' +
'<td> <input type="button" class="BtnPlus" value="+" /> </td>' +
'<td> <input type="button" class="BtnMinus" value="-" /></td>' +
'</tr>'
$(html).appendTo($("#XMLFileTable"))
};
function deleteRow() {
var par = $(this).parent().parent();
par.remove();
};
$("#XMLFileTable").on("click", ".BtnPlus", addRow);
$("#XMLFileTable").on("click", ".BtnMinus", deleteRow);
});
</script>

Mvc HttpPostedFileBase returns null

lately i was trying to handle HttpPostedFileBase returns null issue, but i cannot really figure it out why my input file returns null although i've researched all the similiar questions to get to know about the problem in the site.My sutiation is i need to have four input file because i need four different documents other than each other.
here is part of the view:
#using (Html.BeginForm("Create", "MyController", FormMethod.Post, new { enctype ="multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div style="padding-left: 32px;">
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id)
<table>
<tbody>
<tr>
<td style="width: 132px;"><div><b>...</b></div></td>
<td style="width:250px;">
<input type="file" name="upload1" id="upload1" class="formin" />
<input type="hidden" name="UploadType1" id="UploadType1" value="#Request.QueryString["UploadType"]" />
</td>
</tr>
<tr>
<td style="width: 132px;"><div><b>...</b></div></td>
<td style="width:250px;">
<input type="file" name="upload2" id="upload2" class="formin" />
<input type="hidden" name="UploadType2" id="UploadType2" value="#Request.QueryString["UploadType"]" />
</td>
</tr>
<tr>
<td style="width: 132px;"><div><b>...</b></div></td>
<td style="width:250px;">
<input type="file" name="upload3" id="upload3" class="formin" />
<input type="hidden" name="UploadType3" id="UploadType3" value="#Request.QueryString["UploadType"]" />
</td>
</tr>
<tr>
<td style="width: 132px;"><div><b>...</b></div></td>
<td style="width:250px;">
<input type="file" name="upload4" id="upload4" class="formin" />
<input type="hidden" name="UploadType4" id="UploadType4" value="#Request.QueryString["UploadType"]" />
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td style="width:220px;"><div><b></b></div></td>
<td> <input type="submit" name="submit" value="GÖNDER" class="formbutton" style="font-size: 14px;background:maroon;color:white;"></td>
</tr>
</tbody>
</table>
here is my controller:
[HttpPost]
public ActionResult Create(HttpPostedFileBase upload1, HttpPostedFileBase upload2, HttpPostedFileBase upload3, HttpPostedFileBase upload4, string UploadType1, string UploadType2, string UploadType3, string UploadType4)
{
List<Ek> eks = new List<Ek>();
if (upload1!=null && upload1.ContentLength > 0)
{
var upload1Name= Path.GetFileName(upload1Name.FileName);
var path1 = Path.Combine(Server.MapPath("~/Documents/.../upload1"), PhotoName);
upload1.SaveAs(path1);
Ek e1 = new Ek {
UploadType = GetTypeEnum(UploadType1),
UploadPath = "/Documents/.../.../" + upload1Name
};
eks.Add(e1);
}
if (upload2!= null && upload2.ContentLength > 0)
{
var upload2Name = Path.GetFileName(upload2.FileName);
var path2 = Path.Combine(Server.MapPath("~/Documents/.../..."), upload2Name);
upload2.SaveAs(path2);
Ek e2 = new Ek
{
UploadType = GetTypeEnum(UploadType1),
UploadPath = "/Documents/.../.../" + upload2Name
};
eks.Add(e2);
}
if (upload3!= null && upload3.ContentLength > 0)
{
var upload3Name = Path.GetFileName(upload3.FileName);
var path3 = Path.Combine(Server.MapPath("~/Documents/.../..."), upload3Name );
upload3.SaveAs(path3);
Ek e3 = new Ek
{
UploadType = GetTypeEnum(UploadType1),
UploadPath = "/Documents/.../" + upload3Name
};
eks.Add(e3);
}
if (upload4!= null && upload4.ContentLength > 0)
{
var upload4Name = Path.GetFileName(upload4.FileName);
var path4 = Path.Combine(Server.MapPath("~/Documents/.../"), upload4Name);
languagepoint.SaveAs(path4);
Ek e4 = new Ek
{
UploadType = GetTypeEnum(UploadType1),
UploadPath = "/Documents/.../" + upload4Name
};
eks.Add(e4);
}
//some stuff here
return RedirectToAction("Success");
}
return View(myview);
}
private UploadType GetTypeEnum(string UploadType1)
{
Models.UploadType uType= UploadType.MyType;
switch (UploadType1)
{
case "MyType":
uType = UploadType.MyType;
break;
case "MyType1":
uType = UploadType.MyType1;
break;
case "MyType2":
uType = UploadType.MyType2;
break;
case "MyType3":
uType = UploadType.MyType3;
break;
default:
break;
}
return uType;
}
and finally here is my modal:
public partial class MyMainClass
{
public int Id { get; set; }
public virtual IList<Ek> Ek { get; set; }
}
public partial class Ek
{
public int ID { get; set; }
public UploadType UploadType { get; set; }
public string UploadPath { get; set; }
}
public enum UploadType
{
MyType= 0,
MyType1= 1,
MyType2= 2,
MyType3= 3
}
Thank you for all the answers.
Just Correct Your HttpPost Controller as :
[HttpPost]
public ActionResult Create(IEnumerable<HttpPostedFileBase> files)
{
if (files.Count() > 0) // display no of files uploaded
if (files.Any()) // display true
if (files.First() == null) // display "first null"
return View();
}
Because you are uploading more than one file from view so you have to use IEnumerable of HttpPostedFileBase and write HttpPost attribute on Create action because it is accepting your POST Request.

error to get value from controller

i try to create new room, but roomTypeID always return 1, whats wrong with my code?
i can make a new room type, but i cant insert room facility in my database, because RoomType ID always return 1
this my code..
my controller
public ActionResult NewRoom()
{
ViewBag.hotel = _hotelService.GetByID(_HotelID).HotelName;
List<ShowEditRoomViewModel> showEditRoomViewModel = _roomTypeService.showNewRooms();
return View(showEditRoomViewModel.FirstOrDefault());
}
[HttpPost]
public ActionResult NewRoom(FormCollection typeRoom)
{
_roomTypeService.NewRoom(_HotelID, typeRoom["RoomTypeName"], typeRoom["RoomTypeDescription"]);
List<string> IDs = typeRoom["FacilityIDs"].Split(',').ToList();
List<int> FacilityIDs = new List<int>();
foreach (string ID in IDs)
{
FacilityIDs.Add(Convert.ToInt32(ID));
}
_roomTypeService.UpdateFacilityInRooms(FacilityIDs, Convert.ToInt32(typeRoom["RoomTypeID"]));
return NewRoom();
}
my service
public void UpdateFacilityInRooms(List<int> FacilityIDs, int RoomTypeID)
{
List<HotelRoomFacility> hotelRoomFacilities = _HotelRoomFacilityRopository.AsQueryable().Where(f => f.RoomTypeID == RoomTypeID).ToList();
foreach (int newRoomFacility in FacilityIDs)
{
if (hotelRoomFacilities.Where(h => h.RoomFacilityID == newRoomFacility).Count() == 0)
{
HotelRoomFacility facility = new HotelRoomFacility
{
RoomFacilityID = newRoomFacility,
RoomTypeID = RoomTypeID
};
_HotelRoomFacilityRopository.Add(facility);
}
}
_HotelRoomFacilityRopository.CommitChanges();
}
my view model
public class ShowEditRoomViewModel
{
public int RoomTypeID { get; set; }
public string RoomTypeName { get; set; }
public string RoomTypeDescription { get; set; }
public List<FaciliyInRoom> facilityinRoom { get; set; }
}
my view
#model XNet.Repository.Model.ShowEditRoomViewModel
#{
ViewBag.Title = "NewRoom";
}
<h2>New Room</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Isikan Data</legend>
<div>
#Html.Label("Hotel Name")
</div>
<div>
#ViewBag.hotel
</div>
<br />
<div>
#Html.HiddenFor(model => model.RoomTypeID)
</div>
<br />
<div>
#Html.Label("Room Type Name")
</div>
<div>
#Html.EditorFor(model => model.RoomTypeName)
#Html.ValidationMessageFor(model => model.RoomTypeName)
</div>
<br />
<div>
#Html.Label("Room Type Description")
</div>
<div>
#Html.TextAreaFor(model => model.RoomTypeDescription)
#Html.ValidationMessageFor(model => model.RoomTypeDescription)
</div>
<br />
<table>
<thead>
<tr>
<th>Facility Name</th>
<th> is available</th>
</tr>
</thead>
<tbody>
#foreach (var facility in Model.facilitiesInRoom)
{
<tr>
<td>
#(facility.RoomFacilityName)
</td>
<td style="text-align:center;">
<input type="checkbox" #(facility.RoomFacilityAvailable ? " checked=checked" : null) name="FacilityIDs" value="#facility.RoomFacilityID" />
</td>
</tr>
}
</tbody>
</table>
<br />
<p>
<input type="submit" value="Save" />
<input style="width:100px;" type="button" title="EditHotelDetail" value="Back to Detail" onclick="location.href='#Url.Action("Room", "Hotel") '" />
</p>
</fieldset>
}
My method
public List<ShowEditRoomViewModel> showNewRooms()
{
List<RoomType> roomTypes = (from d in _RoomTypeRepository.All()
select d).ToList();
List<ShowEditRoomViewModel> showEditRoomViewModel = new List<ShowEditRoomViewModel>();
foreach (RoomType roomType in roomTypes)
{
showEditRoomViewModel.Add(new ShowEditRoomViewModel
{
RoomTypeID = roomType.RoomTypeID,
facilitiesInRoom = LoadFacilityInRoom()
});
}
return showEditRoomViewModel;
}
can someone tell me, where is my mistake??
thanks
When you are inserting RoomtypeId in Database, you are using ExecuteNonQuery() method, It will always return 1 whenever you insert a new record in it,
If you are using stored procedure for inserting,you can use
select Scope_identity()
after insertion.

How to get data from partial in form

I have
ASP.NET MVC Form in popup with some controls and partial (data grid) with his own Model.
here is popup:
<div id="AddEditDialog" class="none">
#using (Ajax.BeginForm("Save", "Templates", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "AddEditPlaceHolder",
OnSuccess = "OnSaveSuccess",
HttpMethod = "Post"
}))
{
<div>
<div id="AddEditPlaceHolder"></div>
<div id="PopupButtons" class="btn-holder-centered">
<input type="submit" value="Save" name="SaveButton" />
<input type="button" value="Cancel" name="SaveCancelButton" id="CancelEditHandler" />
</div>
</div>
}
</div>
here is form which I render in AddEditPlaceHolder via js:
#model TemplatesViewModel
<div class="form-field-plain overflow">
<div class="forRow narRow float-left">
#Html.LabelFor(x => x.Revocable)
#Html.CheckBoxFor(x => x.Revocable)
</div>
</div>
<div class="form-field-plain overflow">
<div class="forRow narRow float-left">
#Html.LabelFor(x => x.HtmlTemplate)
#Html.TextAreaFor(x => x.HtmlTemplate)
</div>
</div>
#Html.Partial("_VariablesGridView", Model.Variables)
_VariablesGridView.cshtml:
#model List<TemplateVariableViewModel>
<table id="TemplateVariablesGrid">
<thead>
<tr>
<td>Tag/Code</td>
<td>Prompt</td>
<td>Action</td>
</tr>
</thead>
<tbody>
#foreach (var i in Model)
{
<tr>
<td>
#Html.TextBox("txtTag", #i.Tag, new {})
</td>
<td>
#Html.TextBox("txtPrompt", #i.Prompt, new { })
</td>
<td>
#Html.HiddenFor(x => x.First(s => s.Id == #i.Id).Id)
<label class="delete-variable">delete</label>
</td>
</tr>
}
</tbody>
</table>
<br />
<input type="button" name="btnAddTemplateVariable" value="add new variable"/>
<br />
My problem is :
in Controller 'save form' method public ActionResult Save(TemplateViewModel model)
my model contains all data from form but TemplateViewModel.Variables is empty
Is there any way to fill it in there?
Models:
public class TemplateViewModel
{
public int Id { get; set; }
public string HtmlTemplate { get; set; }
public List<TemplateVariableViewModel> Variables { get; set; }
}
public class TemplateVariableViewModel
{
public int Id { get; set; }
public string Tag { get; set; }
public string Prompt { get; set; }
}
I believe it is because the ASP.Net MVC binding is not putting these fields in context, have a look at your field names delivered to the browser, what is txtTag prefixed by when it gets to the browser and what is is after you do the following:
#Html.Partial("_VariablesGridView", Model)
_VariablesGridView.cshtml:
#model TemplatesViewModel
...
#for (int i = 0; i < Model.Variables.Count; i++)
#Html.TextBox("txtTag", #Model.Variables[i].Tag, new {})
Forgive me if this fails miserably (again), I'm shooting from the hip.

Resources