I'm trying to send parameters with ActionAsPdf using the Rotativa Library (1.6.4), unfortunately, the function is called but the parameter trainee in it is always null.
Here's my code:
List<T_Trainee> trainee= new List<T_Trainee>();
foreach (int f in foo)
{
T_Trainee t = new T_Trainee();
t.email = (string)Session["Mail"];
t.phone = (string)Session["Phone"];
trainee.Add(t);
}
//code to get the PDF
ActionAsPdf pdf = new ActionAsPdf("Index", trainee) { FileName = "Bulletin.pdf" };
Trainee var is a list of object T_Trainee not null -> seen in debug:
//function that return the PDF
public ActionResult Index(List<T_Trainee> trainee)
{
ViewModelFoo vmc = new ViewModelFoo();
vmc.trainee = trainee;
return View(vmc);
}
When the function is call in debug mode, I can clearly see that the parameter "trainee" is null but I still don't understand why.
Can anyone help me? Thanks!
ActionAsPdf seems to be a deprecated function in the last version of Rotativa.
I changed it by ViewAsPdf and now it works. The difference between the two function is that you have to send directly the view model inside the Index method call with ViewAsPdf.
Here's my code, I hope that it will help someone :
Code to call the index and send the viewModel
ViewModelFoo vmc = new ViewModelFoo();
List<T_Trainee> trainees= new List<T_Trainee>();
foreach (int f in foo)
{
T_Trainee t = new T_Trainee();
t.email = (string)Session["Mail"];
t.phone = (string)Session["Phone"];
trainees.Add(t);
}
vmc.trainees = trainees;
//code to get the PDF
ViewAsPdf pdf = new ViewAsPdf("Index", vmc)
{
FileName = "File.pdf",
PageSize = Rotativa.Options.Size.A4,
PageMargins = { Left = 0, Right = 0 }
};
Index that generate the view
public ActionResult Index()
{
return View(vmc);
}
Is foo populated?
Try sample code...
List trainee= new List();
trainee.Add(new T_Trainee {email = "sample#email.com", phone = "555-1212"});
Does that work?
You can also try to bind Action to a model
public ActionResult Index(ViewModelFoo vmc)
The second parameter of ActionAsPdf() is type of RouteValueDictionary, which is a dictionary of key and value. You passed in a custom type thereby it converts it to null. It should work if you pass a RouteValueDictionary instead.
ViewAsPdf() receives an object parameter and treats it as a model for view binding that's why it works.
You can have a look at its source code here:
https://github.com/webgio/Rotativa/blob/master/Rotativa/ActionAsPdf.cs
Related
I am doing this tutorial.
In my controller class I have my
public JsonResult GetAllUser(){
List<database1> allUser = new List<database1>();
using (dbContext1 dc = new dbContext1 ())
{
allUser = dc.database1.ToList();
}
return new JsonResult()
{
Data = allUser,
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
MaxJsonLength = Int32.MaxValue
};
}
public JsonResult GetUserWithSerialNumber(string prefix){
List<database1> allUser = new List<database1>();
using (dbContext1 dc = new dbContext1())
{
allUser = dc.database1.Where(a => a.SerialNumber.Equals(prefix)).ToList();
}
return new JsonResult { Data = allUser, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
As of right now, GetAllUser() does not work while get GetUserWithSerialNumber() returns what it is suppose to. Originally GetAllUser() worked, then it gave me
"The length of the string exceeds the value set on the maxJsonLength property."
so I put "Int32.MaxValue" in the GetAllUser method and now when I run the webpage and click on the GetAllUser button it gives me
A script on this page may be busy, or it may have stopped responding. You can stop the script now, open the script in the debugger, or let the script continue.Script: http://localhost:54936/Scripts/jquery-1.10.2.js:7017
All my other methods work except the GetAllUser(). My cshtml and other code looks exactly like the on in the tutorial so I am not sure what I am doing wrong.
I found out the only reason I get the script message is because my database is huge. It's taking too long to go through all of the entries, put them in a list, and put it as an object.
I've been trying to pass an array of int's created by the following function:
function getCurrentSwimmerList() {
var swimmerList = [];
$("#swimmerTable > tbody > tr").each(function () {
swimmerList.push( parseInt($(this).data('swimmerid')) );
});
return swimmerList;
}
Which I use in token-input to eliminate ceartin suggestions from the search box, so I've set up token-input up like so:
$("#swimmerTokenInput").tokenInput("Admin/retrieveTokensForQuery", {
urlParams: { "IDsAlreadyAdded": getCurrentSwimmerList },
I modified the token-input file to allow you to pass additional paramters in a request by setting urlParams, the addition I made to the code was (in the appropriate section):
//add params passed in as urlParams
if (settings.urlParams != null) {
for (var key in settings.urlParams) {
if (settings.urlParams.hasOwnProperty(key)) {
ajax_params.data[key] = settings.urlParams[key];
}
}
}
I tested and I successfully get these values in the query string (where Ol was typed into the search box):
IDsAlreadyAdded=5%2C6&q=Ol
Which chrome recognizes and parses correctly:
IDsAlreadyAdded:5,6
q:Ol
The signature of the method I'm calling is as follows:
public JsonResult retrieveTokensForQuery(string q, int[] IDsAlreadyAdded)
Each time q successfully get's the appropriate value, however IDsAlreadyAdded always gets a null value. I've looked at various answers on SO (trying traditional=true, or IdsAlreadyAdded[] = ..., having List<int> or IEnumerable<int>) to try fix the problem, but I couldn't get it to work.
Any help would be greatly appreciated.
You could always create a custom model binder that maps the string value to an int[] like this:
public class IntArrayModelBinder : System.Web.Mvc.DefaultModelBinder
{
public override object BindModel(System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (!string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
{
var items = valueProviderResult.AttemptedValue.Split(',');
var result = new int[items.Length];
for (var counter = 0; counter < items.Length; counter++)
{
result[counter] = int.Parse(items[counter]);
}
return result;
}
return base.BindModel(controllerContext, bindingContext);
}
}
and then just register the binder when you initialise your routing:
ModelBinders.Binders.Add(typeof(int[]), new IntArrayModelBinder());
I have a method in another class i'm using to send data to a database. That method is here as well.
public Int32 AddOrder(clsStock NewItem)
{
//this function takes the data passed via NewItem
//and passes it to the parameters for the stored procedure
//
//create an instance of the data dictionary
clsDataDictionary DD = new clsDataDictionary();
//create an instance of the object class
Int32 ReturnValue;
//create an instance of the data conduit
clsDataConduit Items = new clsDataConduit();
//pass the data for this address
Items.AddParameter(DD.sproc_tblOrders_Add_AuthId, NewItem.AuthId);
Items.AddParameter(DD.sproc_tblOrders_Add_ItemId, NewItem.ItemId);
Items.AddParameter(DD.sproc_tblOrders_Add_DateOrdered, NewItem.DateOrdered);
Items.AddParameter(DD.sproc_tblOrders_Add_Cancel, NewItem.Cancel);
//execute the stored procedure
ReturnValue = Items.Execute(DD.sproc_tblOrders_Add);
//return the primary key value
return ReturnValue;
}
The method on my aspx page which i'm using to iterate through my listbox and execute that method for each item in the listbox is here as well.
protected void btnSubmit_Click1(object sender, EventArgs e)
{
//create an instance of the collection class
clsStockCollection Items = new clsStockCollection();
foreach(int id in lstAdded.Items)
{
TheItem.AuthId = 5;
TheItem.ItemId = Convert.ToInt32(lstAdded.Items[id].Value);
TheItem.Cancel = "false";
Items.AddOrder(TheItem);
}
Response.Redirect("Order.aspx");
}
When I run my website and hit the btnSubmit it's giving the following error :
"Specified cast is not valid" that is on the method on the aspx page (the 2nd pastebin file)
Any idea why this is?
It should be like this
foreach(ListItem item in lstAdded.Items)
{
TheItem = new clsStock();
TheItem.AuthId = 5;
TheItem.ItemId = Convert.ToInt32(item.Value);
TheItem.Cancel = "false";
Items.AddOrder(TheItem);
}
You are iterating the ListBox.Items through an int type field. ListBox.Items is a ListItemCollection, what you can do is use implicitly typed variable using var keyword, like:
foreach(var id in lstAdded.Items)
{
TheItem.AuthId = 5;
TheItem.ItemId = Convert.ToInt32(id.Text); //Change here
TheItem.Cancel = "false";
Items.AddOrder(TheItem);
}
Currently it appears you are considering it as an index in foreach loop, instead its a single item from the lstAdded
I am trying to pass my model List< Models.Statement > statementList from one action to another but i am receiving null value in the 2nd controller. Please suggest what is wrong here. Even tried with:
return RedirectToAction("WriteInTemplate", new { statementList = statementList });
Please help.
public ActionResult SendPdfStatement(string InvoiceNumber)
{
try
{
InvoiceNumber = InvoiceNumber.Trim();
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
List<Models.Statement> statementList = new List<Models.Statement>();
statementList = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters).ToList<Models.Statement>();
//WriteInTemplate(statementList);
return RedirectToAction("WriteInTemplate", statementList );
}
catch (Exception e)
{
InvoiceSearchTool.Models.udtExceptionTable exception = new udtExceptionTable();
exception.MethodName = "SendPdfStatement";
exception.Exception = e.ToString();
exception.Date = DateTime.Now;
DYNAMICS_EXTEntities db = new DYNAMICS_EXTEntities();
db.AddToudtExceptionTables(exception);
db.SaveChanges();
return View("Error");
}
}
public ActionResult WriteInTemplate(List<Models.Statement> statementList)
{
try
{
string invoiceNumber = statementList.FirstOrDefault().Invoice.ToString().Trim();
...................snip..........
return RedirectToAction("CreateMessageWithAttachment", "email", invoiceNumber);
}
catch (Exception e)
{
InvoiceSearchTool.Models.udtExceptionTable exception = new udtExceptionTable();
exception.MethodName = "WriteInTemplate";
exception.Exception = e.ToString();
exception.Date = DateTime.Now;
DYNAMICS_EXTEntities db = new DYNAMICS_EXTEntities();
db.AddToudtExceptionTables(exception);
db.SaveChanges();
return View("Error");
}
}
Please take a look here to pass your Model
you are not passing "statementList" , instead you are passing new { statementList= statementList} just pass the model and you should be fine .
return RedirectToAction("WriteInTemplate", statementList);
Answer by sino
RedirectToAction() writes a redirect command to the browser, making it start a brand new request to WriteInTemplate(). Your model object is therefore lost.
Is WriteInTemplate() an independent action which will sometimes be responsible for an entire request from a user or a partial request from a view? If not, you should just call it as a regular method instead of using RedirectToAction().
This is because you had spefified wrong route parameters.
while thinking about this.. did you check that the data are not null?
you are using
return RedirectToAction("WriteInTemplate", statementList );
instead you should use
return RedirectToAction("WriteInTemplate","controllerName", new{"statementList"=stetementList});
see reference here
The way you call the RedirectToAction() method may not be your issue.
For me, the solutions presented above did not work because the RedirectToAction() method builds a RouteValueDictionary by using the .ToString() value of each property in the model. This will only work if all the properties in the model are simple properties and it fails if any properties are complex objects, lists, collections, etc.
because this method does not use recursion.
If for example, a model called MymodelOrganization contained a property List Employees, then that property would result in a query string value of
....&Employees=System.Collections.Generic.List'1[System.String]
and binding would fail, and you would end up (as was my case) with ... null
I had this problem, so I created a copy of my model containing only the elements of the form, stripping my Lists and passed that inside RedirectToAction().
Once on the other action method, I was able to re-assemble my Lists and added them to my Model before calling the last return. Good luck. Here is the idea in my code:
[HttpPost]
public ActionResult ItemSubmissionForm(CombinedModelContent membervalues)
{ ...
ItemSubmissionsDBFields aFieldsList = membervalues.FieldsList; //Stripping other objects
return RedirectToAction("ItemSubmissionConfirm", aFieldsList);
}
[HttpGet]
public ActionResult ItemSubmissionConfirm(ItemSubmissionsDBFields aFieldsList)
{ ...
List<SomeArea> SomeAreaitems = new List<SomeArea>();
SomeAreaitems.Add ...
CombinedModelContent copymembervalues = new CombinedModelContent();
copymembervalues.SomeCodeLists = SomeAreaitems;
copymembervalues.FieldsList = aFieldsList;
return View("SomeConfirmPage", copymembervalues);
I'm tryping to use JSON to update records in a database without a postback and I'm having trouble implementing it. This is my first time doing this so I would appreciate being pointed in the right direction.
(Explanation, irrelevant to my question: I am displaying a list of items that are sortable using a jquery plugin. The text of the items can be edited too. When people click submit I want their records to be updated. Functionality will be very similar to this.).
This javascript function creates an array of the objects. I just don't know what to do with them afterwards. It is called by the button's onClick event.
function SaveLinks() {
var list = document.getElementById('sortable1');
var links = [];
for (var i = 0; i < list.childNodes.length; i++) {
var link = {};
link.id = list.childNodes[i].childNodes[0].innerText;
link.title = list.childNodes[i].childNodes[1].innerText;
link.description = list.childNodes[i].childNodes[2].innerText;
link.url = list.childNodes[i].childNodes[3].innerText;
links.push(link);
}
//This is where I don't know what to do with my array.
}
I am trying to get this to call an update method that will persist the information to the database. Here is my codebehind function that will be called from the javascript.
public void SaveList(object o )
{
//cast and process, I assume
}
Any help is appreciated!
I have recently done this. I'm using MVC though it shouldn't be too different.
It's not vital but I find it helpful to create the contracts in JS on the client side and in C# on the server side so you can be sure of your interface.
Here's a bit of sample Javascript (with the jQuery library):
var item = new Item();
item.id = 1;
item.name = 2;
$.post("Item/Save", $.toJSON(item), function(data, testStatus) {
/*User can be notified that the item was saved successfully*/
window.location.reload();
}, "text");
In the above case I am expecting text back from the server but this can be XML, HTML or more JSON.
The server code is something like this:
public ActionResult Save()
{
string json = Request.Form[0];
var serializer = new DataContractJsonSerializer(typeof(JsonItem));
var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(json));
JsonItem item = (JsonItem)serializer.ReadObject(memoryStream);
memoryStream.Close();
SaveItem(item);
return Content("success");
}
Hope this makes sense.
You don't use CodeBehind for this, you use a new action.
Your action will take an argument which can be materialized from your posted data (which, in your case, is a JavaScript object, not JSON). So you'll need a type like:
public class Link
{
public int? Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Url { get; set; }
}
Note the nullable int. If you have non-nullable types in your edit models, binding will fail if the user does not submit a value for that property. Using nullable types allows you to detect the null in your controller and give the user an informative message instead of just returning null for the whole model.
Now you add an action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult DoStuff(IEnumerable<Link> saveList)
{
Repository.SaveLinks(saveList);
return Json(true);
}
Change your JS object to a form that MVC's DefaultModelBinder will understand:
var links = {};
for (var i = 0; i < list.childNodes.length; i++) {
links["id[" + i + "]"] = list.childNodes[i].childNodes[0].innerText;
links["title[" + i + "]"] = list.childNodes[i].childNodes[1].innerText;
links["description[" + i + "]"] = list.childNodes[i].childNodes[2].innerText;
links["url[" + i + "]"] = list.childNodes[i].childNodes[3].innerText;
}
Finally, call the action in your JS:
//This is where I don't know what to do with my array. Now you do!
// presumes jQuery -- this is much easier with jQuery
$.post("/path/to/DoStuff", links, function() {
// success!
},
'json');
Unfortunately, JavaScript does not have a built-in function for serializing a structure to JSON. So if you want to POST some JSON in an Ajax query, you'll either have to munge the string yourself or use a third-party serializer. (jQuery has a a plugin or two that does it, for example.)
That said, you usually don't need to send JSON to the HTTP server to process it. You can simply use an Ajax POST request and encode the form the usual way (application/x-www-form-urlencoded).
You can't send structured data like nested arrays this way, but you might be able to get away with naming the fields in your links structure with a counter. (links.id_1, links.id_2, etc.)
If you do that, then with something like jQuery it's as simple as
jQuery.post( '/foo/yourapp', links, function() { alert 'posted stuff' } );
Then you would have to restructure the data on the server side.