I am learning C# using DotNetFiddle to code.
I have a web with a bunch of radio buttons and when I click them I want to update the view, for example: delete one button.
Also I need to do the delete logic in the backend and from the view I have to call a POST method to pass data.
So I'm trying to call the POST method with ajax and return RedirectToAction to display the new view, but somehow is not working.
You can check and test my current code here: dotnetfiddle
Controller
using System;
using System.Web.Mvc;
using System.Collections.Generic;
namespace HelloWorldMvcApp
{
public class HomeController : Controller
{
public SampleViewModel sampleViewModel = new SampleViewModel();
[HttpGet]
public ActionResult Index()
{
return View(sampleViewModel);
}
[HttpPost]
public ActionResult UpdateList(string id)
{
sampleViewModel.deleteElement();
return RedirectToAction("Index");
}
}
}
Model
using System;
using System.ComponentModel.DataAnnotations;
namespace HelloWorldMvcApp
{
public class SampleViewModel
{
public List<string> list = new List<string> { "a", "b", "c", "d", "e"} ;
}
}
View
#model HelloWorldMvcApp.SampleViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<!-- template from http://getbootstrap.com/getting-started -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 101 Template</title>
<!-- CSS Includes -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
</head>
<body>
<br/>
<div class="container">
<br></br>
<ul>
#foreach (var c in Model.arr)
{
<li>
<input type="radio" name="option" id="#c">#c
</li>
}
</ul>
</div>
<!-- JS includes -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/4.0/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript">
$('input[type=radio]').click(function() {
var elemId = $(this).attr("id");
$.ajax({
url: '#Url.RouteUrl(new{ action="UpdateList", controller="Home"})',
data: { id : elemId },
type: "POST"
});
});
</script>
</body>
</html>
UPDATE
I've edited your Fiddle into this: https://dotnetfiddle.net/zQq3aR
It does exactly what I describe below in the original answer, but uses the main view. Note that this is NOT what I would recommmend... we're rendering the entire view again, and then replacing the entire page contents with the new content, but I'm not too familiar with DotNetFiddle and don't know how to add more views (can you?).
C#:
[HttpPost]
public ActionResult PostToMe(string id)
{
// TODO: Do some stuff here, then return the updated view
return ButRenderMe(id);
}
[HttpPost]
public ActionResult ButRenderMe(string id)
{
// Remove some content from the model
sampleViewModel.arr = new [] { "a", "b", "c", "all of the above" };
return View(sampleViewModel);
}
JavaScript:
$.ajax({
url: '#Url.RouteUrl(new{ action="PostToMe", controller="Home"})',
data: { id : elemId },
type: "POST",
success: function(data) {
document.open();
document.write(data);
document.close();
}
});
The better way to do this in a normal MVC project would be to have a PartialView used for rendering the content you want to replace... in this case the list. In your "update" action you would update your data, and then re-render just that PartialView and return it. Then replace just that section with the new content.
With the code above, from my Fiddle, we may as well be making a normal page post instead of an AJAX call.
There are a lot of factors here, but hopefully this helps.
ORIGINAL ANSWER
So this is a bit confusing, given that your question doesn't match your code in either the DotNetFiddle or the question itself, but let's see if this helps.
I'm pretty sure that returning a RedirectToAction Result doesn't return the content from the other action your redirecting to. I think in this case, since it's an AJAX request, MVC knows that this isn't the proper usage, and returns a 200 OK status with the following example content. So with this Controller Action:
[HttpPost]
public ActionResult RedirectMe()
{
return RedirectToAction("UpdateList");
}
We get this Response:
<html><head><title>Object moved</title></head><body>
<h2>Object moved to here.</h2>
</body></html>
So again, I don't think that will work the way you want.
I think what you want is to Render the other action, and return that content back. And that should be as simple as calling that other action:
[HttpPost]
public ActionResult PostToMe(string id)
{
return ButRenderMe(id);
}
[HttpPost]
public ActionResult ButRenderMe(string id)
{
return Content("Test");
}
That returns the content "test". I think that you should be able to render a normal view in that other action if you want.
I looked at your DotNetFiddle as well...
In your DotNetFiddle your JavaScript is calling into UpdateList which doesn't seem to exist... not sure why it would even return a 200 OK... The Actions you have are Index and UpdateFlights. If you open the F12 Developer Tools in Chrome you should see that the POST returns no Content.
So I added a Controller Action called UpdateList:
[HttpPost]
public ActionResult UpdateList(string id)
{
sampleViewModel.deleteElement();
return Content("Test");
}
And I can see that "test" is returned as the Response content in the F12 Tools.
Related
I am trying to make a URL shortener using TinyUrl. The problem is that I get the following error:
System.Net.WebException: 'Error on the remote server: (400) Incorrect request.
I have already tried reading in almost the entire site but I have not found the solution.
The controller code is:
namespace Prueba.Controllers
{
[HandleError]
public class TinyURLAPIController : Controller
{
// GET: TinyURLAPI
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View("Index");
}
[HttpPost]
public ActionResult MakeTinyUrl(string strURL)
{
var tinyUrl = WebRequest.Create("http: / tinyurl . com/api-create.php?url=" + strURL);
var shortUrl = tinyUrl.GetResponse();
using (var reader = new StreamReader(shortUrl.GetResponseStream())){
ViewData["tinyUrl"] = reader.ReadToEnd();
}
return PartialView("Index");
}
}
}
And this is the index code:
<Html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<#Ajax.BeginForm("MakeTinyUrl", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "tinyUrl" }))>
#Html.TextBox("url")
<input id="btnMakeUrl" type="submit" value="Make tinyUrl" onclick="Index" />
</body>
</Html>
I need to enter the long URL in a textbox and pressing the button shows the short URL in a label
Can't comment so i'll just write it as an answer!
Your problem isn't the api request.
Your strURL is probably null as it was for me with your code.
Try making the following change in your view:
from
#Html.TextBox("url")
to
#Html.TextBox("strURL")
and also your html isn't quite valid where you defined the form.
try:
#using (Ajax.BeginForm("MakeTinyUrl", new AjaxOptions { HttpMethod = "Post", UpdateTargetId = "tinyUrl" }))
{
#Html.TextBox("strURL")
<input id="btnMakeUrl" type="submit" value="Make tinyUrl" onclick="Index" />
}
It' possible you may have to UrlEncode the value before passing it to the view:
https://learn.microsoft.com/en-us/dotnet/api/system.web.httputility.urlencode?view=netframework-4.8
I understand you've altered the api link in your post due to restrictions.
firstly i'd manually call the api in your preferred browser using the URL you're trying to shorten...make sure that is ok.
e.g. [redacted]tinyurl.com/api-create.php?url=http://www.yoururltest.com
Next, run a test with a breakpoint, stepping through your code to see exactly where the error happens and view the data your controller receives and uses in the request.
I bet you'll find an issue with that somewhere along the way
This is the way I was sending my model to an angular controller scope.
c# Controller:
public class AreaMenuController : RootController
{
// GET: Menu
public PartialViewResult Index()
{
var mod = Modules.Instance.ModuleList.FirstOrDefault(m => m.Prefix.Equals(base.GetArea(), StringComparison.CurrentCultureIgnoreCase));
return PartialView("_AreaMenu", mod.ModuleMenus);
}
}
View cshtml:
#using Nuclei.Models
#model IEnumerable<Nuclei.Models.Menu>
<script type="text/javascript">
#{ var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); }
window.areaMenus = #Html.Raw(serializer.Serialize(Model));
</script>
<script type="text/javascript" src="#Url.Content("~/Scripts/AngularControllers/_AreaMenuController.js")"></script>
<div ng-controller="AreaMenuController as vm" ng-init="vm.initializeController()">
<div id="accordion" ng-class="accordian-menu" style="visibility: visible;">
<ul>
<li ng-repeat="menu in vm.areaMenus">
...
</li>
</ul>
</div>
</div>
Angular Js file:
var NucleiApp = angular.module('NucleiApp');
NucleiApp.controller('AreaMenuController', ['$scope', '$http', '$location', function ($scope, $http, $location) {
"use strict";
var vm = this;
vm.initializeController = function () {
vm.areaMenus = window.areaMenus;
}
}]);
Question 1: Is there a smoother way to send your c# model through to angular other than through global window object?
You can use an $http get from angular, however because this is processed client-side there is always a bit of lag before it gets displayed, because it needs to call the c# controller and get the data. So I'm reserving $http get for updates only.
The other other way I was thinking was to send the view a Json object straight off:
c# controller:
public class AreaMenusController : RootController
{
// GET: Menu
public PartialViewResult Index()
{
return PartialView("_AreaMenu", GetAreaMenus());
}
public JsonResult GetAreaMenus()
{
var mod = Modules.Instance.ModuleList.FirstOrDefault(m => m.Prefix.Equals(base.GetArea(), StringComparison.CurrentCultureIgnoreCase));
return Json(new { areaMenus = mod.ModuleMenus }, JsonRequestBehavior.AllowGet);
}
}
View cshtml:
#using System.Web.Mvc
#model JsonResult
<script type="text/javascript">
window.areaMenus = #Model;
</script>
Question 2: I'm not really sure how to initialize the #model at this point and send it through to the angular file and again, if there is a better option than javascripts global window object... open to suggestions!
We currently do this to bootstrap a set of data that is later updated by a call into a WebAPI.
The reason we do this is we have found cases where the data, when bootstrapped via an API call, was coming back too slowly, which gave a bad experience to our users.
In our razor view:
<html>
<head>
<script>
window.areaMenus = '#Html.Raw(Model.SerializedJsonString)';
</script>
</head>
</html>
Then when our angular app is Run(), we deserialize the data, and use it from there:
var app = angular.module('myApp', ["stuff"])
.run(["menuService", (menuService) => {
// deserialize the json string into my object
var areaMenus = angular.fromJson(window.areaMenus);
// do something with it
menuService.Load(areaMenus);
}]);
This'll get the data available to angular immediately without having to wait for a $http request to complete, which should address your issue.
[EDITED] my previous question closed as duplicate, but I don't think so and I write why below. If I'm wrong, please leave a couple words of explanations if you decide to do it again. I'm newbie in dev, but I read a bunch of SO questions about topic which already been asked and still don't find answer
My goal is to create a form with input fields that can be added from View by clicking on button. I don't know how many fields a user want to add and also I need that form will be bound to List < MyModel > and when user send it'll return to action List < MyModel >.
I created sample project to show you my solution which doesn't work. Can you help me with it please?
MyModel
public class MyDogModel
{
public string Name { get; set; }
public string Breed { get; set; }
}
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
Session.Clear();
return View();
}
[HttpPost]
public ViewResult MyDogs (List<MyDogModel> myDogs)
{
return View(myDogs);
}
[HttpPost]
public PartialViewResult AddDog(List<MyDogModel> dogs)
{
int fieldsCount;
if (Session["FieldsCount"] == null)
{
Session["FieldsCount"] = 1;
fieldsCount = 1;
}
else
{
fieldsCount = (int)Session["FieldsCount"];
Session["FieldsCount"] = fieldsCount++;
}
return PartialView(dogs);
}
}
Index
#{
AjaxOptions options = new AjaxOptions
{
HttpMethod = "post",
UpdateTargetId = "MyDogsForm",
InsertionMode = InsertionMode.Replace
};
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script src="~/Scripts/jquery-1.8.0.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
</head>
<body>
<div>
#Ajax.ActionLink("Add new dog", "AddDog", options)
<div if="MyDogsForm">
</div>
</div>
</body>
</html>
Here in AddDog View I'm getting null reference error in this string
#Html.TextBoxFor(d => d[i].Breed); Exception thrown: 'System.NullReferenceException' in System.Web.Mvc.dll
#model List<WebApplication2.Models.MyDogModel>
#using (#Html.BeginForm("MyDogs","Home"))
{
for (int i = 0; i < (int)Session["FieldsCount"]; i++)
{
#Html.TextBoxFor(d => d[i].Name);
#Html.TextBoxFor(d => d[i].Breed);
}
<button type="submit">Send</button>
}
What does it mean? I just want to fill my List with number of dogs which user'll want to add.
EDIT Thanks #stephen-muecke for suggested solution. Maybe I'm missing something. But actually I'm looking for solution, where form will created and fulfilled with some data at the server. In this answer POST a form array without successful new fields added by JQuery, I'll try it if server-side form changes via ajax not possible, but it'll be more convenient for me to avoid JQuery cause I need to bind not just TextBoxes, but DropDownLists and so on with information which generated on server from database.
Here is my JQGrid Code:
click: function (e) {
debugger;
var id = $(e.target).closest("tr.jqgrow").attr("id");
rowdata = jQuery("#EmpTable").getRowData(id);
Data = { Id: rowdata.Id, Name: rowdata.Name, Designation: rowdata.Designation };
var url = 'http://localhost:50428/Script/Edit/';
return $.post(url, Data);
}
here is my controller code where the data is collecting
[HttpPost]
public ActionResult Edit(FormCollection form)
{
gridmodel properties = new gridmodel();
properties.Id = Convert.ToInt32(form["id"]);
properties.Name = form["Name"];
properties.Designation = form["Designation"];
ViewBag.id = properties.Id;
ViewBag.name = properties.Name;
ViewBag.designation = properties.Designation;
return View();
}
Now here is my View code
the data that is passing from the controller to the view
#model MVC5_JQGrid.Models.gridmodel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Edit</title>
</head>
<body>
<div>
Id:#ViewBag.id
<br />
Name:#ViewBag.name
<br />
Designation:#ViewBag.designation
</div>
</body>
</html>
But i am unable to load this page but in network ---> Response body i can see that these values are assigned
Update
Hello Venkata thanks for the answer i will keep the points which you mentioned and +1 from my side for the observation ,coming to the problem. it was resolved from oleg suggestion:(Here is the answer given by Oleg) The reason of your problem is the usage of $.post(url, Data); which just send data with respect of $.ajax({url: "/Script/Edit", data: Data, type: "POST"});. You need to do $.submit instead. If you would use HTTP GET then you can just assign new URL which includes parameters to location.href (something like location.href = "/Script/Edit?" + $.param(Data)). In case of HTTP POST one need to build with elements which contains (or just have on the page hidden form with all required elements with required name attributes) and use $.submit.
Thanks for the help from oleg and Venkata
Few Changes required in your code:
Observation and Notes:
Before fixing, we should know few points here regarding architecture, control-flow and standards
jQuery AJAX requests get response to its success callback handler(in your code you've missed success call)
In success function We should build or append or set HTML to target placeholder tag in existing page (let us say you have div for Edit section like <div id='editSection'>...</div> in your jqGrid page. you should bind the responseView to editSection div)
When we send AJAX request: In Controller.Action Instead of return View(); we should have return PartialView();
In your view.cshtml give preference to bind elements with Model rather than ViewData. You can get model to view if you do return PartialView(model); in the Action.
Try to reduce usage of ViewData or ViewBag in view.cshtml. Also business logic not recommended in Views.
Try to Follow best practices: Capitalization Conventions (in your code change gridmodel class name to GridModel)
By default routing url template will have {controller}/{action} (from your url Script is controller and Edit is action)
Make sure that you're passing correct Controller and Action names (Is Edit action located in the Controller with name ScriptController?)
Changes in JavaScript JQGrid Code:
url = '/Script/Edit/';
return $.post(url, Data).success(function(response){
//response datatype can be JSON or XML or HTML or text (In your case HTML Edit.cshtml View)
//update you target html tag with response view.
$('#editSection').html(responseView);
});
Changes in Controller:
[HttpPost]
public ActionResult Edit(FormCollection form)
{
var properties = new gridmodel();
properties.Id = Convert.ToInt32(form["id"]);
properties.Name = form["Name"];
properties.Designation = form["Designation"];
ViewBag.id = properties.Id;
ViewBag.name = properties.Name;
ViewBag.designation = properties.Designation;
return PartialView();
}
You can change above Edit Action like below if action require input parameters only id, name & designation
[HttpPost]
public ActionResult Edit(int id, string name, string designation)
{
var gridModel = new GridModel();
gridModel.Id = id;
gridModel.Name = name;
gridModel.Designation = designation;
ViewBag.id = gridModel.Id;
ViewBag.name = gridModel.Name;
ViewBag.designation = gridModel.Designation;
return PartialView(gridModel);
}
Hello Venkata thanks for the answer i will keep the points which you mentioned and +1 from my side for the observation ,coming to the problem. it was resolved from oleg suggestion:(Here is the answer given by Oleg) The reason of your problem is the usage of $.post(url, Data); which just send data with respect of $.ajax({url: "/Script/Edit", data: Data, type: "POST"});. You need to do $.submit instead. If you would use HTTP GET then you can just assign new URL which includes parameters to location.href (something like location.href = "/Script/Edit?" + $.param(Data)). In case of HTTP POST one need to build with elements which contains (or just have on the page hidden form with all required elements with required name attributes) and use $.submit. Thanks for the help from oleg and Venkata
C# asp.net MVC project: I have my index page with a button in it, I want to press it and update the same page with some results.
Here's some code:
The View: (with a button that calls the getConfig method in the controller)
#{
ViewBag.Title = "Home Page";
}
<form method="get" action="/Home/GetConfig/" >
<input type="submit" value="Get Config WS" />
</form>
<p>
#ViewBag.totalRecords
</p>
The controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Test webservices";
return View();
}
public void getConfig()
{
string totalRecords = string.Empty;
wsConfig.config_pttClient client = new wsConfig.config_pttClient();
wsConfig.getConfigInput gci = new wsConfig.getConfigInput();
wsConfig.getConfigOutput gco = new wsConfig.getConfigOutput();
gco = client.getConfig(gci);
totalRecords = gco.result.totalRecords.ToString();
ViewBag.totalRecords = totalRecords;
}
I want to press the view's button and show the totalRecords on the same page.
How can I achieve this?
Edit: There might be other solutions, (if you don't mind updating your entire page) but this how I generally do it.
Ok, there are a couple of things that you need to change in order to make it work:
Create a new partial view that contains just the part that you would like to update (and wrap it an element with an id). In this example, let's call it 'Partial_TotalCount'.
This partial view will contain the following code:
<div id="updated">
<p>
#ViewBag.totalRecords
</p>
</div>
Now, change your original view so that it includes the partial view:
#{
ViewBag.Title = "Home Page";
}
<form method="get" action="/Home/GetConfig/" >
<input type="submit" value="Get Config WS" />
</form>
#Html.Partial("Partial_TotalCount", #Model)
Now, update your controller to work with an ajax request. This would make your controller looks like:
public ActionResult Index()
{
ViewBag.Message = "Test webservices";
if (Request.IsAjaxRequest())
{
getconfig();
return PartialView("Partial_TotalCount");
}
return View();
}
Now, you need to be able to submit the page when you click the button. This can be done through javascript:
First your javascript function that will update the contents:
<script type="text/javascript">
function Refresh() {
$.ajax({
url: '/Home/Index',
type: "GET",
dataType: "html",
success: function(data) {
$("#updated").html(data);
},
error: function() { alert('Refreshing error.'); }
});
}
</script>
You just need to add an onclick on your button. And you can remove the form tags from around your form aswell.
Edit: As requested by the questioner, I provide a bit of explanation on the Javascript function itself:
$.ajax means that we are doing an Ajax request. It means that we are doing some asynchronous requests with the server.
Then a couple of parameters are passed:
Url: The url that should be executed. In your example, the code behind the url "Home/GetConfig" get's executed.
Type: The type of submit that you want to do (POST, GET, ...)
dataType: The type we are expecting back from the server.
Success: The piece of javascript that needs to execute when complete. (In this case, update the DIV element with the id "WithCss" with the contents that are received with the url "Home/Getconfig".
Error: A function that is executed when the request failed for some reason.
There are a lot of other parameters you can pass (for example if you need to pass an id, and others.
For more explanation, please look at the original documentation.
Also, consider marking this answer as accepted.
I hope it works.
Try This:
Replace your input button code with the following code :
<input type="submit" id="btnSave" name="BtnSave" value="Get Config WS" />
Then in controller change the whole code for this code:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Test webservices";
return View();
}
public ActionResult getConfig()
{
return View();
}
[HttpPost]
public ActionResult getConfig(FormCollection Form)
{
if(Form["BtnSave"]!=null)
{
string totalRecords = string.Empty;
wsConfig.config_pttClient client = new wsConfig.config_pttClient();
wsConfig.getConfigInput gci = new wsConfig.getConfigInput();
wsConfig.getConfigOutput gco = new wsConfig.getConfigOutput();
gco = client.getConfig(gci);
totalRecords = gco.result.totalRecords.ToString();
ViewBag.totalRecords = totalRecords;
}
return View();
}
Hopefully it works...!