Opening Multiple Links In ASP.NET MVC? - c#

I have an application that takes some user input from a view, the reports the user wants, and creates a parameter string from it in the controller, and I need to open up multiple report urls after the query string is created but not sure how:
View snippet:
#Html.EditorFor(model => model.Id)
#Html.CheckBoxFor(model => model.report1) Report1
#Html.CheckBoxFor(model => model.report2) Report2
#Html.CheckBoxFor(model => model.report3) Report3
<input type="submit" value="Index" />
controller snippet
[HttpPost]
public ActionResult Index(ViewModel model) {
string parameters="&id="+model";
if(model.report1==true)
{
string report1="http://<urlhere>"+parameters;
}
//CONTINUE for the other two reports as well
}
I need to open the reports in multiple tabs. I have researched it extensively and it seems like you can't open multiple tabs from the controller, so I'm at a loss. I considered putting the urls in a list, passing them into a View, and using JavaScript to open them on the page load, but I honestly am not sure how to do that in Javascript and MVC.

As you already found out this is not possible from the server side, so pass the report URLs to the client and use something like the following JavaScript:
#if (!String.IsNullOrWhiteSpace(Model.ReportUri))
{
<a id="reportLink" href="#Model.ReportUri" target="_blank">REPORT</a>
<script type="text/javascript">
var link = document.getElementById('reportLink');
link.click();
link.parentNode.removeChild(link);
</script>
}
Please keep in mind, that popup-blockers will most likely block this, so you should tell this somehow to the user. Also maybe it is usefull to keep the links on the page in your case (so remove the last line of my script) to give the user the chance to manually open them when they get blocked...

Related

Why does clicking #Url.Action throws an error?

