I want to open a FolderBrowseDialog on button click in MVC razor(VB) syntax.
For that i am calling a jquery function on "onclick" button event and through that function i am making a Post request to function in my controller that contains code to show FolderBrowseDialog.
here is my code.
Html:
<input type="button" class="btn" value="browse" onclick="SelectFolder()"/>
jquery:
<script type="text/javascript">
function SelectFolder()
{
$.post("#Url.Action("FolderPicker", "Home")", function () {
alert('sdd');
},function(ex){
alert("Error occured in AJAX");
});
}
</script>
Controller.. vb code to show FolderBrowserDialog.
<STAThreadAttribute()>
Sub FolderPicker()
Dim browser As FolderBrowserDialog = New FolderBrowserDialog()
browser.Description = "Select Folder"
browser.ShowNewFolderButton = False
browser.RootFolder = Environment.SpecialFolder.Desktop
Dim result As DialogResult = browser.ShowDialog()
If result = DialogResult.OK Then
Dim selectedPath As String = browser.SelectedPath
End If
End Sub
At
Dim result As DialogResult = browser.ShowDialog()
i am getting exception
Current thread must be set to single thread apartment (STA) mode before OLE
calls can be made. Ensure that your Main function has STAThreadAttribute
marked on it. This exception is only raised if a debugger is attached to the
process.
I also included STATreadAttribute() and STATread() but still i get this error.
Am i missing some thing?
Is there any other way to do it?
FolderBrowseDialog is not available in ASP.NET/MVC.
You can read some more stuff here.
the STATreadAttribute should be on the Main function of your program,meaning the entry point of the program
Related
My JS work only if i reload page.
Description my problems
1.I have book
2.Book have description
3.In action show i have logic for present description
Logic:
1. If description.length > 250 i show 250 symbols and show view_more button
2. If user click on button then my js must work
4.When i on books#index => choose some book(#show)=> now i on book_page and i see view_button => when i click => nothing was happen
5. But if i reload page before click on button => my js work fine
My js
(function(window, document, undefined){
window.onload = init;
function init(){
let btn_view_more = document.getElementById('button_for_view_more')
let description_all = document.getElementById('book_description_all')
let description_short = document.getElementById('book_description_short')
btn_view_more.addEventListener('click', ()=>{
description_all.classList.remove('hide_description')
description_short.style.display = 'none'
});
};
})(window, document, undefined);
But i have new version of my js
document.addEventListener("turbolinks:load", function() {
myFunc();
})
function myFunc(){
let btn_view_more = document.getElementById('button_for_view_more')
let description_all = document.getElementById('book_description_all')
let description_short = document.getElementById('book_description_short')
btn_view_more.addEventListener('click', ()=>{
description_all.classList.remove('hide_description')
description_short.style.display = 'none'
});
}
New version work fine even i don't reload page, but i have error in my console
Cannot read properties of null (reading 'addEventListener') (can't find id button_for_view_more)
What i want:
1. Js work even i don't reload page
2. Zero error in console
My solution is connect js only to the page where it is used, delete from javascript/some.js and drop require from pack/application.js. You can also add a condition to your script that will check whether such an ID is currently on the page. And only after run the main block.
When I use the built-in Google+ sign-in button, everything works as expected. The OAuth call to Google is made in the popup, the user accepts or cancels, then the callback is called.
When I try to customize my button using the example gapi.signin.render method, the Google call is made but the callback is called immediately.
I am a server-side developer trying to provide a POC for the front-end developers. I only know enough Javascript to be dangerous. Can someone tell me why the gapi.signin.render method is making an asynchronous call to the authorization, which makes the callback get called before the user has clicked anything in the popup? In the alternative, please help me correct the code in the 2nd example below to effect the callback being called only after the user clicks Accept/Cancel in the OAuth Google window. In the second alternative, please tell me how I can change the text of the built-in Google+ sign-in button.
The code that works (built-in, non-customizable Google+ sign-in button):
<SCRIPT TYPE="text/javascript">
/**
* Asynchronously load the Google Javascript file.
*/
(
function() {
var po = document.createElement( 'script' );
po.type = 'text/javascript';
po.async = true;
po.src = 'https://apis.google.com/js/client:plusone.js?onload=googleLoginCallback';
var s = document.getElementsByTagName('script')[ 0 ];
s.parentNode.insertBefore( po, s );
}
)();
function googleLoginCallback( authResult ) {
alert( "googleLoginCallback(authResult): Inside." );
}
</SCRIPT>
<DIV ID="googleLoginButton" CLASS="show">
<DIV
CLASS="g-signin"
data-accesstype="online"
data-approvalprompt="auto"
data-callback="googleLoginCallback"
data-clientid="[Google Client Id].apps.googleusercontent.com"
data-cookiepolicy="single_host_origin"
data-height="tall"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-scope="https://www.googleapis.com/auth/userinfo.email"
data-theme="dark"
data-width="standard">
</DIV>
</DIV>
The gapi.signin.render code that does not work:
<SCRIPT TYPE="text/javascript">
/**
* Asynchronously load the Google Javascript file.
*/
(
function() {
var po = document.createElement( 'script' );
po.type = 'text/javascript';
po.async = true;
po.src = 'https://apis.google.com/js/client:plusone.js?onload=myGoogleButtonRender';
var s = document.getElementsByTagName('script')[ 0 ];
s.parentNode.insertBefore( po, s );
}
)();
function myGoogleButtonRender( authResult ) {
gapi.signin.render( 'myGoogleButton', {
'accesstype': 'online',
'approvalprompt': 'auto',
'callback': 'googleLoginCallback',
'clientid': '[Google Client Id].apps.googleusercontent.com',
'cookiepolicy': 'single_host_origin',
'height': 'tall',
'requestvisibleactions': 'http://schemas.google.com/AddActivity',
'scope': 'https://www.googleapis.com/auth/userinfo.email',
'theme': 'dark',
'width': 'standard'
});
}
function googleLoginCallback( authResult ) {
alert( "googleLoginCallback(authResult): Inside." );
}
</SCRIPT>
<button id="myGoogleButton">Register with Google+</button>
I figured out why the code was not working for a custom button. I had the button defined within a Struts 2 form. Apparently, in lieu of the traditional Chain of Responsibility pattern, where the click event is handled by one processor, both the Struts form and the Google API were processing the click. So, what I thought was a failure of the Google gapi.signin.render call making an asynchronous call to the callback, it was the Struts form trying to submit.
To fix it, you can:
Move the button outside of the Struts form (not very elegant)
Add "onclick="return false;" clause to the button
<button id="myGoogleButton" onclick="return false;">Register with Google+</button>
Wrap the "button" in a DIV like:
<DIV ID="myGoogleButton">
<SPAN CLASS="zocial googleplus">Register with Google+</SPAN>
</DIV>
I hope this fixes someone else's problem. I spent 9 days trying to figure this out.
What I am looking to do is:
1) From an MVC View, Start a long running Process. In my case, this process is a seperate Console Application being executed. The Console Application runs for potentially 30 minutes and regurlarily Console.Write's its current actions.
2) Back on the MVC View, periodically poll the server to retrieve the latest Standard Out which I have redirected to a Stream (or anywhere I can get access to it for that matter). I'll append newly retieved standard output to a log textbox or something equivalent.
Sounds relativly easy. My client side programming is a bit rusty though and I'm having issues with the actual streaming. I would assume this is not an uncommon task. Anyone got a decent solution for it in ASP.NET MVC?
Biggest issue seems to be that I cant get the StandardOutput until the end of execution, but I was able to get it with an event handler. Of course, using the event handler seems to lose focus of my output.
This is what I was working with so far...
public ActionResult ProcessImport()
{
// Get the file path of your Application (exe)
var importApplicationFilePath = ConfigurationManager.AppSettings["ImportApplicationFilePath"];
var info = new ProcessStartInfo
{
FileName = importApplicationFilePath,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false
};
_process = Process.Start(info);
_process.BeginOutputReadLine();
_process.OutputDataReceived += new DataReceivedEventHandler(_process_OutputDataReceived);
_process.WaitForExit(1);
Session["pid"] = _process.Id;
return Json(new { success = true }, JsonRequestBehavior.AllowGet);
}
void _process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
_importStandardOutputBuilder.Insert(0, e.Data);
}
public ActionResult Update()
{
//var pid = (int)Session["pid"];
//_process = Process.GetProcessById(pid);
var newOutput = _importStandardOutputBuilder.ToString();
_importStandardOutputBuilder.Clear();
//return View("Index", new { Text = _process.StandardOutput.ReadToEnd() });
return Json(new { output = newOutput }, "text/html");
}
I haven't written the client code yet as I am just hitting the URL to test the Actions, but I'm also interested how you would approach polling for this text. If you could provide the actual code for this too, it would be great. I would assume you'd have a js loop running after kicking off the process that would use ajax calls to the server which returns JSON results... but again, its not my forte so would love to see how its done.
Thanks!
Right, so from the couple of suggestions I received and a lot of trial and error I have come up with a work in progress solution and thought I should share with you all. There are definitely potential issues with it at the moment, as it relies on static variables shared across the website, but for my requirement it does the job well. Here goes!
Let's start off with my view. We start off by binding the click event of my button with some jquery which does a post to /Upload/ProcessImport (Upload being my MVC Controller and ProcessImport being my MVC Action). Process Import kicks off my process which I will detail below. The js then waits a short time (using setTimeout) before calling the js function getMessages.
So getMessages gets called after the button is clicked and it does a post to /Upload/Update (my Update action). The Update action basically retrieves the status of the Process and returns it as well as the StandardOutput since last time Update was called. getMessages will then parse the JSON result and append the StandardOutput to a list in my view. I also try to scroll to the bottom of the list, but that doesn't work perfectly. Finally, getMessages checks whether the process has finished, and if it hasn't it will recursivly call itself every second until it has.
<script type="text/javascript">
function getMessages() {
$.post("/Upload/Update", null, function (data, s) {
if (data) {
var obj = jQuery.parseJSON(data);
$("#processOutputList").append('<li>' + obj.message + '</li>');
$('#processOutputList').animate({
scrollTop: $('#processOutputList').get(0).scrollHeight
}, 500);
}
// Recurivly call itself until process finishes
if (!obj.processExited) {
setTimeout(function () {
getMessages();
}, 1000)
}
});
}
$(document).ready(function () {
// bind importButton click to run import and then poll for messages
$('#importButton').bind('click', function () {
// Call ProcessImport
$.post("/Upload/ProcessImport", {}, function () { });
// TODO: disable inputs
// Run's getMessages after waiting the specified time
setTimeout(function () {
getMessages();
}, 500)
});
});
</script>
<h2>Upload</h2>
<p style="padding: 20px;">
Description of the upload process and any warnings or important information here.
</p>
<div style="padding: 20px;">
<div id="importButton" class="qq-upload-button">Process files</div>
<div id="processOutput">
<ul id="processOutputList"
style="list-style-type: none; margin: 20px 0px 10px 0px; max-height: 500px; min-height: 500px; overflow: auto;">
</ul>
</div>
</div>
The Controller. I chose not to go with an AsyncController, mainly because I found I didn't need to. My original issue was piping the StdOut of my Console application to the view. I found couldn't ReadToEnd of the standard out, so instead hooked the event handler ProcessOutputDataReceived up which gets fired when standard out data is recieved and then using a StringBuilder, append the output to previously received output. The issue with this approach was that the Controller gets reinstantiated every post and to overcome this I decided to make the Process and the StringBuilder static for the application. This allows me to then receive a call to the Update Action, grab the static StringBuilder and effectivly flush its contents back to my view. I also send back to the view a boolean indicating whether the process has exited or not, so that the view can stop polling when it knows this. Also, being static I tried to ensure that if an import in in progress, don't allow other's to begin.
public class UploadController : Controller
{
private static Process _process;
private static StringBuilder _importStandardOutputBuilder;
public UploadController()
{
if(_importStandardOutputBuilder == null)
_importStandardOutputBuilder = new StringBuilder();
}
public ActionResult Index()
{
ViewData["Title"] = "Upload";
return View("UploadView");
}
//[HttpPost]
public ActionResult ProcessImport()
{
// Validate that process is not running
if (_process != null && !_process.HasExited)
return Json(new { success = false, message = "An Import Process is already in progress. Only one Import can occur at any one time." }, "text/html");
// Get the file path of your Application (exe)
var importApplicationFilePath = ConfigurationManager.AppSettings["ImportApplicationFilePath"];
var info = new ProcessStartInfo
{
FileName = importApplicationFilePath,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false
};
_process = Process.Start(info);
_process.BeginOutputReadLine();
_process.OutputDataReceived += ProcessOutputDataReceived;
_process.WaitForExit(1);
return Json(new { success = true }, JsonRequestBehavior.AllowGet);
}
static void ProcessOutputDataReceived(object sender, DataReceivedEventArgs e)
{
_importStandardOutputBuilder.Append(String.Format("{0}{1}", e.Data, "</br>"));
}
public ActionResult Update()
{
var newOutput = _importStandardOutputBuilder.ToString();
_importStandardOutputBuilder.Clear();
return Json(new { message = newOutput, processExited = _process.HasExited }, "text/html");
}
}
Well, that's it so far. It works. It still needs work, so hopefully I'll update this solution when I perfect mine. What are your thoughts on the static approach (assuming the business rule is that only one import can occur at any one time)?
Look into long poll. Basically you can open an ajax request and then hold onto it inside the controller.
Sample of long poll
This is something that you will want to do Async or you will possibly have issues with thread starvation.
Consider writing a service that runs on a server somewhere and pipes its output to a file/db accessible by your web server. Then you can just load the generated data in your website and returning them to your caller.
Understand that tying up your web server's threads for extended periods of time can result in thread starvation and make it look like your website has crashed (even though it's acutally just busy waiting for your console app to run).
The thing that confuses me somewhat and it's probably due to the conventions in
the jquery ajax() request .post() function is that it does not indicate anywhere that if request is successful that it should call the handleUpdate() function which gets the returned json object via "var json = context.get_data();", also why is the whole chunk of code starting with "if (data.ItemCount == 0)" in the handleUpdate() identical to the one in the .post() on success run > function (data) { duplicate code } .
Maybe because function (data) {} is callback function it waits for the entire request/response cycle to finish and that includes "var json = context.get_data();" in handleUpdate() ?
Thanks..
Pasted from the tutorial PDF, no other jscript in this view.
<script type="text/javascript">
$(function () {
// Document.ready -> link up remove event handler
$(".RemoveLink").click(function () {
// Get the id from the link
var recordToDelete = $(this).attr("data-id");
if (recordToDelete != '')
{
// Perform the ajax post
$.post("/ShoppingCart/RemoveFromCart", { "id": recordToDelete },
function (data) {
// Successful requests get here
// Update the page elements
if (data.ItemCount == 0)
{
$('#row-' + data.DeleteId).fadeOut('slow');
}
else
{
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
});
}
});
});
function handleUpdate()
{
// Load and deserialize the returned JSON data
var json = context.get_data();
var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);
// Update the page elements
if (data.ItemCount == 0)
{
$('#row-' + data.DeleteId).fadeOut('slow');
}
else
{
$('#item-count-' + data.DeleteId).text(data.ItemCount);
}
$('#cart-total').text(data.CartTotal);
$('#update-message').text(data.Message);
$('#cart-status').text('Cart (' + data.CartCount + ')');
}
</script>
The handleUpdate() function is a relic from the previous MVC2 version of the tutorial where the Ajax for removing items from the cart was handled by Microsoft's Ajax called via an Ajax.ActionLink helper. (see below)
This was changed to use JQuery Ajax in the MVC3 version of this tutorial but the handleUpdate() code has been left in it seems by mistake during the conversion from MVC2 to MVC3.
<script src="/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="/Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="/Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
function handleUpdate(context) {
// Load and deserialize the returned JSON data
var json = context.get_data();
var data = Sys.Serialization.JavaScriptSerializer.deserialize(json);
// Update the page elements
$('#row-' + data.DeleteId).fadeOut('slow');
$('#cart-status').text('Cart (' + data.CartCount + ')');
$('#update-message').text(data.Message);
$('#cart-total').text(data.CartTotal);
}
</script>
...
<%: Ajax.ActionLink("Remove from cart", "RemoveFromCart",
new { id = item.RecordId },
new AjaxOptions { OnSuccess = "handleUpdate" })%>
There is no way (according to this code) that handleUpdate is being called on success of $.post. Jquery post function has following syntax
$.post(url,data, callback);
and in the code you can see that all three parameters are explicitly specified and callback is an anonymous function with signature
function(data){}
Now, what you can see is that this anonymous function and handleUpdate are doing exactly the same logic. That makes me believe that they belong to the two different scenarios. For example, first scenario is that links are rendered using
Html.ActionLink(LinkText, ActionName, new{#class = "RemoveLink"})
In this case click event is handled by jquery function on the top and all the logic is done in this function (including ajax and callback). Second function might have been used for some
//please confirm all parameters of the function
Ajax.ActionLink(LinkText, ActionName, new AjaxOptions{onSuccess = "handleUpdate"});
and this seems to be connected with microsoftmvc ajax files that that used to exist in ancient times. You can put alert in each function and check what is the case with you.
Im trying to create an ajax (post) event that will populate a table in a div on button click.
I have a list of groups, when you click on a group, I would like the table to "disappear" and the members that belong to that group to "appear".
My problem comes up when using jQuery's .ajax...
When I click on the button, it is looking for a controller that doesnt exist, and a controller that is NOT referenced. I am, however, using AREAS (MVC2), and the area is named Member_Select where the controller is named MemberSelect. When I click on the button, I get a 404 stating it cannot find the controller Member_Select. I have examined the link button and it is set to Member_Select when clicked on, but here's the ajax call:
$.ajax({
type: "POST",
url: '/MemberSelect/GetMembersFromGroup',
success: function(html) { $("#groupResults").html(html); }
});
I havent been able to find any examples/help online.
Any thoughts/suggestions/hints would be greatly appreciated.
Thanks!
Have you tried navigating to /MemberSelect/GetMembersFromGroup to see what you get? - if it's 404'ing it's because the route can't be matched to a controller/ action.
I've not used the new areas functionality, but I'm not sure that the URL you've got is correct...I would have thought it would have been /AREANAME/MemberSelect/GetMembersFromGroup...but I could be wrong..!
When I did this, it worked fine. I didn't use POST and I don't know what AREAS means.
$("#item").autocomplete({
source: function(req, responseFn) {
addMessage("search on: '" + req.term + "'<br/>", true);
$.ajax({
url : ajaxUrlBase1 + "GetMatchedCities/" + req.term,
cache : false,
type : "GET", // http method
success : function(msg){
// ajax call has returned
var result = msg;
var a = [];
if (result !== null){
for(var i=0; i < result.length; i++) {
a.push({label: result[i].prop1, id: result[i].prop2});
}
}
responseFn(a);
}
});
}
});
Use:
area_name/controller_name/action_name
Instead of doing $.ajax I would use jQuery Form Plugin.
and have my form set as:
Html.BeginForm("Index","AdminArea/Admin",FormMethod.Post,
new { id="form-user", name="form-user"})
To use jQuery Form Plugin have a look here:
http://arturito.net/2010/12/02/asp-net-mvc2-jquery-form-post-tutorial/
You cold save your url in a Hidden Form element in (Html.HiddenForm()) and use the #id javascript operator to retrieve it. Just found this out today.