Rendering a JsonResult to a string in Razor - c#

I have a server-generated object that I need to convert to a JSON object for JavaScript to consume. I prefer to render this JSON object directly into a JS variable when the view renders to prevent an additional HTTP request.
This is in my controller:
public virtual JsonResult GetTheThings()
{
return Json(new
{
foo = "hello world",
bar = 3,
}, JsonRequestBehavior.AllowGet);
}
I can access this directly at http://localhost:32243/MyController/GetTheThings and I get the following rendered in my browser.
{"foo":"hello world", "bar":3}. Perfect!
So, now I basically just want to render the result of this view into a string. How do I do this? What I have below does not work, but hopefully it give you the idea.
This is my attempt
<script>
var myObj = #Html.RenderPartial(MVC.MyController.GetTheThings());
</script>
Note that I am also using T4 Templates.
In the end, this is what I want to be rendered in the view.
<script>
var myObj = {"foo":"hello world", "bar":3};
</script>

Since you only want the object to be rendered when the view is rendered (and not from an AJAX call), your best bet is probably to make it part of your model. Create a string property and save the JSON as a string, then use JSON.parse in your View to convert it to an object.
Example:
<script>
var myObj = JSON.parse("#Html.Raw(Model.JsonString)");
</script>
Much cleaner that way, and there really isn't any good reason to have your controller doing this since you aren't requesting the object via AJAX.

You could create an HtmlHelper extension like so and instead of using a JsonResult, use a strongly-typed view (assuming using JSON.Net):
public static class HtmlHelperEx
{
public static string ToJson(this HtmlHelper html, object obj)
{
return JsonConvert.SerializeObject(obj);
}
}
Controller Action:
public ActionResult Index()
{
return View(new ModelToSerialize());
}
Then use it in your view like:
#model ModelToSerialize
<script type="text/javascript">
var myObj = #Html.ToJson(Model)
</script>

You would theoritically be able to do it like this:
<script>
var myObj = json.parse('#Html.RenderPartial(MVC.MyController.GetTheThings());');
</script>
Although, GetTheThings() will not actually fire here, it's just a T4MVC placeholder. You should probably store your value into a view model, and get it into javascript like below. If you want to make a separate call to a controller to get this value, it will need to be an ajax call.
public class MyController
{
public ActionResult MyView(){
var vm = new MyViewModel();
vm.MyObjectAsJsonString = GetMyJsonString();
return View(vm);
}
public string GetMyJsonString(){
return ""; //get your json
}
}
public class MyViewModel
{
public string MyObjectAsJsonString{ get; set; }
}
#model MyViewModel
<script>
var myObj = json.parse('#Model.MyObjectAsJsonString');
</script>
To do it via ajax:
<script>
$.ajax({
url: '#Url.Action(MVC.MyController.GetTheThings())',
type: 'get',
}).done(function (result){
var myObj = result;
});
</script>

<script>
var myObj = '#Html.RenderPartial("Getthethings","controller name");'
</script>

Use the #Html.Raw(...) wrapper.
In my own project, I do refer to the existing model coming down in from the controller itself, but there's no reason you can't get the same effect as a partial...it's just not really the core purpose of using async calls...which you should be using instead of relying on the html rendering engine to do your 'dirty' work.

Related

Update a partial view with a string from another partial view on submit?

