Reputation: 197
My Asp.Net 6 - Razor Pages application has an editable table within a form.
I am trying to bind the editable table fields to a model but am not sure how to do so because td
fields cannot be used with asp-for
tag helpers.
I want to be able to retrieve the edited values from the table in the Page Model's OnPost()
method.
These are the ids of the two editable table columns that I want to retrieve the values from:
For id="importColumn"
, when a user clicks on a cell, the cell value for this column gets changed from a tick to a cross using a Javascript on-click handler.
For id="modifiedColumnNames"
, the user modifies the value of any cell within this column.
.cshtml Form:
<form method="post" asp-antiforgery="true">
<table id="columnsToImportTable" class="display roundedCorners" width="100%">
<thead>
<tr>
<th class="centreText">Column Name</th>
<th class="centreText">Import Column</th>
<th class="centreText">Database Column Name</th>
</tr>
</thead>
<tbody>
@for (var i = 0; i < Model.newLayer.ColumnNames.Count(); i++)
{
<tr>
<td class="greyOutBackground centreText defaultCursor">@Model.newLayer.ColumnNames[i]</td>
<td id="importColumn" class="greyOutBackground centreText pointerCursor green tick true">✔
<input type="hidden" asp-for="@Model.newLayer.ImportColumn[i]" value="true">
</td>
<td id="modifiedColumnNames">
<div contenteditable>@Model.newLayer.ColumnNames[i]</div>
<input type="hidden" asp-for="@Model.newLayer.ColumnNames[i]">
</td>
</tr>
}
</tbody>
</table>
<div>
<input id="importButton" type="submit" value="Import">
</div>
</form>
Javascript/Jquery click handler:
$('#columnsToImportTable td.tick').click(function(e) {
e.stopPropagation();
e.preventDefault();
var $this = $(this);
if ($this.hasClass('true')) {
$this.html('<td id="importColumn" class="greyOutBackground centreText pointerCursor red tick false centreElement">✖<input type="hidden" asp-for="@Model.newLayer.ImportColumn[i]" value="false"></td>');
} else {
$this.html('<td id="importColumn" class="greyOutBackground centreText pointerCursor green tick true centreElement">✔<input type="hidden" asp-for="@Model.newLayer.ImportColumn[i]" value="true"></td>');
}
$this.toggleClass('true');
});
The Model:
public class NewLayer
{
public List<string> ColumnNames { get; set; } = new List<string>();
public List<string> ColumnDataTypes { get; set; } = new List<string>();
public List<string> LayerNames { get; set; } = new List<string>();
public List<string> ImportColumn { get; set; } = new List<string>();
public string SelectedLayer { get; set; } = null!;
public string SelectedNameField { get; set; } = null!;
}
The Page Model:
public class IndexModel: PageModel {
private FileProcessor _FileProcessor = new FileProcessor();
private AppContext _context;
[BindProperty(SupportsGet = true)]
public NewLayer newLayer {get;set;}
public IndexModel(AppContext context) {
_context = context;
newLayer = new NewLayer();
}
public void OnGet() {
var filePath = "[FilePath]";
var ColumnNamesAndDatatypes = _FileProcessor.GetColumnNamesAndTypes(filePath);
for (var i = 0; i < ColumnNamesAndDatatypes.Count(); i++) {
newLayer.ColumnNames.Add(ColumnNamesAndDatatypes[i].ColumnName);
newLayer.ColumnDataTypes.Add(ColumnNamesAndDatatypes[i].DataType);
newLayer.ImportColumn.Add("true");
}
newLayer.LayerNames = _context.Layers.Select(l => l.LayerName).ToList();
}
public void OnPost(NewLayer newLayer) {
var test = this.newLayer;
}
}
Upvotes: 2
Views: 3818
Reputation: 21353
Since you are using the Model Binding method to submit the form and transfer the data from the client to the back-end, we should set the hidden field's name property based on the view model and the index.
To the importColumn
column, when user click on the cell, you could use clone()
method to get the importColumn
hidden field, then change its value attribute, and finally add it to the new td
element.
To the modifiedColumnNames
column, when user modifies the value, you need to copy the contenteditable div content to the hidden field. you can use the div's input
event.
You can refer to the following code:
Html elements: add class for the modifiedColumnNames hidden field.
<form method="post" asp-antiforgery="true">
<table id="columnsToImportTable" class="display roundedCorners" width="100%">
<thead>
<tr>
<th class="centreText">Column Name</th>
<th class="centreText">Import Column</th>
<th class="centreText">Database Column Name</th>
</tr>
</thead>
<tbody>
@for (var i = 0; i < Model.newLayer.ColumnNames.Count(); i++)
{
<tr>
<td class="greyOutBackground centreText defaultCursor">@Model.newLayer.ColumnNames[i]</td>
<td id="importColumn" class="greyOutBackground centreText pointerCursor green tick true">✔
<input type="hidden" asp-for="@Model.newLayer.ImportColumn[i]" value="true">
</td>
<td id="modifiedColumnNames">
<div class="editdiv" contenteditable>@Model.newLayer.ColumnNames[i]</div>
<input type="hidden" asp-for="@Model.newLayer.ColumnNames[i]">
</td>
</tr>
}
</tbody>
</table>
<div>
<input id="importButton" type="submit" value="Import">
</div>
</form>
JS code:
<script>
$(function(){
$('#columnsToImportTable td.tick').click(function(e) {
e.stopPropagation();
e.preventDefault();
var $this = $(this);
// get the ImportColumn hidden field.
var hiddentag = $(this).find("input[type='hidden']").clone();
if ($this.hasClass('true')) {
//change the hidend field' value
$(hiddentag).attr("value","false");
//get the html
var newtag = $(hiddentag)[0].outerHTML;
$this.html('<td id="importColumn" class="greyOutBackground centreText pointerCursor red tick false centreElement">✖' + newtag + '</td>');
} else {
$(hiddentag).attr("value","true");
var newtag = $(hiddentag)[0].outerHTML;
$this.html('<td id="importColumn" class="greyOutBackground centreText pointerCursor green tick true centreElement">✔' + newtag + '</td>');
}
$this.toggleClass('true');
});
//
$(".editdiv").each(function(index, item){
$(item).on('propertychange input', function (e) {
$(this).next("input[type='hidden']").val($(this).text());
});
});
});
</script>
Index.cshtml.cs:
public class IndexModel : PageModel
{
//private FileProcessor _FileProcessor = new FileProcessor();
//private AppContext _context;
[BindProperty(SupportsGet = true)]
public NewLayer newLayer { get; set; }
public IndexModel()
{
newLayer = new NewLayer();
}
public void OnGet()
{
for (var i = 0; i < 3; i++)
{
newLayer.ColumnNames.Add("Name " + i.ToString());
newLayer.ColumnDataTypes.Add("DataType " + i.ToString());
newLayer.ImportColumn.Add("true");
}
newLayer.LayerNames = new List<string>() { "L1", "L2" };
}
public void OnPost(NewLayer newLayer)
{
var test = this.newLayer;
}
}
The result is like this:
Upvotes: 3