k... Strager was able to help me a bit. However, it's still not working quite right. Need someone that knows both MVC and jQuery please...
I have a page that has a image that when clicked on will launch a dialog box that gives the ability to upload a file. Once the dialog closes the image is supposed to refresh with what was uploaded/stored in the database...
All works great the first time. However if I try uploading a 2nd image; the 1st image still displays. It also doesn't seem like my controller method is being called the 2nd time... Below is my code...
I've also eliminated that the page is being cached. Again, the controller method is not being called the 2nd time around...
Controller
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetImage(Guid parentId)
{
var la = Helper.Service.GetAttachmentByEntity(parentId, MembershipProvider.SecurityTicket).ToList();
la.OrderByDescending(x => x.CreatedDate);
return File(la[0].Data, la[0].ContentType);
}
View
<script type="text/javascript">
$(function() {
var iphcObject = $('#<%=imagePlaceHolderControl.ClientID %>');
iphcObject.children().find('#imageUploadDialog').bind('onClose', function() {
iphcObject.children().find('#image').attr('src', '<%=Url.Action("GetImage", "Image", new { parentId = Model.ParentId }) %>');
});
});
</script>
<asp:Panel ID="imagePlaceHolderControl" runat="server">
<div style="border: solid 1px; width: <%=Model.Width%>; height: <%=Model.Height%>; text-align: center; vertical-align: middle;">
<a href="#" onclick="$('#imageUploadDialog').dialog('open');" title="Add a picture...">
<img id="image" src="<%=Url.Content("~/content/images/icon_individual_blank.gif") %>" /></a>
<%Html.RenderDialog("imageUploadDialog", "Upload image...", "ImagePlaceHolder_Upload", "Image", Model, 235, 335); %>
</div>
</asp:Panel>
What is #image shouldn't it be find('img')? Or give the IMG an ID of 'image'.
I don't know ASP, but this line:
iphcObject.find('#image').attr('src', '<%=Url.Action("GetNewImage", "Image", new { parentId = Model.ParentId }) %>');
Appears to be parsed at the time the page is initially viewed, not when the new image is uploaded.
To fetch the new image's URL, send that data in the response to the POST request. This should be easy, especially if you use JSON or raw text for the transfer.
For the jQuery side:
$.post('<%=Url.Action("GetImage", "Image", new { parentId = Model.ParentId }) %>', {}, function(data)
{
iphcObject.find('#image').attr('src', data.newImageUrl);
}, 'json');
If anyone knows how to do it on the ASP side, please edit this post or make another!
You can tack on an arbitrary number to force a refresh on the client side:
<img id="img" src="#Url.Action("GetImage")" alt="random" />
<script type="text/javascript">
var src = $("#img").attr("src");
var count = 0;
setInterval(function () {
$("#img").attr("src", src + "?" + count);
count++;
}, 3000);
</script>
Your <img> does not have the id image so .find('#image') isn't returning any elements.
I don't know ASP, but... Have you checked the HTTP headers? It could be that the image URL is being cached by the browser. Try LiveHTTPHeaders for Firefox and see what calls are actually making it to the server. Firebug may also be of some assistance here (it tracks cache hits in the browser vs network calls).
Related
I am revitalizing a very old application and trying not to introduce Devexpress or Telerik into this application.
I have a need for some dropdownlists with multiple selection availability. I have poked around on the web and the chosen jquery plugin looks to be the route to go.
I have it implemented in one of my test pages, but I am trying to get this implemented rather quickly without much tooling around with it. I am having some difficulties grabbing the multiple selected values on the server side in my code behind. I don't really want to have a bunch of client side functionality holding and maintaining data on change etc.
Any one ever attempt to get at this data server side versus client side and have luck?
Code example. :
<select id="slcExample" multiple class="chosen-select" style="width:350px;" runat="server"></select>
<script type="text/javascript">
$(document).ready(function () {
var config = {
'.chosen-select': {},
'.chosen-select-deselect': { allow_single_deselect: true },
'.chosen-select-no-single': { disable_search_threshold: 10 },
'.chosen-select-no-results': { no_results_text: 'Oops, nothing found!' },
'.chosen-select-width': { width: "95%" }
}
for (var selector in config) {
$(selector).chosen(config[selector]);
}
});
</script>
I have found that if I could get at this property .SelectedIndices that I would have access to the selected values but it will not let me use this on the server side as it is a protected property of the select in asp.net.
Here is what I ended up doing. :
Dim index As Integer
Dim valuesChosen As String = ""
For index = 0 To (slcExample.Items.Count - 1)
If (slcExample.Items(index).Selected) Then
valuesChosen += slcExample.Items(index).Value.Trim + ";"
End If
Next
I needed something on the server side. Hopefully this helps someone else. If you have a better option I am open to seeing it and will mark as answer if better.
You can create a hidden field with asp:net class. with a javascript method, add all value in a comma separated list.
You can have the list on the server side after submiting your form.
try putting clientIDMode="Static"
<asp:HiddenField runat="server" ID="hidTest" ClientIDMode="Static" />
but if you can't, you will have to see the generated name from asp to update it in your javascript method
<script type="text/javascript">
$(document).ready(function () {
var config = {
'.chosen-select': {},
'.chosen-select-deselect': { allow_single_deselect: true },
'.chosen-select-no-single': { disable_search_threshold: 10 },
'.chosen-select-no-results': { no_results_text: 'Oops, nothing found!' },
'.chosen-select-width': { width: "95%" }
}
var hiddenSeparatedList = "";
for (var selector in config) {
hiddenSeparatedList += $(selector).chosen(config[selector]) + ','
$('#hidTest').val(hiddenSeparatedList);
}
});
</script>
I had the same problem and switched to this JQuery plugin instead: http://www.erichynds.com/blog/jquery-ui-multiselect-widget
Since the asp.net dropdown control does not allow multiple items, I used a regular tag with runat server and an ID. The plugin actually selects the items and then you can read them from code behind.
The plugin will works client-side on the asp.net dropdown, but you can't get the selected items in the code behind. So depending on your needs .. .
Hope this helps!
Bonnie
I used a List instead:
int i;
IList<string> chosenItems = new List<string>();
for (i = 0; i <= selectExample.Items.Count - 1; i++)
{
if (selectExample.Items[index].Selected)
{
chosenItems.Add(selectExample.Items[index].Value.Trim());
}
}
I'm trying to use a simple jquery-ui modal dialog as a delete confirmation in an ASP.NET C# application. I've done this many times before, but for some reason in this application it is misbehaving. I see the dialog pop up then it immediately disappears before I can click on either "Yes" or "No". Here's the relevant code (Javascript):
<script type="text/javascript" src="/resources/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="/resources/jquery-ui-1.9.1.custom.min.js"></script>
<link rel="stylesheet" type="text/css" href="/resources/ui-lightness/jquery-ui-1.9.1.custom.css" />
<script type="text/javascript">
var remConfirmed = false;
function ConfirmRemoveDialog(obj, title, dialogText) {
if (!remConfirmed) {
//add the dialog div to the page
$('body').append(String.Format("<div id='confirmRemoveDialog' title='{0}'><p>{1}</p></div>", title, dialogText));
//create the dialog
$('#confirmRemoveDialog').dialog({
modal: true,
resizable: false,
draggable: false,
close: function(event, ui) {
$('body').find('#confirmRemoveDialog').remove();
},
buttons:
{
'Yes, remove it': function() {
$(this).dialog('close');
remConfirmed = true;
if (obj) obj.click();
},
'No, keep it': function() {
$(this).dialog('close');
}
}
});
}
return remConfirmed;
}
//String.Format function (since Javascript doesn't have one built-in
String.Format = function() {
var s = arguments[0];
for (var i = 0; i < arguments.length - 1; i++) {
var reg = new RegExp("\\{" + i + "\\}", "gm");
s = s.replace(reg, arguments[i + 1]);
}
return s;
}
</script>
Here's where I'm using the confirmation function (in the OnClientClick of an <asp:Button> control):
<asp:Button ID="btnRemoveProgram" runat="server" Text="Remove" CausesValidation="false" OnClientClick="ConfirmRemoveDialog(this, 'Please confirm removal', 'Are you sure you wish to remove the selected Program? This cannot be undone.');" />
As I said, I've successfully used this same construct (nearly identical code) many times before; I don't know why it isn't working now. Any ideas will be greatly appreciated, I'm truly stumped on this one.
The runat="server" is telling the button that it should post back to perform events at the server. The OnClientClick will be executed before that on the client side, so you will see the dialog and then immediate the page posts, causing the dialog to disappear.
The problem is that your modal dialog box is not modal in the traditional windows sense. The javascript continues on. The simplest test is to add an alert right before your return, you will see it pops up right after the dialog is shown.
To get around this issue, return false always in the OnContentClick and then in your Yes/No button event handlers use the __doPostback javascript method.
You need to return the remConfirmed to the caller which is the button itself. On your button, do this:
OnClientClick="return ConfirmRemoveDialog(/* the rest of the code */);"
I'm trying to use jQuery to load a PartialView. It does this fine at the first loading of the page. But then I need to be able to reload the PartialView when a save button is pressed. I get a reload, but this time the PartialView is all I get back. I.e. I don't get the PartialView loaded as a part of the main page, but rather as a page of its own. What am I doing wrong?
Here are the relevant parts of the jQuery in the View:
$.get('<%=Url.Action("GetTasks", "Timesheet", new {id = DateTime.Today.ToShortDateString() }) %>', function (data) {
$('#tasksDiv').html(data);
}); //This part works fine on first load of the page
$('#savenewtask').click(function (event) {
event.preventDefault();
$.get('<%=Url.Action("GetTasks", "Timesheet", new {id = DateTime.Today.ToShortDateString() }) %>', function (data) {
$('#tasksDiv').html(data);
});
}); //This only loads the PartialView, but not as part of the main page...
The button and the div to load in:
<p>
<input type="button" value="Spara" id="savenewtask" />
</p>
<div id="tasksDiv">
</div>
UPDATE:
It actually worked, I had just confused the two input fields I have on the page. But I'll rephrase the question to a simple one: Is this the best way to do this sort of thing with PartialViews, or should I go about it another way? (I.e. I was just trying to figure out a way to achieve what I wanted without knowing if it is the "best practice" way of doing it).
I have typically used the load method, which sets the innerHtml.
var url = '<%=Url.Action("GetTasks", "Timesheet", new {id = DateTime.Today.ToShortDateString() }) %>'
$("#tasksDiv").load(url);
I am in the process of putting a new site together which will make use of AJAX to pull through page content should the user have javascript enabled.
So, I am in the situation whereby every Action Method requires a check to see if the request was through AJAX or not, which is straightforward. If the request was through AJAX then I can return a partialview, if not then a full view can be returned.
With this pattern though, I'll need to create a View and a PartialView for every page on the site. The only real difference between them is going to the inclusion of the masterpage.
Am I missing a trick here is is this doubling up of views the only way to go?
Thanks
EDIT - a bit more info
Lets say I had a page that could get accessed through /site/test. Somewhere in my JS I would add a hash to the url like so #/site/test. JS would then watch for any hash changes and load the partial views as needed. If JS was not available though, an entire view would need to be returned.
So for each page I would need the view, which would then include a call to RenderPartial which would load up the partial view which would actually contain the page content. So, for every page there are two files. It just seems there should be a cleaner way of doing this.
Sergio, yes you are missing a trick!!
You should organise your page so that the static content in it is just that - static. This static page then calls the partial(s) that give the dynamic content. this would typically be used in the main page as such (i'm using jquery as per microsofts adopted stance on ajax now):
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>My Header</h2>
<%--lots of stuff omitted--%>
<div id="dynamicList"><%Html.RenderPartial("List"); %></div>
<%--also lots missed out here--%>
<input type="button" id="btnRefresh" value="refresh" />
</asp:Content>
this means that the partial would always be rendered in the initial request. subsequent refreshes would call the partial method in the controller and repopulate the 'dynmaicList' div along the lines of:
<script type="text/javascript">
// you might have a click or similar here to invoke the partial refresh
$(function() {
//click event (or some other 'change' event)
$('#btnRefresh').click(function() {
dynamicList();
});
});
function dynamicList() {
// where action/controller retruns a partialview result
var url = '<%= Url.Action("List", "MyController") %>';
// this is merely a wrapper method around jquery $ajax
SendAjax(url, formParams(), beforedynamicListQuery, dynamicListResponse);
}
function beforedynamicListQuery() {
$("#dynamicList").fadeTo('slow', 0.5);
}
function dynamicListResponse(data) {
if (data.length != 0) {
if (data.indexOf("ERROR:") >= 0) {
$("#dynamicList_errmsg").html(data);
}
else {
var selector = "#dynamicList";
$(selector).fadeTo('slow', 1, function() {
$(this).html(data);
});
}
}
}
</script>
anyway, that's my take on it!! ;)
Suppose we have the following HTML file:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test iframe download</title>
<script type="text/javascript">
var init = 0;
function download() {
document.getElementById("dload_frame").src = "http://example.com/dload.py";
}
function alert() {
if (init == 0) {
init = 1;
}
else {
document.getElementById("alert_span").innerHTML = "Got it!";
}
}
</script>
</head>
<body>
<span id="alert_span">Main content.</span><br/>
<input type="button" value="Download" id="btn" onclick="download()" />
<iframe id="dload_frame" src="http://404.com/404" onload="alert()"> </iframe>
</body>
</html>
Now, if the URL to which iframe's src is being rewritten to (in this case - "http://example.com/dload.py") returns HTML, no problem: the onload event fires, the span's contents are replaced, everybody's happy.
However, if the content type of the file returned by the URL is set to something that forces the browser to open the save file dialog, the iframe's onload event never fires.
Is there any workaround? Using iframes isn't necessary, the desired behavior is to launch a callback after the browser begins downloading the supplied file.
I have encountered the same problem as this:
Here is my work-around, please see if it works for you:
<script>
function download(){
var url = 'http://example.com/dload.py';
var htm = '<iframe src="' + url +'" onload="downloadComplete()"></iframe>';
document.getElementById('frameDiv').innerHTML = htm;
}
</script>
<div style="display:none" id="frameDiv">
</div>
<button onclick="download()">download file</button>
As far as I can remembered iframe's onload event fires only once.
Setting another value for src attribute will not cause the onload event to fire again.
I have the same problem, onLoad handler is only fire when the content change. If you download a file. If you delete HTTP header to print file content on iframe, the onload is correctly fire.
My solution after many different approaches to get this working across ff ie safari and chrome was not have a 2 step download.
the original JS request to create an iframe loads a src that would normally have loaded the pdf
However, the src now loads a page with yet another iframe inside of it, which now contains the url of the pdf.
in the html response I trigger the onload and also a catchall setTimeout funciton which calls my response on window.parent.window.handlerfunction which my onload on the top iframe would have been. The result is a PDF download and a trigger on the top level parent of the handler function that works across the browsers since it no longer relies on detecting an actual iframe load but rather relies on supported parent/child window relationships.
hope this helps someone who was also stuck
You can check iframe readyState property repeatedly after short time intervals until it gets completed.
function iframe_onload(iframe_id, js) {
var iframe = document.getElementById(iframe_id);
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
if (iframeDoc.readyState == 'complete') {
eval(js)
return;
}
window.setTimeout('iframe_onload("' + iframe_id + '",`' + js + '`);', 100);
}
You might need help of jquery for this, for instance you can do this:
$.get('http://example.com/dload.py',{},function(result){
$('alert_span').html(result);//or some content
});