I'm a noob in .Net and all the web developpement :s
I'm having an issue using html.BeginForm and html.ActionLink.
I got this in my homeWeb.cshtml:
#using (Html.BeginForm("resultWeb", "Result", new { val = 1 }, FormMethod.Post ))
{
<div class="main-block">
<input style="width:100%;" type="text" name="searchValue" /><br />
<div style="text-align: center;">
<input type="submit" value="Submit" />
</div>
</div>
}
its calling my result controller and my resultWeb view sending the val = 1 as parameter
here is my ResultController.cs:
[HttpPost]
public ActionResult resultWeb(int val, FormCollection collection)
{
List<WebSite> list = new List<WebSite>();
// Doing stuff with my list and the val
return View(list);
}
this part is working and well sending the parameter to my view.
The problem is when i try to do the same thing with an html.ActionLink on an other page
resultWeb.cshtml:
<tr>
#for (int i = 0; i <= Model.Count / 15; i++)
{
int j = i + 1;
<td>#Html.ActionLink(#j.ToString(), "resultWeb", new { val = j })</td>
}
</tr>
And when i click on one of the links, it doesn't work i got this error:
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Result/resultWeb/1
I guess i'm doing something wrong but i don't understand what. Does someone can help me on this ?
Thanks !
Actionlinks can't post a form/data to a controller. All they do is create <a> tags.
If you want to submit the form with an actionlink, you could use the #Ajax.ActionLinkhelper, or just post the form with jquery alltogether.
Also, this question has been asked lots of times before on stackoverflow, like here or here.
Thousands Answer is correct you cannot Post data via ActionLinks. If your FormsCollection is not too large then you can use Query Strings.
This is what I have done
Controller:
public ActionResult Index(string loc, string ma, string mo, int co = 0, int mi = 0)
{
search c = new search() { loc = loc, ma = ma, co = co, mo = mo, mi = mi }
/*replace search() and query string variables with your FormsCollection*/
/*Do thing here*/
return View(DisplayModel)
}
MyModels
public class DisplayModel
{
public search Search { get; set; }
public List<Website> Result { get; set; }
}
public class Search
{... All my search variables in this model}
And finally the View
#model MyApp.Models.DisplayModel
<div>
#using (Html.BeginForm("Index", "Buying", FormMethod.Get)){
<fieldset>
<legend>My form</legend>
<input id="ma" name="ma" type="hidden" disabled="disabled" value="#Model.Search.ma" />
... The way you choose to display your your view. You can either keep the same form hidden or
<input type="submit" value="mybutton"/>>
</fieldset></div>
#foreach( var item in Model.Result)
{
... The way you choose to display your List.
}
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 very new to using Razor with C# and am conducting this project in order to try to better my understanding of it.
This application is supposed to, once completed, is ask the user to input three integers and then print out the sum of those integers. Right now, I have the basic frame of the View and Controller set up. (There is currently no Model.) The controller is set up to use an HTTP-Post protocol in order to send information to the HTML form.
What I'm struggling with, is the code needed to communicate the data directly to the form, as well as whatever parameters are needed so that ASP.net will ignore the presence of two identically-named controller actions (which I'm told it should be able to do once the Razor syntax is set up properly).
Any guidance here would be very helpful. (Note: It is a requirement that I use the HTTP Post protocol in the finished solution.)
Controller:
public ActionResult Index(int firstInt = 0, int secondInt = 0, int thirdInt = 0)
{
return View();
}
[HttpPost]
public ActionResult Index(int firstInt = 0, int secondInt = 0, int thirdInt = 0)
{
int sum = firstInt + secondInt + thirdInt;
ViewBag.result = sum;
}
Index View:
<form action="" method="post">
<table>
<tr><td>Enter the 1st Number: <input id="firstInt" name="firstInt" type="text" value="0" /></td></tr>
<tr><td>Enter the 2nd Number: <input id="secondInt" name="secondInt" type="text" value="0" /></td></tr>
<tr><td>Enter the 3rd Number: <input id="thirdInt" name="thirdInt" type="text" value="0" /></td></tr>
<tr>
<td><input id="Submit" type="submit" value="submit" /><input id="Reset" type="reset" value="reset" /></td>
</tr>
<tr>
<td>Sum = #ViewBag.result</td>
</tr>
</table>
</form>
You need to make sure your methods with same name has unique signature. Since you mentioned the user enter the numbers via the form, there is no reason to have those params in the GET action method. Just remove it and it should work now.
public ActionResult Index()
{
return View();
}
Also you need a return View(); statement in your HttpPost action. Otherwise you will get a compilation error.
[HttpPost]
public ActionResult Index(int firstInt = 0, int secondInt = 0, int thirdInt = 0)
{
int sum = firstInt + secondInt + thirdInt;
ViewBag.result = sum;
return View();
}
If you are going to have many parameter values coming from the form, i would advise creating a view model which has those properties and use that as the parameter.
Normally what you do for Razor views is declare the Model, and use the Razor methods to output the form and form elements. As Shyju says, you need unique signatures on the actions.
#using MyModelNamespace
#model MyModel
#using (Html.BeginForm("Index", "HomeController", routevalues, etc..) {
Html.TextBoxFor(m => m.firstInt);
}
Your post action should take the model as a parameter
public ActionResult Index() {
return View();
}
[HttpPost]
public ActionResult Index(MyModel m) {
int sum = m.firstInt + m.secondInt + m.thirdInt;
ViewBag.result = sum;
return View(m);
}
You can set ViewBag state in your action.
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 using HTML ActionLink in MVC4. I need to remove QueryStrings using formCollection. How to Use it?
Here is my Code:
<div class="mainNav">
<nav>
<ul>
#for (int i = 0; i < Query.Count - 1; i++)
{
<li>#Html.ActionLink(Query[i].Parent_Menu,"ManageContent","Home", new { CmsFid = Query[i].CMSFunction_Id,ParentMenu=Query[i].Parent_Menu },new { #style = "color:#FFF;text-decoration:none" })</li>
}
#for(int i=Query.Count-1;i<Query.Count;i++)
{
<li>#Html.ActionLink(Query[i].Parent_Menu,"ManageContent","Home", new { CmsFid = Query[i].CMSFunction_Id,ParentMenu=Query[i].Parent_Menu },new {#class="last", #style = "color:#FFF;text-decoration:none" })</li>
}
</ul>
</nav>
</div>
I dont think that you will be able to get the formCollection elements in your action (using Html.ActionLink). Also if you are able you may need to parse them or type cast them for your properties. so I recomend you to have an action method like
public ActionResult ManageContent(int CmsFid,string ParentMenu)
{
}
well you can use formCollection for form post where you may have many data
if its a form post you can use like this
public ActionResult YourPostMethod(FormCollection formData)
{
int CmsFid=int.Parse(formData["CmsFid"]);
}
I want to load the same view again by passing updated parameter from text input on the link click. I tried to use something like <a href="#Url.Action("Index", "Home", new {id = "txtCount".value }). Not sure if there is even a way to do this. I know I could use partial but I want to reload the whole page with updated parameter. Thanks in advance for any help.
Controller
[HttpGet]
public ActionResult Index(int id)
{
return View(id);
}
View
#model int
#using (#Html.BeginForm())
{
<input id="txtCount" value="1" />
Update
for (int i = 0; i < Model; i++)
{
<div>#i </div>
}
}
Maybe something like this
Go!
and binding with jquery
$("#mylink").click(function(){
document.location.href = '#Url.Content("~/Controller/Action")' + $("#mytxt").val();
return false;
});
Obviously with the proper validations if the textbox is empty and all that stuff.
You cannot add the id value to the #Url.Action because it is processed before on the server side, before the page is rendered