I have some problems with deletion item from database (SQLServer) using parameters for that. I want to press "Delete" reference in Index() then put name parameter in Delete() and redirect action to Index() again and show content of db. When I press "Delete" reference I show nothing but start page of Index() :(
public async Task<IActionResult> Delete(string nm)
{
IQueryable<Phone> users = db.Phones;
if (!String.IsNullOrEmpty(nm))
{
users = users.Where(p => p.Name.Contains(nm));
foreach (var item in users)
{
db.Phones.Remove(item);
}
await db.SaveChangesAsync();
}
return RedirectToAction("Index");
}
#model DataApp2.Models.Phone
#{
ViewBag.Title = "Delete";
}
<form method="get">
<div class="form-inline form-group">
<label class="control-label">Name: </label>
#Html.TextBox("nm", Model.Name, htmlAttributes: new { #class = "form-control" })
<input type="submit" value="Delete" class="btn btn-default" />
</div>
</form>
Building the input yourself and using a form is a bit overkill/overcomplicated. Instead, you can leverage the .NET MVC framework to send the request to your action
by replacing the form you posted and everything inside of it with:
#Html.ActionLink("Delete", "Delete", new { nm = Model.Name })
This will generate a link (<a> tag) with the text "Delete" (first param of the ActionLink) and send the Model.Name in a data field called nm to the Delete action in your controller (second param of the ActionLink).
I've put together a proof of concept showing that this works:
View:
#Html.ActionLink("Delete", "Delete", new { nm = "hi" })
Controller Action:
public ActionResult Delete(string nm)
{
if (!String.IsNullOrEmpty(nm))
{
ViewBag.Name = nm;
}
return RedirectToAction("Index");
}
the controller is successfully setting ViewBag.Name in this example. Note as far as the issue you're having, it makes no difference that I'm returning a ActionResult here instead of async Task<IActionResult> as you are.
I'm guessing that you're not populating Model.Name in the action that initially loads the page. Please post the code for your get action that loads the view if you'd like more information. You can test this theory by sticking:
#if (string.IsNullOrEmpty(Model.Name))
{
<h1>Name is empty!</h1>
}
else
{
<h1>Name is #Model.Name</h1>
}
in your view if you dont want to step through the code via the debugger
Related
So I have 2 controller classes;
AnnouncementsController, this just generates a homepage of sorts with posts from users on it.
// GET: Announcements
public async Task<IActionResult> Index()
{
var announcements = await _context.Announcement.ToListAsync();
announcements = announcements.OrderByDescending(x => x.CreatedOn).ToList();
foreach (var ann in announcements)
{
ann.TimeAgo = ann.CreatedOn.TimeAgo();
}
var users = await _context.Users.ToListAsync();
var comments = await _context.Comment.ToListAsync();
AnnouncementAndCommentsViewModel aacVM = new AnnouncementAndCommentsViewModel();
AnnouncemenstViewModel announcemenstViewModel = new AnnouncemenstViewModel();
announcemenstViewModel.Announcement = announcements;
announcemenstViewModel.User = users;
announcemenstViewModel.Comment = comments;
CommentViewModel commentViewModel = new CommentViewModel();
aacVM.announcemenstViewModel = announcemenstViewModel;
aacVM.commentViewModel = commentViewModel;
ViewData.Add("currentUserID",GetCurrentUser().Id);
return View(aacVM);
}
Then I have the CommentsController
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CommentViewModel commentViewModel)
{
if (ModelState.IsValid)
{
var comment = new Comment();
comment.CreatedOn = DateTime.Now;
comment.Body = commentViewModel.Comment.Body;
Announcement announcement = GetAnnouncement(commentViewModel.Announcement.AnnouncementID);
comment.Announcement = announcement;
ApplicationUser user = GetCurrentUser();
comment.User = user;
_context.Add(comment);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(commentViewModel);
}
From my AnnouncementController Index view, I have a list of posts and then I want to be able to write and add comments directly in that view as opposed to going to another view.
I have 2 ViewModels, one for storing announcements and one for comments.
I'm trying to call the Create Post method in the Comments Controller with its argument that accepts a CommentViewModel although I can't figure it out.
I keep getting sent to the Create Get Method with the body filled in.
Here's my index view and how I'm trying to post the data to the CommentsController
#model Treharris.Models.AnnouncementAndCommentsViewModel;
.
.
.
#foreach (var item in Model.announcemenstViewModel.Announcement)
{
.
.
.
<div class="announcement-body">
<p>#Html.DisplayFor(modelItem => item.Body)</p>
</div>
<div class="comments">
<p id="comment">Comments</p>
#using(Html.BeginForm("Create","Comments", FormMethod.Post,
new { cvm = Model.commentViewModel }))
{
##Model.commentViewModel.Announcement = item;
#Html.HiddenFor(m => m.commentViewModel.Announcement)
#Html.TextBoxFor(m => m.commentViewModel.Comment.Body,
new { placeholder = "Comment body" })
<input type="submit" value="Create" />
}
As #Shyju commented, for model binding to work using the form extensions, the Model that you pass to the View has to be the same that the post action receives.
If you look at the HTML that gets generated,
#Html.HiddenFor(m => m.commentViewModel.Announcement)
outputs something as
<input type="hidden" name="commentViewModel.Announcement" value="propVal" />.
On the Action you expect a CommentViewModel object that has no property named commentViewModel, but has a property named Announcement. That is why the binding does not occur.
Either change the parameter on the post action method to match the View Model, but be aware that all properties that do not exist on the form are posted as null,
OR
drop the Html form extension methods that are being deprecated in .NET Core and use simple Html for these type of bindings as the follow:
<form asp-controller="Comments" asp-action="Create" method="post">
<input type="hidden" name="Announcement" value="#Model.commentViewModel.Announcement" />
<input type="text" name="Comment.Body" placeholder="Comment body" value="#Model.commentViewModel.Comment.Body" />
<input type="submit" value="Create" />
</form>
Remember to include all the information regarding the Announcement object as hidden fields, as you cannot post complex objects on forms as you are trying to do. That is an HTML feature/limitation.
For example, simple include the announcement id in the form as:
<input type="hidden" name="Announcement.Id" value="#item.id" />
I'm attempting to, on the click of a button, complete a to-do item, therefore removing it from the list.
I'm using an ActionLink for the button:
#foreach (var item in Model)
{
<li class="#item.Priority">
#item.Text
<div class="agile-detail">
#Html.ActionLink("Done", "Complete", "Home", new { id = item.ToDoId }, new { #class = "pull-right btn btn-xs btn-primary" })
<i class="fa fa-clock-o"></i> #item.Date
</div>
</li>
}
And a very short action for the processing in the controller:
public ActionResult Complete(int todoId)
{
using (var db = new KnightOwlContext())
{
DashboardHelper dashboardHelper = new DashboardHelper(db);
dashboardHelper.CompleteToDo(todoId);
return RedirectToAction("Index", "Home");
}
}
Clicking the button generated a URL of:
http://site/Home/Complete/1
I've looked up a solution and so far it looks like it could be any number of issues. Also the ActionLink button is inside a partial view so I'm not sure if that changes anything in terms of incorrect routing setup? For routing too I'm just using the default config that comes with an MVC project in Visual Studio.
Just having trouble narrowing down the cause of the issue so where to check first?
The parameter in your method is int todoId but you not passing any value for that - your only passing a value for a parameter named id.
Change the method to
public ActionResult Complete(int id)
or change the link to use new { todoId = item.ToDoId }, but that will add a query string value, not a route value, unless you create a specific route definition with url: "Home/Complete/{todoId}"
In my MVC application I have a search page that will display a few data-entry boxes and a "submit" button to execute the search... All this is fine and dandy and works as expected.
However, I need to implement a "deep-link" search mechanism where the data-entry fields are pre-populated from the URI string and then execute the search.
How can I get the Controller's Index method to show the main view and then execute the Search method to fill the <div id="results"> of the view.
Index.cshtml
#model Models.SearchRequest
#using (Ajax.BeginForm("Search", new AjaxOptions() { InsertionMode = InsertionMode.Replace, UpdateTargetId = "results" }))
{
#Html.AntiForgeryToken();
.
.
. Data entry boxes for search
.
.
<div>
<input type="submit" value="Search" />
</div>
<div id="results">
<!-- Results of search go her -->
</div>
}
SearchController.cs
public ActionResult Index([FromUri] Models.SearchRequest request)
{
.. validation of the request
return View(request);
}
public ActionResult Search(Models.SearchRequest request)
{
ViewModels.ResultsVM results = ... stuff that executes the search ...
return PartialView("ResultsPV", results);
}
ResultsPV.cshtml
#model ViewModels.ResultsVM
.
.
. Lots of Razor to display the data
Actually, I've just solved the problem.
-1 In my SearchRequest model, I added
public ViewModels.ResultsVM Result;
-2 Changed the the Index method to add
if (...uri data is valid...)
{
request.Result = ExecuteSearch(request);
}
-3 Changed the Search method to
return PartialView("ResultsPV", ExecuteSearch(request));
-4 Extracted all the code that actually did the searching into a new method
private ViewModel.ResultsVM ExecuteSearch(Models.SearchRequest request)
{
ViewModels.ResultsVM results = /* stuff that executes the search */
return (results);
}
-5. Changed the <div id="results"> to ...
<div id="results">
#if (Model.Result != null)
{
#Html.Partial("ResultsPV", Model.Result)
}
</div>
I am new to mvc, so am still struggling with some concepts.
I have a very simple form and controller.
Controller:
public class InquiryController : BaseController {
public ActionResult Index() {
return View();
}
public ActionResult Search(int Id) {
int newid = Id;
return View();
}
}
and the form:
#using (Html.BeginForm("Index", "Results", FormMethod.Post))
{
#Html.TextBox("id", null, new { type = "Search", autofocus = "true", style = "width: 200px" })
<input type="submit" value="Search" />
}
What is happening is that when I enter a number in the form, I want that number to be passed to the "Search" function in the controller. I do not understand how to get the form to call the controller and do its processing inside that controller. Instead, it goes to an error looking for the "results" page, which i understand comes from the BeginForm method.
So in essence, how can i get my form to pass the number entered into the search box to my controller function?
Specify what action and what controller you want to go to in the first two arguments of Html.BeginForm.
#using (Html.BeginForm("Search", "Inquiry", FormMethod.Post))
{
#Html.TextBox("id", null, new { type = "Search", autofocus = "true", placeholder = "Harvest Tag, 6 Digit Tag No.", style = "width: 200px" })
<input type="submit" value="Search" />
}
Inside the action you can access the Id like you're doing but you could also get it from FormCollection["id"] or Request["id"] as the original string and validate it to avoid getting parsing errors in case the user submits an invalid int.
The first parameter to BeginForm is the action to take, and the second is the controller to execute, so your usage should be:
#using (Html.BeginForm("Search", "Inquiry", FormMethod.Post))
The value that gets passes in is found (in this case) by the HTML ID of the input control (id)
I have this:
<div id="miniShoppingCartContainer">
#Html.Action("MiniShoppingCart", "ShoppingCart")
</div>
where MiniShoppingCart action returns MiniShoppingCart.cshtml partial view with all the content.
In this partial view I added an ajax call for increasing the quantity of product cart:
#using (Ajax.BeginForm("IncreaseProductQuantity", "ShoppingCart", new { shoppingCartItemId = item.Id }, new AjaxOptions { UpdateTargetId = "miniShoppingCartContainer", InsertionMode = InsertionMode.Replace }))
{
<li>
<input type="submit" class="btn-up" />
</li>
}
which calls a method:
public ActionResult IncreaseProductQuantity(int shoppingCartItemId)
{
//get shopping cart item
var cart = _workContext.CurrentCustomer.ShoppingCartItems
.Where(x => x.ShoppingCartType == ShoppingCartType.ShoppingCart).ToList();
var sci = cart.Where(x => x.Id == shoppingCartItemId).FirstOrDefault();
if (sci == null)
{
return RedirectToRoute("ShoppingCart");
}
//update the cart item
_shoppingCartService.UpdateShoppingCartItem(_workContext.CurrentCustomer,
sci.Id, sci.Quantity + 1, true);
return MiniShoppingCart();
}
Please note that at the end of the method I call the MiniShoppingCart ActionResult which prepares the cart and return the PartialView (as you see at the beginning of the post).
Well, the update of a product is happening fine but the content is not refreshed (or replaced)...
Can you please indicate where I am wrong?
UPDATE:
Doing an inspection with Chrome Dev. Tools I see an error when doing post:
POST http://localhost/ShoppingCart/IncreaseProductQuantity?shoppingCartItemId=11 500 (Internal Server Error)
f.support.ajax.f.ajaxTransport.sendjquery-1.7.1.min.js:4
f.extend.ajaxjquery-1.7.1.min.js:4
ejquery.unobtrusive-ajax.min.js:5
(anonymous function)jquery.unobtrusive-ajax.min.js:5
f.event.dispatchjquery-1.7.1.min.js:3
f.event.add.h.handle.ijquery-1.7.1.min.js:3
2
It's strange to guess what is the problem from this log...Basically, If I make debug I can see that it does all the operations until return PartialView(model); of MiniShoppingCart() method...
Issue found:
>The partial view 'IncreaseProductQuantity' was not found or no view engine supports the searched locations.
So basically, doing return MiniShoppingCart(); from IncreaseProductQuantity method doesn't automatically return the MiniShoppingCart partial view but will still try to return IncreaseProductQuantity partial view which of course does not exists.
Consequently, I have done it like:
var model = PrepareMiniShoppingCartModel();
return PartialView("MiniShoppingCart", model);