I've got a partial view that lists details about my entities. Details include a photo, caption, date, and name. There is an ActionLink for Delete that removes the item from the list.
I would like to make the Caption field an editable textbox to allow the user to edit the contents. Then I wold like to add an 'Update' link that will write this data to the DB. This is the only attribute that can be edited.
Basically I want to avoid the user having to open an 'Edit' form to change this field. I would like to be able to do it from within the list. Not sure how to create the 'Update' ActionLink, send it to the controller, and have the user-entered text available to the controller method.
The model that you pass to the view is of a certain type(of item in db). So, if You change the label with name "Caption" to a textbox of the same name, then the binding would remain the same - the output would be a textbox with the caption taken from the model passed to the View in Controller Action.
As for the update link:
I would create an Action of the same name as the View with [HttpPost] attribute and the model as parameter. The action would be invoked by a submit button in View (so all the labels and the textbox should be enclosed in a form). Inside that action perform the db update.
So:
The View should be something like this:
#model db.Foo
#using (Html.BeginForm()) {
<fieldset>
#Html.LabelFor(model=>model.Name)
(...)
#Html.TextBoxFor(model=>model.Caption)
<input type="submit" value="Update Caption"/>
</fieldset>
}
and the controller actions:
//The action that passes the model to the View
public ActionResult Details()
{
//
//get foo here
//
return View(foo); //Where foo is the item You use for your model
}
[HttpPost]
public ActionResult Details(foo model)
{
//
//Update model here with model of type foo
//
}
Hope this helps
Since it seems to be an extremely basic edit, I would just put the Caption in a Div then using CSS/jQuery display an edit button (glyphicon glyphicon-pencil) on mouseover. On click set the Div to Editable (use styling to show it's in edit mode, and only exit edit mode with a save button). On save, do a jQuery Ajax call to update the value in the database.
Please be extra cautious to not have any SQL Injection Attacks as editable content could contain anything from a user.
CSS might look like:
.editor editor-display button
{
display: none;
}
.editor editor-display:hover .edit-button
{
display: inline-block;
}
.editor editor-edit .edit-button
{
display: none;
}
.editor editor-edit .save-button
{
display: inline-block;
}
Html might look like:
<div class="my-entity editor editor-display" data-id="5" >
<div class="edit-content">#model.Caption</div>
<button id="edit-caption-button" class="edit-button" />
<button id="save-caption-button" class="save-button" />
<div>
Javascript might look like:
$(document).ready(function()
{
$("#save-caption-button").on("click", function()
{
$(this).parent("editor").removeClass("editor-display").addClass("editor-edit");
});
$("#save-caption-button").on("click", function()
{
$.ajax({
url: "/MyEditity/UpdateCaption",
data: { id: $(this).parent(".my-entity").data("id"),
caption: $(this).parent(".my-entity").find(".edit-content").first().text() };
});
$(this).parent("editor").removeClass("editor-edit").addClass("editor-display");
});
});
Controller:
public ActionResult UpdateCaption(int id, string caption)
{
// lookup entity by id
// change caption value
// save
return new EmptyResult();
}
This is a super basic (UNTESTED!) example, with no exception handling.
Related
I'm trying to create paging inside html form.
How do I make each page button change the model's CurrentPage property?
I'm new to MVC, so bear with me please.
I have a list of reports that I need to display in a view named Search. I made a model for that (ReportViewModel), and another model that holds a list of those, named SearchViewModel. In SearchViewModel I have some other variables like SearchName, SearchDate, CurrentPage, PageCount, PageSize, etc. The goal is to do paging and searching in one action named Search in ReportsController.
Now, I would like to keep that action simple by giving that function only SearchViewModel instance as a parameter, so that inside I use all variables of that model. Doing that, I won't have to type parameter names twice (once in SearchViewModel and once as parameters in Search action). I will not need that search function elsewhere, so it's ok for it to not have any parameters other than SearchViewModel instance.
So, very loosely, it looks something like this:
ReportsController.cs
public class ReportsController : Controller
{
public ActionResult Search(SearchViewModel model)
{
if (!ModelState.IsValid)
return View(model);
// do search magic here
return View(model)
}
}
Search.cshtml
#model ISFinReports.Models.SearchViewModel
#{
ViewBag.Title = "List";
Layout = "~/Views/_masterLayout.cshtml";
}
<link href="~/CSS/Search.css" rel="stylesheet" />
<script src="~/Scripts/Search.js"></script>
#using (Html.BeginForm("Search", "Reports", FormMethod.Get))
{
<div id="divSearchBar">
// add #Html.DropDownListFor, #Html.TextBoxFor and submit button here
</div>
<div id="divReportsTable">
<table id="tblReports">
#if (Model.Reports != null)
{
foreach (var r in Model.Reports)
{
// add tr, td of all reports in report list
}
}
</table>
</div>
<div style="text-align:center">
#for (int i = 0; i < Model.PageCount; i++)
{
#Html.ActionLink(i.ToString(), "Search", "Reports", new { CurrentPage = i });
// or
// <input type="submit" value="#i" onClick="meybe something like this?" />
}
Current Page #Html.EditorFor(q => q.CurrentPage), of total #Model.PageCount pages
</div>
}
I know that I can simply type twice all the data I need to flow between controller and view, once in model and once as Search action parameters, but that's extactly what I'm trying not to do. Currently I have a textbox for editing the current page, and it works, because it's created via #Html.EditorFor, but I'd like to have multiple buttons for each page.
I could maybe create a hidden field in that form, and create a javascript function that'll find that hidden field by id, and change it's value, and then call whatever the submit button onClick function is. BUT, is there a better/shorter/more-MVC way to implement that?
I have a form where a user fils in some details, and submits the data with a submit button.
I need to add a new button to my form, which does a post back, passing the model back, adds some items to a List<> (which is a property of the model), and then refreshes the page.
How do I route the post to a different action than the form's post method?
You can use the name of the submit button as a parameter in your action.
Let's say you have the following submit buttons:
<input type="submit" name="btnCompare" value="Compare" />
<input type="submit" name="btnSave" value="Save" />
You can capture both of them in one action and then check to see which one was clicked:
[HttpPost]
public ActionResult SavedResults(Results myResults, string btnCompare, string btnSave) {
if (btnCompare != null) {
//btnCompare was clicked. Do related stuff here.
}
else if (btnSave != null) {
//btnSave was clicked. Do related stuff here.
}
}
you should use two different buttons with same name but different value and use this button name in your post action on controller with condition.
use this in your view
use following code on controller.
[HttpPost]
public ActionResult ActionName(ModelName model, string Action)
{
if(Action.Equals("button1")
{
}
if(Action.Equals("buttons")
{
//write your code to add items in list
model.itemList.Add(newitem);
}
return RedirectToAction("ActionName",model);
}
I'm pretty much a jquery newb...I've almost got this working I think, let me know if i can clarify anything.
I have a screen displaying a list..let's call them affiliates. To create a new affiliate, a modal style pop up dialogue is used.
When the dialogue "Create" button is clicked, the form data must be saved (creating a new affiliate), the dialogue disappears, and the affiliate list updates without reloading the page
The jquery file at the bottom shows how I'm trying to do it now: trying to detect a click on the "confirm" button, get the form to submit using the data target property, and using the form's target property to know what container to update.
What is happening is: nothing. The cancel button works, create button does absolutely nothing.
Also note that the "Create" button, which will act as the form's submit, is not located within the <form> tags.
I'm pretty sure I'm doing modals wrong but let's ignore that for the moment and focus on the async post and list update. I've included my relevant code below to support my post.
--AffiliateListPartial
#model IPagedList<Acme.Business.DomainModel.Affiliates.Affiliate>
<div class="items-list" id="affiliate-list-view">
#foreach (var item in Model)
{
<a href="#Url.Action("AffiliateDetails", "Vendor", new { id = item.AffiliateId })">
//basic spans and razor display list in here..nothing notable
</a>
}
</div>
The above partial view is contained within a full view, lets call it AffiliateList. Nothing particularly relevant in there except that it is controlled by the VendorController/Affiliatelist method.
The VendorController.AffiliateList looks like:
public ActionResult AffiliateList(string searchTerm = null, int page = 1)
{
var userId = WebSecurity.GetUserId(User.Identity.Name);
var model = (from a in db.Affiliates.ToList()
where a.VendorId == userId
select a).ToPagedList(page, 15);
if(Request.IsAjaxRequest()) return PartialView("_AffiliateListPartial", model);
return View(model);
}
The modal style dialoque for creating a new affiliate (I'll just include the lines that I think are relevant):
_Modal.AffiliateCreate.Partial
<form id="affiliate-create-form" class="form" method="post" action="#Url.Action("AffiliateCreate")" data-acme-ajax="true" data-acme-target="#affiliate-list-view">
// multiple input elements
</form>
<div class="modal-footer">
<button name="close_modal"><span>Cancel</span></button>
<button name="confirm" data-acme-target="#affiliate-create-form"><span>Create</span></button>
</div>
And the VendorController.AffiliateCreate method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AffiliateCreate(Affiliate affiliate)
{
if (!ModelState.IsValid) return View(affiliate);
db.Affiliates.Add(affiliate);
db.SaveChanges();
return RedirectToAction("AffiliateList");
}
And the .js file's relevant parts:
$("button[name='confirm']").on("click", function() {
var $form = $(this).attr("data-acme-target");
var options = {
url: $form.attr("action"),
type: $form.attr("type"),
data: $form.serialize()
};
$.ajax(options).done(function (data) {
var $target = $($form.attr("data-acme-target"));
var $newHtml = $(data);
$target.replaceWith(data);
$newHtml.effect("highlight");
});
$(".modal_overlay").css("opacity", "0");
$(".modal_container").css("display", "none");
return false;
});
$("button[name='close_modal']").on("click", function(event) {
$(".modal_overlay").css("opacity", "0");
$(".modal_container").css("display", "none");
return false;
});
var $form = $(this).attr("data-acme-target"); is getting the attribute named 'data-acme-target' of the button, rather than the form it's associated with. So then when you're using $form.attr('action'), you aren't getting anything back.
Since data-acme-target is an ID to another control that is the form you want to submit, use $($(this).attr("data-acme-target")); to get it.
I have created a function for doing on validations on textbox entered value based on selection in drop downlist .. ..suppose If the dropdownlist selected item is amount then the entered value in textbox must be in between 10 t0 20 like this i have got two more validations.. for that purpose, I have got one textbox and one dropdwonlist and one button in my view
when i enter value in textbox as 30 and select the drop downlist item as "Amount" and then click on the submit button, the view is not showing any error message and ( if i left textbox as empty and then press submit button its should show error msg but its not showing) (I have written Custom functions for validating these ones on server side)
I have put a breakpoint at a postValues method but its not hitting ...
for that I have done like this (controller part)
public class CrossFieldsTxtboxesController : Controller
{
public ActionResult Index()
{
var itemsforDropdown = new List<SelectListItem> {
new SelectListItem{ Text = "Amount" , Value = "Amount"},
new SelectListItem{Text= "Pound", Value ="Pound"},
new SelectListItem {Text ="Percent", Value ="percent"}
};
ViewData["Listitems"] = itemsforDropdown;
return View("DdlCrossFields");
}
[HttpPost]
public ActionResult PostValues(CrossFieldValidation model)
{
if (!ModelState.IsValid)
{
return View(model);
}
else
{
return RedirectToAction("DdlCrossFields");
}
}
}
and this is my view part
#model MvcSampleApplication.Models.CrossFieldValidation
#{
ViewBag.Title = "DdlCrossFields";
}
<h2>DdlCrossFields</h2>
#using (Html.BeginForm())
{
<div class ="editor-field">
#Html.TextBoxFor(m => m.TxtCrossField)
#Html.ValidationMessageFor(m=>m.TxtCrossField)
</div>
#Html.DropDownList("Listitems",ViewData["Listitems"] as SelectList)
<input id="PostValues" type="Submit" value="PostValues" />
}
and this is my model part
public class CrossFieldValidation
{
public string DDlList1
{ get; set; }
[Required(ErrorMessage = "Quantity is required")]
[Display(Name= "Quantity:")]
[ValueMustbeInRange]
[NumericAttributes]
public string TxtCrossField
{ get; set; }
}
would any one have any idea why its not working for button click , any suggestions also would be grateful
many thanks..
I don't see any place where you specify an action that should handle POST request (PostValues in your case). You can use an overload of Html.BeginForm and specify POST action name explicitly:
Html.BeginForm("PostValues", "CrossFieldsTxtboxes")
If I'm right, your POST requests go to Index action and therefore ModelState.IsValid is not checked there.
You can use client side validation using jQuery Unobtrusive validation plugin. Please check if you have the following keys in your web.config file:
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
But note that custom validation attributes require additional JavaScript code to work on client.
I dont see where you are actually calling the method when you do the post - do you have a javscript click handler?
You need to have the method and controller you want to go to in your Html.BeginForm() or you can have something like the below in your view:
#Html.ActionLink("Post Values", "PostValues", "CrossFieldsTxtboxesController", null, new { #class = "postValuesButton", onclick = "return false;" })
I have a controller "Contracts" and 3 GET methods: "All", "Focus", which contains grid, and "Edit" for editing, and one POST method: "Edit".
Users can access to Edit from any grid by using button. But after POST executed, i want to redirect them to action that send request.
For example:
open "Focus" grid
select row
clicked edit
make changes, and click save
finally redirect to "Focus" grid (!)
or
open "All" grid
select row
clicked edit
make changes, and click save
finally redirect to "All" grid (!)
public ActionResul All()
{
var items=dbContext.Items;
return View("All",items);
}
in the All View you will have the grid with the data. Selecting a record from the Grid and clicking Edit will take you to the second Action method for Edit.
You may pass a some flag when calling the edit method here. You may add that when you build the edit link like this
#Html.ActionLink("Edit","Edit","Item",new { #id=item.Id, #from="all"},null)
So my Edit will have a querystring key "from" with a value "all"
Same way,in your the View for your Focus, you may pass a different value
#Html.ActionLink("Edit","Edit","Item",new { #id=item.Id, #from="focus"},null)
Now the Edit Action method, You read the parameter and set it as the property value of our edit view model. you may want to add this property to your ViewModel.
public ActionResult Edit(int id,string from)
{
EditItemViewModel item=GetItem(id);
item.From=from;
return View(item);
}
and this View will have the edit form. We will keep the value of From inside a form element so that we can use that on the form post.
#model EditItemViewModel
#using(Html.BeginForm())
{
#Html.HiddenFor(m => m.Id);
#Html.TextBoxFor(m => m.ItemName)
#Html.TextBoxFor(m => m.ItemDesc)
#Html.HiddenFor(m => m.From)
<input type="submit" value="Update" />
}
Where user can edit and submit it again . you handle that in the HttpPost Edit action method. Check the From property value and decide where to redirect after saving
[HttpPost]
public ActionResult Edit(EditItemViewModel model)
{
if(ModelState.IsValid)
{
//Save data
if(model.From=="all")
return RedirectToAction("All");
else
return RedirectToAction("Focus");
}
return View(model);
}
i think that your post edit can be like this
After clicking on the gridrow having (Edit/All/1 or Edit/Focus/2) you can redirect to this
public ActionResult Edit(int id, yourviewmodel viewmodel,string YourFilter)
{
return RedirectToAction(YourFilter);
}
and in global.asax you can set your route like this for the edit submit
routes.MapRoute(
"filters",
"All",
new { controller = "Contracts", action = "All" }
);
routes.MapRoute(
"filters1",
"focus",
new { controller = "Contracts", action = "focus" }
);
for edit click
routes.MapRoute(
"EditFilter",
"Edit/{YourFilter}/{id}",
new { controller = "Contract", action = "Edit",YourFilter = UrlParameter.Optional,id = UrlParameter.Optional }
);