when I define my Delete method below with [HttpPost], the Delete method cannot be called from the View. However, when deleting [HttpPost] line, it works normally. I tried lots of the things and actually it may be related to wrong usage of #Html.Hidden or #using (Html.BeginForm() in my View. So, could you please clarify me about these points below?
1) I do not open a View after clicking delete button on my WebGrid. After a confirmation method the Delete method in the Controller should be called and the record should be deleted by staying on the same page.So, is it wrong not using [HttpPost] for my Delete method below?
2) If it is possible, how should I do in order to use [HttpPost] for the Delete method? Which changes do I have to do on my View i.e. using form or hidden property?
View:
#model IEnumerable<MyProject.Domain.Entities.Applicant>
#using PRMeetingReg.WebUI.HtmlHelpers
#{
var grid = new System.Web.Helpers.WebGrid(
source: Model,
columnNames: new List<string>() { "Title" },
ajaxUpdateContainerId: "myGrid",
defaultSort: "Name",
canPage: true,
canSort: true,
rowsPerPage: 5
);
grid.SortDirection = SortDirection.Ascending;
}
<div class="Grid">
#grid.GetHtml(
tableStyle: "table",
headerStyle: "webgrid-header",
footerStyle: "webgrid-footer",
rowStyle: "webgrid-row-style",
alternatingRowStyle: "webgrid-alternating-row",
selectedRowStyle: "webgrid-selected-row",
firstText: "<<",
lastText: ">>",
mode: WebGridPagerModes.All,
fillEmptyRows: true,
numericLinksCount: 5,
columns: grid.Columns(
grid.Column("ApplicantID", "No", canSort: true),
grid.Column("Name", "Name", canSort: true),
grid.Column("Surname", "Surname", canSort: true),
//for using multiple Html.ActionLink in a column using Webgrid
grid.Column("Actions", format: (item) =>
new HtmlString(
#Html.ActionImage("../../Content/icons/detail.png", "Detail", "icon-link", "Detail", "Admin", new { applicantId= item.ApplicantID }).ToString() +
#Html.ActionImage("../../Content/icons/edit.png", "Edit", "icon-link", "Edit", "Admin", new { applicantId= item.ApplicantID }).ToString() +
#Html.ActionImage("../../Content/icons/delete.png", "Delete", "icon-link", "Delete", "Admin", new { applicantId= item.ApplicantID }).ToString()
)
)
)
)
<p>
<input id="add" type="submit" value="Yeni Ekle" class="button" />
</p>
</div>
Controller:
[HttpPost]
public ActionResult Delete(int applicantId)
{
Applicant deletedApplicant = repository.DeleteApplicant(applicantId);
if (deletedApplicant != null)
{
TempData["message"] = string.Format("{0} was deleted",
deletedApplicant.Name);
}
return RedirectToAction("Index");
}
My HTML Helper:
public static MvcHtmlString ActionImage(this HtmlHelper html, string imagePath, string alt, string cssClass,
string action, string controllerName, object routeValues)
{
var currentUrl = new UrlHelper(html.ViewContext.RequestContext);
var imgTagBuilder = new TagBuilder("img");
imgTagBuilder.MergeAttribute("src", currentUrl.Content(imagePath));
imgTagBuilder.MergeAttribute("title", alt);
imgTagBuilder.MergeAttribute("class", cssClass);
string imgHtml = imgTagBuilder.ToString(TagRenderMode.SelfClosing);
var anchorTagBuilder = new TagBuilder("a");
anchorTagBuilder.MergeAttribute("href", currentUrl.Action(action, controllerName, routeValues));
anchorTagBuilder.InnerHtml = imgHtml;
string anchorHtml = anchorTagBuilder.ToString(TagRenderMode.Normal);
return MvcHtmlString.Create(anchorHtml);
}
Thanks in advance.
Your ActionImage custom helper generates an image containing an anchor tag inside (<a>). In HTML an anchor sends GET request. Your controller action is decorated with the HttpPost request which explains why it is never called.
One possibility to make this work is to use an AJAX request and perform a POST request instead of GET when clicking on the Delete link.
Related
in web-grid i can not use navigation properties between my classes(products and productimages classes). for example i have used below code in web grid:
grid.Column("", "test",item=> (item.ProductImages.First().Id)+(item.Price))
but i got error:
'System.Collections.Generic.HashSet<WebStore.Models.ProductImage>' does not contain a definition for 'First'
my total code is like below:
#model IEnumerable<WebStore.Models.Product>
#using System.Linq;
#{
var grid = new WebGrid(source: Model, rowsPerPage: 5,ajaxUpdateContainerId:"divGrid");
}
#grid.GetHtml(tableStyle: "gridStyle", headerStyle: "gridHeader", rowStyle: "gridRow", alternatingRowStyle: null,htmlAttributes:new{Id="divGrid"},
columns: new WebGridColumn[] {
grid.Column("ProductName", "Product Name"),
grid.Column("Price", "Price"),
grid.Column("Description", "Description"),
grid.Column("CategoryName","Category Name",x=>x.Category.CategoryName),
grid.Column("", "test",item=> (item.ProductImages.First().Id)+(item.Price)),
grid.Column("","",x=>Html.ActionLink("Edit", "Edit", new{id=x.Id})),
grid.Column("","",x=>Html.ActionLink("Details", "Details", new{id=x.Id})),
grid.Column("","",x=>Html.ActionLink("Delete", "Delete", new{id=x.Id}))
}
)
this is my index view:
#model IEnumerable<WebStore.Models.Product>
#using System.Linq
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_LayoutCategory.cshtml";
}
<br/>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<div id="divGrid">
#{ Html.RenderPartial("_ProductTitle", Model); }
</div>
this is my RenderPartial that use web grid before i posted:
#model IEnumerable<WebStore.Models.Product>
#using System.Linq
#ViewBag.test
#{
var grid = new WebGrid(source: Model, rowsPerPage: 5,ajaxUpdateContainerId:"divGrid");
}
#grid.GetHtml(tableStyle: "gridStyle", headerStyle: "gridHeader", rowStyle: "gridRow", alternatingRowStyle: null,htmlAttributes:new{Id="divGrid"},
columns: new WebGridColumn[] {
grid.Column("ProductName", "Product Name"),
grid.Column("Price", "Price"),
grid.Column("Description", "Description"),
grid.Column("CategoryName","Category Name",x=>x.Category.CategoryName),
grid.Column("", "test",item=>(int) (item.ProductImages.FirstOrDefault().Id)+(int)(item.Price)),
grid.Column("","",x=>Html.ActionLink("Edit", "Edit", new{id=x.Id})),
grid.Column("","",x=>Html.ActionLink("Details", "Details", new{id=x.Id})),
grid.Column("","",x=>Html.ActionLink("Delete", "Delete", new{id=x.Id}))
}
)
It's because definition of First is available in System.Linq. So, you should have System.Linq in your razor page :
#using System.Linq;
if you are using Linq in multiple page, you can add System.Linq namespace in web.config so that you do not need to write above using in each page. You can add namespace in web.config in following configuration :
<system.web.webPages.razor>
<pages>
<namespaces>
<add namespace="System.Linq" />
</namespaces>
</pages>
</system.web.webPages.razor>
i got the answer of my question with below code from the net:
I followed your steps and test your code and the same error display. So I think the format in your code can not be used in webgrid. So I think about another way to meet your requirement: we need a ViewModel to display what you want to show in the View and search the first item in Controller then save as ViewModel and pass the ViewModel to View, then we do not need search in the View. Here I will show you the steps with my demo.
Now, we have two model Product and Quantity that one product has many quantities. We should create a ViewModel.
ViewModel.cs:
public class ViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public long quan { get; set; }
}
We can initialize some data and search the first value of Quantity depend on Product in Controller and assign to the ViewModel.
InventoryController .cs:
public class InventoryController : Controller
public ActionResult WebgridSample()
{
ObservableCollection<Product> inventoryList =
new ObservableCollection<Product>();
inventoryList.Add(new Product
{
Id = "P101",
Name = "Computer",
Description = "All type of computers",
quantity = new List<Quantity>
{
new Quantity {QUAN = 100 },
new Quantity {QUAN = 200 },
new Quantity {QUAN = 300 }
}
});
inventoryList.Add(new Product
{
Id = "P102",
Name = "Laptop",
Description = "All models of Laptops",
quantity = new List<Quantity>
{
new Quantity {QUAN = 400 },
new Quantity {QUAN = 500 },
new Quantity {QUAN = 600 }
},
});
inventoryList.Add(new Product
{
Id = "P103",
Name = "Camera",
Description = "Hd cameras",
quantity = new List<Quantity>
{
new Quantity {QUAN = 700 },
new Quantity {QUAN = 800 },
new Quantity {QUAN = 900 }
}
});
IEnumerable<string> model = (from sig in inventoryList
select new ViewModel
{
Name = sig.Name,
Description = sig.Description,
quan = sig.quantity.FirstOrDefault(),
}).ToList();
return View(model);
}
We can call each parameters of ViewModel in webgrid without using First().
WebgridSample.cshtml:
<div id="gridContent">
#grid.GetHtml(tableStyle: "webGrid",
headerStyle: "header",
alternatingRowStyle: "alt",
selectedRowStyle: "select",
columns: grid.Columns(
grid.Column("Id", ),
grid.Column("Name", " Name"),
grid.Column("Description", "Description", style: "description"),
grid.Column("Quantity", "quan "</i>)
))
Is it possible to pass an hidden field value from Razor View to Controller inside or tag? As the field is hidden, it is really a problem to pass its value to the Controller. On the other hand I think the only way to pass this hidden field is using input field inside hyperlink. How to create a code like below?
<a href="/Admin/Delete?ApplicantID=44">
<img class="myclass" src="../../Content/delete.png" title="Delete" />
<input type="hidden" value= "Delete" /></a>
Updates
View:
...
grid.Column("Actions", format: (item) =>
new HtmlString(
#Html.ActionImage("../../Content/detail.png", "Detail", "icon-link", "Detail", "Admin", new { applicantId = item.ApplicantID }).ToString() +
#Html.ActionImage("../../Content/edit.png", "Edit", "icon-link", "Edit", "Admin", new { applicantId = item.ApplicantID }).ToString() +
#Html.ActionImage("../../Content/delete.png", "Delete", "icon-link", "Delete", "Admin", new { applicantId = item.ApplicantID }).ToString()
)
)
...
Controller:
[HttpPost]
public ActionResult Delete(int applicantId)
{
Applicant deletedApplicant = repository.DeleteApplicant(applicantId);
if (deletedApplicant != null)
{
TempData["message"] = string.Format("{0} was deleted",
deletedApplicant.Name);
}
return RedirectToAction("Index");
}
}
Helper Method:
public static MvcHtmlString ActionImage(this HtmlHelper html, string imagePath, string alt, string cssClass,
string action, string controllerName, object routeValues)
{
var currentUrl = new UrlHelper(html.ViewContext.RequestContext);
var imgTagBuilder = new TagBuilder("img");
imgTagBuilder.MergeAttribute("src", currentUrl.Content(imagePath));
imgTagBuilder.MergeAttribute("title", alt);
imgTagBuilder.MergeAttribute("class", cssClass);
string imgHtml = imgTagBuilder.ToString(TagRenderMode.SelfClosing);
var anchorTagBuilder = new TagBuilder("a");
anchorTagBuilder.MergeAttribute("href", currentUrl.Action(action, controllerName, routeValues));
anchorTagBuilder.InnerHtml = imgHtml;
string anchorHtml = anchorTagBuilder.ToString(TagRenderMode.Normal);
return MvcHtmlString.Create(anchorHtml);
}
<input>, <textarea>, <button> and <select> element values are only passed during a form submission. Moreover, they are only passed on when assigned a name attribute.
Clicking an anchor will not pass these values (unless you interject with some JavaScript and append it to the URL). The easiest method is to turn your link in to a mini form:
#using (Html.BeginForm("Delete", "Admin", FormMethod.POST,
new { ApplicantID = #Model.ApplicantID }))
{
<!-- make sure to give this field a name -->
<input type="hidden" name="???" value="Delete" />
<input type="image" src="#Url.Content("~/Content/delete.png")" title="Delete" />
}
Otherwise, use javascript and bind to the anchor's click event and inject the hidden input's value before proceeding.
Based on discussion below, try this.
#Ajax.ActionLink("Delete",
"Delete", new { applicantid = item.applicantid },
new AjaxOptions
{
Confirm = "Delete?",
HttpMethod = "POST",
}, new { #class = "classname" })
a.classname
{
background: url(~/Content/delete.png) no-repeat top left;
display: block;
width: 150px;
height: 150px;
text-indent: -9999px; /* hides the link text */
}
NOTE: this does not need to be inside a form.
I have mvc 3 application in which i'm having one input form on Index.cshtml view. also having one webgrid which is having edit,delete button
depending upon these action links i need to change my submit button text. how can i achieve this inside homecontroller.cs ? using only one view for all edit,insert.
checking useraction inside homecontroller.cs
public ActionResult Index(string userAction)
{
if (userAction == "Edit" )
{
}
if (userAction == "Delete" )
{
}
}
View code.
<p>
<input type="submit" value="Insert" />
</p>
On webgrid having link for edit , delete
on that condition i need to change submit button text.
#if (grid != null)
{
#grid.GetHtml(
tableStyle: "grid",
headerStyle: "head",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column("", header: null, format: #<text>#Html.ActionLink("Edit", "Index", new { uid = (int)item.id, userAction = "Edit" })
#Html.ActionLink("Delete", "Index", new { uid = (int)item.id, userAction="Delete" }, new { #class = "Delete" })</text>),
}
You can store the userAction in ViewData or ViewBag and access it from the View.
public ActionResult Index(string userAction)
{
ViewBag.UserAction = userAction;
}
<input type="submit" value="#(ViewBag.UserAction == "X" ? "Y" : "Z")" />
I have created a web application in mvc3 and created two partial views
one having controls two dropdownlist.
second having webgrid which shows data from database.
partialview1.cshtml
#model Mapping.Models.SecurityIdentifierMapping
#using (Html.BeginForm("Mapping", "Home"))
{
#Html.DropDownList("SecurityID", Model.PricingSecurityID, "-- Select SecurityID --")
<br />
#Html.DropDownList("CUSIPID", Model.PricingSecurityID, "-- Select CUSIPID --")
<br />
<button type="submit">Map</button>
}
partialview2.cshtml
#model IEnumerable<Mapping.Models.SecurityIdentifierMapping>
#{
ViewBag.Title = "Mapping";
WebGrid grid = null;
if (Model.Count() > 0 ){
grid = new WebGrid(source: Model,
defaultSort: "Id",
canPage: true,
canSort: true,
rowsPerPage:20);
}
}
<h2>Mapping</h2>
#if (grid != null)
{
#grid.GetHtml(
tableStyle: "grid",
headerStyle: "head",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column("", header: null, format: #<text>#Html.ActionLink("Edit", "Edit", new { id = (int)item.id }) #Html.ActionLink("Delete", "Delete", new { id = (int)item.id })</text>),
grid.Column("PricingSecurityID"),
grid.Column("CUSIP")
)
)
}
<br />
<p>
#Html.ActionLink("Back", "Index")
</p>
in index.cshtml
<div>
#Html.Partial("_ControlsPartial",)
</div>
<div>
#Html.Partial("_WebGridPartial")
</div>
inside Indexcontroller.cs in Index()
public ActionResult Index()
{
//FOR POPULATE DROPDOWN
//SecurityIdentifierMapping objModel = new SecurityIdentifierMapping();
//objModel.PricingSecurityID = objRepository.GetPricingSecurityID();
//objModel.CUSIP = objRepository.GetCUSIP();
//return View(objModel);
//FOR DISPLAY DATA IN WEBGRID
return View(dbContext.SecurityIdentifierMappings);
}
here problem is webgrid partial view is having#model IEnumerable<Mapping.Models.SecurityIdentifierMapping>
and
controlpartilview is having #model Mapping.Models.SecurityIdentifierMapping
so HOW CAN I CREATE A VIEWMODEL.cs A NEW CLASS WHICH WILL HAVE BOTH MODELS AND HOW CAN I WRITE IT IN INDEX(() SO THAT THAT METHOD WILL POPULATE DROPDOWN ALSO AND SHOW DATA IN WEBGRID ALSO ?
Why not just create a custom View Model class that contains two properties:
public class SecurityIdentifierMappingViewModel
{
public IEnumerable<SecurityIdentifierMapping> MappingList {get; set; }
public SecurityIdentifierMapping Mapping {get; set; }
}
Then you can pass this custom view model to the Index view and the corresponding property as the view model of each of the partials
EDIT
Your Index action would then look something like this:
public ActionResult Index()
{
// single mapping
var mapping = new SecurityIdentifierMapping();
maping.PricingSecurityID = objRepository.GetPricingSecurityID();
mapping.CUSIP = objRepository.GetCUSIP();
var viewModel = new SecurityIdentifierMappingViewModel
{
Mapping = mapping,
MappingList = dbContext.SecurityIdentifierMappings.ToList()
};
return View(viewModel);
}
And in Index.cshtml:
#model SecurityIdentifierMappingViewModel
<div>
#Html.Partial("_ControlsPartial", Model.Mapping)
</div>
<div>
#Html.Partial("_WebGridPartial", Model.MappingList)
</div>
I have table:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Add</legend>
<br />
#{
var grid = new WebGrid(ViewBag.produkty,null, "names", 5);
}
#grid.GetHtml(
tableStyle: "grid",
headerStyle: "head",
alternatingRowStyle: "alt",
columns: grid.Columns(
grid.Column("name"),
grid.Column("value"),
grid.Column(header: "Add", format: (item) =>
new HtmlString(
Html.TextBoxFor(model => model.add).ToString())),
grid.Column( header: "Ok", format: (item) =>
new HtmlString(
Html.ActionLink("OK", "add_method", new { ID_name = item.ID_name }).ToString()))
)
)
</fieldset>
}
Controller:
public ActionResult use()
{
var nam = (from d in baza.Names
select new { d.ID_name, d.name, d.value}).ToList();
ViewBag.names= nam;
return View();
}
public ActionResult add_method(int ID_name, useModel use)
{
Use us = new Use();
var dat = DateTime.Today;
us.value = use.add;
us.ID_Name= ID_name;
us.data = dat;
baza.Zuzycies.InsertOnSubmit(us);
baza.SubmitChanges();
return RedirectToAction("use", "Product");
}
Model:
public class useModel
{
public int ID_name{ get; set; }
public decimal value{get;set;}
public string date { get; set; }
}
So, I have list of product on page. And I want to add a value (amount of product) into TextBox and press a ActionLink "OK" next to the textbox. How can I get amount of product in add_method? Or how insert submit button next to every one product (instead ActionLink "OK"), then is enought make use POST method...
You can use a grid componet with built-in edits functions (like the telerik Grid).
I think it's better to use ajax not reagular post request for your scenario.
Or you can do that ajax calls to the server with jquery, just send the parameters to the controller.