When I click a button input which triggers an action ImagePopup inside a controller Stories but it throws an error.
Code:
#{
var listData = (List<HimHer.Models.Stories>)ViewBag.Grid;
foreach (var imageName in listData)
{
<div class="col-md-4">
#Html.HiddenFor(model => model.Story)
<input
class="img-responsive img-thumbnail"
type="image"
onclick="location.href='#Url.Action("ImagePopup", "Stories", new {story= imageName.Story})'"
src="#("/UploadedFiles/"+ imageName.Image)"
alt="Submit"
width="100%"
height="100%"/>
</div>
}
}
Upon clicking an input it throws an error:
The resource cannot be found. Requested URL: /Stories/ImagePopup
Even though it exists. It is right inside the Stories folder. It's a partial view without a model.
[HttpPost]
public ActionResult ImagePopup(string story)
{
ViewBag.PopupStyle = "";
ViewBag.PopupStory = story;
return View("GetImagesStories");
}
What am I doing wrong?
It's looking for an HTTPGet Action I believe.
If you want to call your post, you'll need to use HTML.BeginForm but it can get hairy if there are too many on a page.
Setting the href location of the current page:
location.href=
Causes the browser to do a Get Request type and does not post any form data back to your controller. Because the method on your controller specifically only works for post requests (because of the [HttpPost] attribute) there is no other matching methods that could work, thus you get an Exception.
Solutions:
You can continue using the Get method. Replace [HttpPost] with [HttpGet] will get you half way there. The other requirement will be for you to make sure the Url.Action code contains all the necessary information to be posted back (for example all the data in #Html.HiddenFor(model => model.Story) is not included, I don't know if you need it or not).
Or
You can modify your code to use the Post method. Change your <input type="image" to a <button type="submit"> and add a form around each button and hidden input element.
try using [HttpGet] attribute for your ImagePopup action method

ASP.Net MVC 5: Html.RenderAction("...") at runtime

I want to add a PartialView multiple times by pressing a button.
<div id="FilterRows">
#{Html.RenderAction("_FilterRow");}
</div>
<button id="newRow" type="button"
class="btn btn-sm btn-default"
style="width: 50px">+</button>
This piece of code works properly. But now i want to append the div FilterRows with another PartialView of _FilterRow at clicking on the button.
This is how it looks today:
Something like:
$(document).ready(function() {
$("#newRow").click(function() {
$("#Exec").append("<br>", #{Html.RenderAction("_FilterRow");} {
});
});
});
Is unfortunately not working. Any Ideas?
If you add an action which returns the partial rendered as a partial (ie. return PartialView("myView", model); then you can load using jQuery:
# Create a new element to contain...
var el = $('<div></div>');
$('#parent').append(el);
# ...the new content
el.load('#Url.Action("action", "controller"');
(This means running the JS in the razor view to get the correct URL generation. If most of the JS is in its own file, pass the URL from a little JS in the Razor file just for things like URLs.)
As long as your script is in the page (and not in an external .js file) you can use Razor inside js (although feedback directly from MicroSoft indicates that this is "unexpected", it works fine).
Have a look at the rendered html to see what's wrong.
In this case you need quotes (") around the render action:
$("#FilterRows").append("#{Html.RenderAction("_FilterRow");}");
This assumes a number of potential issues:
the 'RenderAction' can't have any newlines in the output
the 'RenderAction' can't have any quotes in the output (either use ' on the append and " inside the render or the other-way-around)
the action to be rendered cannot have any row-specific parameters (which appears to be ok in this case to add a new blank row)
the script must be in a .cshtml file (though you can get around this by setting a global/namespace'd variable in the .cshtml and have the actual code in a .js file)
you need to use the correct combination of #{}/#() and render/tostring
You might be better off with #Html.RenderPartial if you just want to render some html and don't need an action.
An alternative, perhaps more friendly, mechanism would be to have the blank-row already on the page (perhaps hidden) and use .clone().

MVC And Ajax.BeginForm and DIV.Load

I have a partial that defines an Ajax.BeginForm. The model returned has a property for ReportLink created on the server resource that returns a Url to a PartialView.
On the Ajax,BeginForm.OnSuccessFunction I am attempting to return and load html content with $("reportContent").load(AJAXRESULT.RenderLink)
However, I get into an infinite loop somewhere.
Edited to add moving parts:
#model xxxx.Reports.Models.Reports.BaseReportModel
#{Layout = null;}
<div id="reportBase" class="k-content">
<div id="reportControl" >
#using (Ajax.BeginForm(
Model.PostAction,
Model.PostController,
null,
new AjaxOptions() { OnSuccess = "editPostSuccess", OnFailure = "editPostFailure" },
new { id = "reportBase_frmViewer", name = "reportBase_frmViewer" }))
{
#Html.AntiForgeryToken()
#RenderSection("reportParams", required: false)
if (#Model.AllowRefresh){
<input type="button" id="btnRefresh" value="refresh" />
}
if (#Model.AllowExportToPDF){
<input type="button" id="btnPDF" value="PDF" />
}
if (#Model.AllowExportToExcel){
<input type="button" id="btnExcel" value="XLS" />
}
#Html.HiddenFor(p => p.AllowExportToExcel)
#Html.HiddenFor(p => p.AllowExportToPDF)
#Html.HiddenFor(p => p.AllowRefresh)
#Html.HiddenFor(p => p.AutoStartReport)
}
</div>
</div>
<div id="wait"></div>
<div id="reportContent"></div>
//The
<script type="text/javascript">
function editPostSuccess(ajaxContext) {
showWaitIndicator(false);
$('#reportContent').load(ajaxContext.RenderLink**,<--This is a link to an asction that calls renderPartial on the controller** function () {
$("#reportContent").show();
});
}
function editPostFailure(ajaxContext) {
showWaitIndicator(false);
var response = JSON.parse(ajaxContext.responseText);
var errorMessage = response.Message;
$('#reportContent').html('#Resources.labels.lblServerErrorsOnForm' + " " + errorMessage);
$("#reportContent").show();
alert("FAILURE:"+response.Message);
}
Update: I am no longer sure that browser is locking up. The content is 3.15 MB. I think the raw html is very verbose and takes that long to render in the browser. Here is the execution sequence:
LoadPartial(ReportModel) - This has an Ajax.BeginForm() that returns report meta data and has a refresh button. When the refresh button is clicked. I call a js function that loads a div using div.load(ReportModel.RenderLink), which is a url to get the content as html from the controller.
The ReportModel.RenderLink points to a controller method that returns an html report based on model params sent to the controller. The result is a PartialView("ReportContentPartial",string) where string is the html fragment.
For testing the ReportContentPartial just dumps #Model into a div as #Html.Raw(Model) and this is where the browser locks up. I thought it was in an infinite loop but it is just taking way to long to render the html.
The part I do not understand is when I use #Html.BeginForm and dump the #Html.Raw(HTML) it is pretty quick. I need to somehow use the same mechanism used in that rendering method. The report html is fetched and returned in less than a second. The problem is when I trace out of #Html.Raw(HTML) the browser locks and takes +15 seconds to render the content.
I will keep plugging at it. I am sure there is a more efficient way to go about it and I probably need to think about ways to break up the reports.
Update 2: This seems to be development environment issue. I am launching IE from VS2012 using the run command. I sense that something is up with the asp.net dev server. When I load the same url in chrome, while debugging in vs, it renders in less than a second.
I solved this. It seems there was an issue with browser definition files that shipped with .net 40. I resorted back to a forms based version of the reporting application that used .aspx pages and postbacks to see why it would render fast and the MVC version was so slow. When using IE 10 it would not render and I found the _dsoPostBacks were not working on the forms version. I then found Scott Hanselman's blog on browsers definition files and applied the hotfix on the test server and now the mvc version in ie is rendering at an acceptable rate. It has something to do with recognizing the browser and downgrading js support. I do not know why that fixed my problem but it did :)

Multiple access control on a web page - C#

In my company we're currently working in our portal. It will be something like a social network.
I'm working on the "profile" part of the portal.
I was wondering, as I'm new to C#, how can I implement a "see-only-what-i-let-you" functionality.
Take for instance Facebook. There you can show only parts of your profile to some people, other parts to other people, you have full acess as others may have no acess at all.
That's precisely what I need to implement.
We're working with MVC3/jQuery1.5/WCF.
One option would be to split your view up in to child actions:
#{Layout = "~/Views/Shared/Layouts/MasterLayout.cshtml";}
<div class="splitter">
<div class="left-column">
#Html.Action("Navigator")
#Html.Action("MyPosts")
</div>
<div class="main-content">
#Html.Action("RecentStories")
#Html.Action("Adverts")
</div>
</div>
Then in your controller each action decides if the current user can see this piece of content, e.g.
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult RecentStories()
{
if(current_user_cannot_access_this_content())
return View("BlankView"); // Might want to render some place holder content
// Setup necessary view data by pulling back content from the database.....
return View(); // Render the /Views/<controller>/RecentStories.cshtml view
}
Try to cache user permission data across child actions if possible (depends on how your calculation of what a user can see works).
This code is assuming all your actions are running off the same controller. You can hit a different controller for a child action just by passing the name of the controller as the second parameter.

MVC 2 Ajax.Beginform passes returned Html + Json to javascript function

I have a small partial Create Person form in a page above a table of results. I want to be able to post the form to the server, which I can do no problem with ajax.Beginform.
<% using (Ajax.BeginForm("Create", new AjaxOptions { OnComplete = "ProcessResponse" }))
{%>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%=Html.LabelFor(model => model.FirstName)%>
</div>
<div class="editor-field">
<%=Html.TextBoxFor(model => model.FirstName)%>
<%=Html.ValidationMessageFor(model => model.FirstName)%>
</div>
<div class="editor-label">
<%=Html.LabelFor(model => model.LastName)%>
</div>
<div class="editor-field">
<%=Html.TextBoxFor(model => model.LastName)%>
<%=Html.ValidationMessageFor(model => model.LastName)%>
</div>
<p>
<input type="submit" />
</p>
</fieldset>
<%
}
%>
Then in my controller I want to be able to post back a partial which is just a table row
if the create is successful and append it to the table, which I can do easily with jquery.
$('#personTable tr:last').after(data);
However, if server validation fails I want to pass back my partial create person form with the validation errors and replace the existing Create Person form.
I have tried returning a Json array
Controller:
return Json(new
{
Success = true,
Html= this.RenderViewToString("PersonSubform",person)
});
Javascript:
var json_data = response.get_response().get_object();
with a pass/fail flag and the partial rendered as a string using the solition below but that doesnt render the mvc validation controls when the form fails.
SO RenderPartialToString
So, is there any way I can hand my javascript the out of the box PartialView("PersonForm") as its returned from my ajax.form? Can I pass some addition info as a Json array so I can tell if its pass or fail and maybe add a message?
UPDATE
I can now pass the HTML of a PartialView to my javascript but I need to pass some additional data pairs like ServerValidation : true/false and ActionMessage : "you have just created a Person Bill". Ideally I would pass a Json array rather than hidden fields in my partial.
function ProcessResponse(response) {
var html = response.get_data();
$("#campaignSubform").html(html);
}
Many thanks in advance
what I've done in this circumstance is actually render out a partial view that has all the information (e.g. validation info, etc) and then use DOM functions in jquery to extract what I need, if it's more than just for display. Keeps everything in one format that way and I don't have to wory about handling both json and html.
Why do you even need to pass back the partial person object? As far as I'm aware ASP.NET MVC should leave the form filled out as the user filled it in but add validation errors to the form.
They can then correct the errors and submit the form again. Sound more like a problem with the design of the web site and something that you shouldn't be doing in the first place.
If you need to store the data the user entered I would create a json person object and submit this object with the ajax request and then when the validation errors come back you already have the data that was sent.
Ajax enables you to have state in the web client as long as the user is viewing the page.

Categories