Austin Austin - 2 months ago 9
HTML Question

On Click - Make @Html.DisplayFor an editable text field

So I am playing with a practice site I made and I want to attempt a feature that will allow me to select any data value in the table, edit it, then save it to the Database.

Right now I have an "Edit" button that runs an auto-generated Edit method in the controller and that has its own View for editing and saving, but I want to attempt this edit function within the Index View (or at least pass the data to the edit function within this Index view)

Here is how the page looks right now
enter image description here

I also have a row-expand script that display some additional information that I also want to be editable. (all in same view)

enter image description here

View Code for Index.cshtml

@model IEnumerable<WebApplication2.Entities.Movie>

@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style type="text/css">
table tr button {
opacity: 0.5;
float: right;
}

table tr:hover button {
opacity: 1;
}
</style>

<br />
<br />
<br />
<br />
<div class="panel panel-primary" style="width:100%">
<div class="panel-heading">
<span style="font-size: 30px; font-style:oblique"><span style="font-size:larger;"><span style="margin-right: 5px" class="glyphicon glyphicon-film"></span>Movies</span></span>
</div>

<div class="col-lg-offset-8 col-lg-4">
<button type="button" style="margin:3px" class="btn btn-success btn-block" onclick="location.href='@Url.Action("Create")';return false;"><span style="font-size:larger"><span style="margin-right: 5px" class="glyphicon glyphicon-plus"></span>Add New Movie</span></button>

</div>

<table class="table table-striped table-hover table-responsive table-condensed">
<tr>
<th>
<h4 style="font-size:x-large"><span style="font-weight:bolder">Title</span></h4>
</th>
<th>
<h4 style="font-size:x-large"><span style="font-weight:bolder">Release Date</span></h4>
</th>
<th>
<h4 style="font-size:x-large"><span style="font-weight:bolder">@Html.DisplayNameFor(model => model.Description)</span></h4>
</th>
<th>
@using (Html.BeginForm("Index", "Movie"))
{
<div class="dropdown">
<select class="btn btn-group-lg btn-default col-lg-4" style="margin-top: 15px; height: 36px; opacity: 1" data-toggle="dropdown" name="Filter">
<option value="0" disabled selected>Filter By...</option>
<option value="1">Movie Name</option>
<option value="2">Description</option>
</select>
</div>

<input type="text" name="searchString" class="col-lg-6" style="margin-top: 16px; text-align:center; height:35px; font-size:20px" placeholder="enter text" />
<button type="submit" class="btn btn-group-lg btn-primary col-lg-2 glyphicon glyphicon-arrow-right" style="margin-top: 15px; height:36px; opacity:1" value="" />
}
</th>
</tr>

@foreach (var item in Model)
{
<tr>
<td class="col-lg-2">
<span style="font-size: 17px;">@Html.DisplayFor(modelItem => item.Name)</span>
</td>
<td class="col-lg-3">
<span style="font-size: 17px;">@Html.DisplayFor(modelItem => item.ReleaseDate)</span>
</td>
<td class="col-lg-3">
<span style="font-size: 17px; font-style:italic">@Html.DisplayFor(modelItem => item.Description)</span>
</td>
<td class="col-lg-3 col-lg-offset-1">
<button type="button" class="btn btn-warning col-lg-4" onclick="location.href='@Url.Action("Edit", "Movie", new { id = item.ID })';return false;"><span style="margin-right: 5px" class="glyphicon glyphicon-pencil"></span>Edit</button>
<button type="button" class="btn btn-danger col-lg-4 col-lg-offset-4" onclick="location.href='@Url.Action("Delete", "Movie", new { id = item.ID })' ;return false;"><span style="margin-right: 5px" class="glyphicon glyphicon-trash"></span>Delete</button>
</td>
</tr>
<tr>
<td colspan="12">
<p style="font-size: 17px; font-style: italic; font-family: 'Roboto', sans-serif">
Movie ID: @Html.DisplayFor(modelItem => item.ID)
<br />
Placeholder
</p>
</td>
</tr>
}
</table>
<span style="font-style: italic; font-size: 15px; font-family: 'Roboto', sans-serif; padding-top:0px" />click for details</span>
</div>

<script>

$(function () {
$("td[colspan=12]").find("p").hide();
$("td[colspan=12]").addClass("nopadding");

$("tr").click(function () {
var $target = $(this);
var $detailsTd = $target.find("td[colspan=12]");
if ($detailsTd.length) {
$detailsTd.find("p").slideUp();
$detailsTd.addClass("nopadding");
} else {
$detailsTd = $target.next().find("td[colspan=12]");
$detailsTd.find("p").slideToggle();
$detailsTd.toggleClass("nopadding");
}
});
});

</script>
@Scripts.Render("~/bundles/myscript")


Controller code for Movie

namespace WebApplication2.Controllers
{
public class MovieController : Controller
{
private MoviesEntities db = new MoviesEntities();

// GET: /Movie/
public ActionResult Index(string Filter, string searchString)
{

if (String.IsNullOrEmpty(searchString) || (Int32.Parse(Filter) == 0))
{
return View(db.Movies.ToList());
}
else
{
var parameter = Int32.Parse(Filter);
return View(db.Movies.Where(x => (parameter == 1 && x.Name.Contains(searchString)) || (parameter == 2 && x.Description.Contains(searchString))).Distinct().ToList().OrderBy(x => x.Name));
}

}

// GET: /Movie/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}

// GET: /Movie/Create
public ActionResult Create()
{
return View();
}

// POST: /Movie/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Name,ReleaseDate,Description")] Movie movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie);
db.SaveChanges();
return RedirectToAction("Index");
}

return View(movie);
}

// GET: /Movie/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}

// POST: /Movie/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,Name,ReleaseDate,Description")] Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}

// GET: /Movie/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}

// POST: /Movie/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Movie movie = db.Movies.Find(id);
db.Movies.Remove(movie);
db.SaveChanges();
return RedirectToAction("Index");
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}


Yea so generally I click the "Edit" button, I go to a new View and then I make changes and save it to the DB. But I would like to do that all within the Index View by just clicking on the data itself (
@Html.DisplayFor(modelItem => item.INSERT_ITEM_NAME)
: Title, Release Date, Description, MovieID, Placeholder) and turn the respective option into a text field for editing.

Reference: I would also have a "Save" button appear during this time to actually save the changes that would replace the Edit and Delete until the editing finished.

Again I am not sure if this is entirely possible, but any and all help would be greatly appreciated!

Answer

In your Razor template, you will need both an @Html.DisplayFor(...) and an @Html.EditorFor(...). The text field should be hidden, and upon clicking the display text, JavaScript needs to hide the display text and show the text field, then set focus to it:

<span class="item-display">
    @Html.DisplayFor(modelItem => modelItem.Description)
</span>
<span class="item-field">
    @Html.EditorFor(modelItem => modelItem.Description)
</span>

Some CSS:

.item-field: {
    display: none;
}

And some jQuery for good measure:

$(document.documentElement)
    .on("click", "span.item-display", function(event) {
        $(event.currentTarget)
            .hide()
            .next("span.item-field")
            .show()
            .find(":input:first")
            .focus()
            .select();
    })
    .on("keypress", "span.item-field", function(event) {
        if (event.keyCode != 13)
            return;

        event.preventDefault();

        var $field = $(event.currentTarget),
            $display = $field.prev("span.item-display");

        $display.html($field.find(":input:first").val());
        $display.show();
        $field.hide();
    });

JSFiddle: http://jsfiddle.net/A3bg6/1/

When pressing ENTER on the text field, it will hide the field and show the display, plus update the display.

Comments