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>
Related
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
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 have an MVC view where user can set a flag and post data to controller (post because I want to hidden query string)
After the controller have done his job I want to redirect to website home page that is an aspx page (my site is mixed aspx and MVC)
Is there a way to do that?
This is my view
#model MessaggiVM
<form role="form" class="form-inline" method="post" action="Messaggi/VaiAllaHome">
<button id="btnHome">Vai alla pagina iniziale</button>
<div class="form-group">
<label for="nascondi">hiding</label>
<input id="nascondi" type="checkbox" name="nascondi" value="true" />
</div>
<input type="hidden" name="elencoPost" value="#Model.Posts" />
#*#Html.ActionLink("Messaggi", "VaiAllaHome", new { posts = Model.Posts} )*#
</form>
And this the controller
[HttpPost]
public RedirectResult VaiAllaHome(bool? nascondi = false, IEnumerable<Messaggio> elencoPost = null)
{
// do something
return Redirect(Url.Content("~/"));
}
When I run this code controller action is executed without error but redirect is not done and browser remain on the view
Other problem is that elencoPost parameter is empty in the action but I'm investigating it
EDIT
Honestly I'm thinking to post data on input change and switch button for a simply link
EDIT 2:
found the reason: in default.aspx i have a auto-redirect to Message page :(
Try
return Redirect("~/home.aspx");
or
return Redirect(Url.Content("~/home.aspx")
You should be able to use Redirect with a relative url:
[HttpPost]
public RedirectResult VaiAllaHome(bool? nascondi = false, IEnumerable<Messaggio> elencoPost = null)
{
// do something
return Redirect("/home.aspx");
}
Try using a #Url.Content on your form tag
<form action="#Url.Content("~/Messaggi/VaiAllaHome/")">
Then in your Controller
[HttpPost]
public RedirectResult VaiAllaHome(bool? nascondi = false, IEnumerable<Messaggio> elencoPost = null)
{
// do something
return View(Url.Content("~/"));
//return RedirectToAction("Action", "Controller", new { routeParameter = value } /*e.g. "id = 1"*/);
}
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.
}
I am using c# and ASP.NET MVC4 for a web application (with mobile template).
I'm having a problem with my Details view page. (First you select something from Index page and then it goes to Details page) I have put a bing map on the page and the map doesn't load.
First I thought it was something wrong with the map but its not.
I noticed that the url is
http://localhost:2550/Place/Details
of the page. However if I manually put a '1' on the end like so http://localhost:2550/Place/Details/1
then the map loads on the page. I don't understand why this is...
does anyone know why? thanks
my view page for Details:
#model Project.Models.Place
#{ ViewBag.Title = "Details";}
<h2>Place Details</h2>
<fieldset>
<div class="display-label"> Name: #Model.Name</div>
<div class="display-label">Address: #Model.Address</div>
<div class="display-label">Post Code: #Model.PostCode</div>
<div class="display-label"> PhoneNo: #Model.PhoneNo</div>
</fieldset>
<p> #Html.ActionLink("Back to List", "Index")</p>
<body onload="getMap();">
<div id='myMap' style="position:relative; width:400px; height:400px;"></div>
<div>
<input type="button" value="createWalkingRoute" onclick="createDirections();" />
</div>
<div id='directionsItinerary'> </div>
</body>
#section scripts{
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<script type="text/javascript">
var map = null;
var directionsManager;
var directionsErrorEventObj;
var directionsUpdatedEventObj;
function getMap() {
map = new Microsoft.Maps.Map(document.getElementById('myMap'), { credentials: 'mykey' });
}
function createDirectionsManager() {
var displayMessage;
if (!directionsManager) {
directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
displayMessage = 'Directions Module loaded\n';
displayMessage += 'Directions Manager loaded';
}
alert(displayMessage);
directionsManager.resetDirections();
directionsErrorEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsError', function (arg) { alert(arg.message) });
directionsUpdatedEventObj = Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', function () { alert('Directions updated') });
}
function createWalkingRoute() {
if (!directionsManager) { createDirectionsManager(); }
directionsManager.resetDirections();
// Set Route Mode to walking
directionsManager.setRequestOptions({ routeMode: Microsoft.Maps.Directions.RouteMode.walking });
var seattleWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: 'Seattle, WA' });
directionsManager.addWaypoint(seattleWaypoint);
var redmondWaypoint = new Microsoft.Maps.Directions.Waypoint({ address: 'Redmond, WA', location: new Microsoft.Maps.Location(47.678561, -122.130993) });
directionsManager.addWaypoint(redmondWaypoint);
// Set the element in which the itinerary will be rendered
directionsManager.setRenderOptions({ itineraryContainer: document.getElementById('directionsItinerary') });
alert('Calculating directions...');
directionsManager.calculateDirections();
}
function createDirections() {
if (!directionsManager) {
Microsoft.Maps.loadModule('Microsoft.Maps.Directions', { callback: createWalkingRoute });
}
else {
createWalkingRoute();
}
}
</script>
}
my controller action for Details:
public ViewResult Details(int id)
{
ViewBag.events = eventRepository.PlaceEvents(id);
return View(placeRepository.Find(id));
}
Possible cause, may be you haven't written Controller default controller with Zero arguments.
Or you haven't written controller with [HttpPost] attribute
Will be easy if you put code for the controller here.
If you say that the navigation with /1 at the end works but your current url is without the number, your url on the index page is wrong.
Your url is now something like
#Html.ActionLink("Details", "Place")
Change it to something like this:
#Html.ActionLink("Details", "Place", new { id = #Model.Id })
So the problem is that your id isn't given to your details action.