I am new to MVC and Web Api, and getting stuck.
Please Note: I am using ApiControllers, not Controllers!
Situation
I have two partial views. The first has drop down lists and a submit button. The second is just a confirmation view showing what the user selected after user submits. On clicking submit, I want to send a string (containing the user's combined selections) to the second partial view and update it.
Question
When I press submit on the first partial view, I want to send a string to the second partial view and update the second partial view. How can I do this? The sending of the string can be indirect, of course, like view to controller then back to view. I just want to know at least one way.
Again, note that I am using Web Api Controllers, NOT Controllers. Most people use normal Controllers, which doesn't apply to me. They are different.
Things I tried / What I believe I cannot do from other StackOverflow answers:
I cannot use a Controller method to return a Partial View, like in this very similarly asked question Same question but not with ApiControllers.
I cannot store any data into a class, so I cannot strongly type a view with #model ExampleClass to access stored contents with #Model.ExampleProperty.
Since I cannot store data into a class or return a model from a controller method like View(model), I also could not use Html.DropDownListFor(model => model.SelectedValue) to store the SelectedValue into my model and access it in the other partial view. Instead to populate my drop down lists, I manually added a div, called a GET method that returned a list, and populated the lists by appending to them.
So, what exactly CAN I do? I also tried ViewData, but the stored data would not last after a Controller method finished. I am out of ideas. I hope I am mistaken about something and missed a method, because it feels it should not be this hard. If you could even briefly describe the flow of how I would transfer a string and/or update the second partial view after pressing submit, that would be greatly appreciated!
Thanks so much in advance!
Model class
public class Vehicle
{
public class Make
{
public int MakeId { get; set; }
public string MakeName { get; set; }
}
public class Model
{
public int ModelId { get; set; }
public string ModelName { get; set; }
}
}
ApiController class. The ViewData seems to not save its contents. I wanted to store the user's selections into ViewData with the first POST method, and GET the selections with the second method, but the contents of ViewData have become null already.
public class VehicleController : ApiController
{
public ViewDataDictionary ViewData = new ViewDataDictionary();
[HttpPost]
public void StoreMakeAndModel(string user_selections){
ViewData["message"] = user_selections;
}
[HttpGet]
public string ConfirmationMessage(){
ViewData["message"] = "You selected the " + ViewData["message"].ToString();
return ViewData["message"].ToString();
}
//This returns a list to populate a drop down list
[HttpGet]
public IEnumerable<Vehicle.Make> Makes(){
//This function reads from xml and returns List<Vehicle.Make>
...
return makesList;
}
//This returns a list to populate a drop down list
[HttpGet]
public IEnumerable<Vehicle.Model> Models(int id){
//This function reads from xml the car models that match the make id
...
return modelsList;
}
}
First partial view It has 2 drop down lists and submit button. There is a hidden div that shows once the form submits that renders the second partial view. I hoped that it would load only when it is shown so that I can use $(document).ready(function()) to display my string there, but it loads when the main page loads even though it's hidden. So I don't know how to update it with the string after the first partial view submits.
<select required id="makes_DDL" name="makes_DDL"></select>
<select required id="models_DDL" name="models_DDL"></select>
<input type="submit" id="submit_button" value="Submit Form" onclick="formSubmit()" />
<div id="thank_you_div" style="display:none;">
#Html.Partial("_ThankYou");
</div>
function formSubmit() {
//Combines the make and model into one string, Ex: Chevrolet Malibu
var parameter = $("#makes_DDL").children(":selected").text() + " "
+ $("#models_DDL").children(":selected").text();
var uri = "api/vehicle/storemakeandmodel/" + parameter;
$.ajax({
url: uri,
type: "post",
updatetargetid: "thank_you_div",
success: function (data) {
$("#thank_you_div").show();
},
});
}
My list population looks like this; this is my first list. I could not get #Html.DropDownListFor to work because I could not store anything since I cannot return something like View(model) from a controller method.
$(document).ready(function () {
$.getJSON("api/vehicle/makes", function (makes) {
$("#makes_DDL").append("<option value= ''>Select a make</option>");
$.each(makes, function (id, make) {
$("#makes_DDL").append("<option value=" + make.MakeId + ">" + make.MakeName + "</option>");
});
});
});
Second partial view It tries to get the stored message in ViewData, but it is already gone and GETS null.
<div id="thank_you_div">Thank you!</div>
<script type="text/javascript">
$(document).ready(function () {
alert("Thank You Partial View ready!");
$.ajax({
url: "api/vehicle/confirmationmessage",
type: "get",
updatetargetid: "thank_you_div",
success: function (message) {
$("#thank_you_div").html(message)
}
});
})
</script>
Take a look at your ApiController action methods.
[HttpPost]
public void StoreMakeAndModel(string user_selections)
{
ViewData["message"] = user_selections;
}
[HttpGet]
public string ConfirmationMessage()
{
ViewData["message"] = "You selected the " + ViewData["message"].ToString();
return ViewData["message"].ToString();
}
Looks like you are trying to store data to ViewData when the first method is called and read it in the second method when you make your second ajax call. But that will not work! Because http is stateless. Your second call has no idea what your first call did. You cannot store data in ViewData to access between multiple http requests. It does not make sense to use ViewData in an api controller at all. Api controller endpoints should be purely to return data.
In your case, what you should be doing is, simply have your first method return the string message you want. In the success handler of the first ajax call,you will get this string (Response from the api method) in the success call back. You can use this to update the DOM as needed.
There is no need to have the partial view as well. All you need is a container div element to show the response.
<select required id="makes_DDL" name="makes_DDL"></select>
<select required id="models_DDL" name="models_DDL"></select>
<input type="submit" id="submit_button" value="Submit Form" onclick="formSubmit()" />
<div id="thank_you_div" style="display:none;"></div>
Now in your first ajax call's success callback, update the DOM with the response coming back.
Also $.ajax does not have a setting property called updatetargetid !
function formSubmit() {
//Combines the make and model into one string, Ex: Chevrolet Malibu
var parameter = $("#makes_DDL").children(":selected").text() + " "
+ $("#models_DDL").children(":selected").text();
var uri = "api/vehicle/?user_selections=" + parameter;
$.ajax({
url: uri,
type: "post"
}).done(function(res) {
$("#thank_you_div").html(res).show();
}).fail(function(x, v, e) {
console.log(e);
});
}
Assuming your api controller method returns the string you want
[HttpPost]
public string StoreMakeAndModel(string user_selections)
{
return "You selected the "+user_selections;
}

JSON.NET Deserializing Client Side

In C# I do this from a controller:
public ActionResult GetCompanies()
{
var vm = new CompanyViewModel { jsonData = JsonConvert.Serialize(_repository.All<Company>()) };
return View(vm);
}
ViewModel looks like this:
public CompanyViewModel
{
public string jsonData {get;set}
}
On the client side:
#model MyWebApp.ViewModels.CompanyViewModel
#Scripts.Render("~/bundles/knockout")
<script type="text/javascript">
var serverData = <!-- Many Things To Do Here -->
</script>
In the part <!-- Many Things To Do Here --> there a few things you can do to put the server data into a JavaScript Object.
What is the best way to do this, because I have had problems with JSON.parse to do with some characters JSON.NET serializes.
Is just putting the plain data in there okay (it works): var serverData = #Html.Raw(#Model.jsonData)
What other things should/can be done here?
Rather than creating a model just to hold a JSON string, just pass the plain .NET object to your view, and serialize it to JSON there:
In your controller:
public ActionResult GetCompanies()
{
return View(_repository.All<Comapny>());
}
Then in your view:
<script>
var companies = #Html.Raw(JsonConvert.SerializeObject(Model));
</script>
If you want/need a strongly typed model in your view, you should change your CompanyViewModel to this:
public class CompanyViewModel
{
public IList<Company> Companies {get; set;}
//Or whatever sort of collection your repository returns
}
And in your controller
public ActionResult GetCompanies()
{
var vm = new CompanyViewModel { Companies = _repository.All<Company>() };
return View(vm);
}
Then in your view:
#model MyWebApp.ViewModels.CompanyViewModel
<script>
var companies = #Html.Raw(JsonConvert.SerializeObject(Model.Companies));
</script>

Passing a model from view back to controller to export as CSV

I am trying to make a downloadable CSV. The problem I am having is I am quite unsure how to properly pass the data from the view back over to the controller.
All of my data I want in the CSV is inside #Model.Table. Problem is I am not sure how to pass over that data properly back into my controller. Notice in my js below I try to do that using csvData = #Model.Table.ToCsv(). This horribly fails so I am unsure how to properly do this.
<div class="btn btn-default pull-right" id="dispoCSV">
<i class="icon-file-alt"></i>
Disposition Report</div>
<script>
$("#dispoCSV").click(function () {
var csvData = #Model.Table.ToCsv();
$.post("Respondent/DownloadCSV", function (data) {
csvData
})
});
</script>
When I am pass this obstacle I think I can easily firgure our how to make my DownloadCSV method turn this into a csv.
I don't think you need to convert to CSV from the view. Your controller can do that. I would try it like this:
Model:
public class YourModel {
public YourDataType Table { get; set; }
}
View:
#model YourModel
#using (Html.BeginForm("Submit", "YourController", FormMethod.Post)) {
// Put your model properties in the form so that they get passed to the controller on form submit;
// use #Html.HiddenFor if you want to hide them from being displayed.
// Since your table property has nested properties, you probably need a custom editor template for this.
}
Controller:
public class YourController : Controller {
// Include an action here to display your view
[HttpPost]
public ActionResult Submit(YourModel model) {
var csvData = model.Table.ToCsv(); // assuming you have this method already since it's in your code above
// Do something with the data and return a view
}
}
Instead of passing the entire data back, You should be passing the filter criteria which can be used to generate the data. And in your Action method, Read this criteria and Produce the data and send it in CSV format back the UI.

Ajax does not retrieves data in partial view in mvc

I my below code i am calling partial view with ajax but when i click on the link of product Name the description of that product is not retrieved through ajax and error of ajax executes. I am retrieving the details of items selected by user on the same page but it is not retrieved. Please give any suggestion where is the issue arising because i am new to MVC. thanks...
Create.cshtml
#model List<PartialView.Models.tbl_product>
<!DOCTYPE html>
<html>
<head>
<title>Create</title>
<script src="#Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$('.msg').click(function () {
var id = this.id;
$.ajax({
url: "/Category/Display",
data: { data: id },
success: function (mydata) {
$("#link").empty().append(mydata);
},
error: function (mydata) { alert("error"); },
type: "POST"
});
return false;
});
});
</script>
</head>
<body>
#foreach (var item in Model)
{
<a class="msg" href="#" id="#item.ProductId">#item.ProductName</a>
}
<div id="link">
</div>
</body>
</html>
ClicksUs.cshtml (PartialView)
#model List<PartialView.Models.tbl_product>
#foreach(var items in Model)
{
#items.ProductDesc
}
CategoryController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using PartialView.Models;
namespace PartialView.Controllers
{
public class CategoryController : Controller
{
dbEntities dbentity = new dbEntities();
public ActionResult Create()
{
return View(dbentity.tbl_product.ToList());
}
public ActionResult Display(int data)
{
var query = dbentity.tbl_product.First(c => c.ProductId == data);
return PartialView("ClicksUC", query);
}
}
}
Your Details controller action selects a single element here (because you are calling .First()):
public ActionResult Display(int data)
{
var query = dbentity.tbl_product.First(c => c.ProductId == data);
return PartialView("ClicksUC", query);
}
So the type of the query variable is tbl_product and not List<tbl_product>.
On the other hand your partial's model is List<PartialView.Models.tbl_product> which is obviously wrong.
Your partial's model should be a single tbl_product:
#model PartialView.Models.tbl_product
#Model.ProductDesc
Oh and what others said about the typo in your partial view name.
there are three issues in the code that you could address.
One is a typo (the partialview is called ClicksUS, NOT ClicksUC),
the other is related to the way you return the data
the third is that you use the type: "POST", you should change this to type: "GET".
try changing the code to:
public ActionResult Display(int data)
{
// using First() would have caused you an error in the view if not found
// still not perfect below, but a step closer
var query = dbentity.tbl_product.FirstOrDefault(c => c.ProductId == data);
// You had ClicksUC, rather than ClicksUS
return PartialView("ClicksUS", query);
}
I'd also strongly suggest that you create a ViewModel for your data, rather than passing the objects from the database as this will allow you to control exactly the data that should be viewed and how it should be formatted etc.
[edit]
Also, as Darin says, based on a single row being retruned, you should change your partial view model type to:
#model PartialView.Models.tbl_product

ASP.Net MVC: Calling a method from a view

In my MVC app the controller gets the data (model) from an external API (so there is no model class being used) and passes that to the view. The data (model) has a container in which there are several objects with several fields (string values). One view iterates over each object and calls another view to draw each of them. This view iterates over the fields (string values) and draws them.
Here's where it gets tricky for me. Sometimes I want to do some special formatting on the fields (string values). I could write 20 lines of code for the formatting but then I would have to do that for each and every field and that would just be silly and oh so ugly. Instead I would like to take the field (string value), pass it to a method and get another string value back. And then do that for every field.
So, here's my question:
How do I call a method from a view?
I realize that I may be asking the wrong question here. The answer is probably that I don't, and that I should use a local model and deserialize the object that I get from the external API to my local model and then, in my local model, do the "special formatting" before I pass it to the view. But I'm hoping there is some way I can call a method from a view instead. Mostly because it seems like a lot of overhead to convert the custom object I get from the API, which in turns contains a lot of other custom objects, into local custom objects that I build. And also, I'm not sure what the best way of doing that would be.
Disclaimer: I'm aware of the similar thread "ASP.NET MVC: calling a controller method from view" (ASP.NET MVC: calling a controller method from view) but I don't see how that answers my question.
This is how you call an instance method on the Controller:
#{
((HomeController)this.ViewContext.Controller).Method1();
}
This is how you call a static method in any class
#{
SomeClass.Method();
}
This will work assuming the method is public and visible to the view.
Building on Amine's answer, create a helper like:
public static class HtmlHelperExtensions
{
public static MvcHtmlString CurrencyFormat(this HtmlHelper helper, string value)
{
var result = string.Format("{0:C2}", value);
return new MvcHtmlString(result);
}
}
in your view: use #Html.CurrencyFormat(model.value)
If you are doing simple formating like Standard Numeric Formats, then simple use string.Format() in your view like in the helper example above:
#string.Format("{0:C2}", model.value)
You can implement a static formatting method or an HTML helper, then use this syntax:
#using class_of_method_namespace
...
// HTML page here
#className.MethodName()
or in case of a HTML Helper:
#Html.MethodName()
Controller not supposed to be called from view. That's the whole idea of MVC - clear separation of concerns.
If you need to call controller from View - you are doing something wrong. Time for refactoring.
why You don't use Ajax to
its simple and does not require page refresh and has success and error callbacks
take look at my samlpe
<a id="ResendVerificationCode" >#Resource_en.ResendVerificationCode</a>
and in JQuery
$("#ResendVerificationCode").on("click", function() {
getUserbyPhoneIfNotRegisterd($("#phone").val());
});
and this is my ajax which call my controller and my controller and return object from database
function getUserbyPhoneIfNotRegisterd(userphone) {
$.ajax({
type: "GET",
dataType: "Json",
url: '#Url.Action("GetUserByPhone", "User")' + '?phone=' + userphone,
async: false,
success: function(data) {
if (data == null || data.data == null) {
ErrorMessage("", "#Resource_en.YourPhoneDoesNotExistInOurDatabase");
} else {
user = data[Object.keys(data)[0]];
AddVereCode(user.ID);// anather Ajax call
SuccessMessage("Done", "#Resource_en.VerificationCodeSentSuccessfully", "Done");
}
},
error: function() {
ErrorMessage("", '#Resource_en.ErrorOccourd');
}
});
}
You should create custom helper for just changing string format except using controller call.
I tried lashrah's answer and it worked after changing syntax a little bit.
this is what worked for me:
#(
((HomeController)this.ViewContext.Controller).Method1();
)
You should not call a controller from the view.
Add a property to your view model, set it in the controller, and use it in the view.
Here is an example:
MyViewModel.cs:
public class MyViewModel
{ ...
public bool ShowAdmin { get; set; }
}
MyController.cs:
public ViewResult GetAdminMenu()
{
MyViewModelmodel = new MyViewModel();
model.ShowAdmin = userHasPermission("Admin");
return View(model);
}
MyView.cshtml:
#model MyProj.ViewModels.MyViewModel
#if (#Model.ShowAdmin)
{
<!-- admin links here-->
}
..\Views\Shared\ _Layout.cshtml:
#using MyProj.ViewModels.Common;
....
<div>
#Html.Action("GetAdminMenu", "Layout")
</div>

Categories

Resources