I notice that when I do AJAX submission using Rails UJS form, the Javascript response will only be evaluated when the server return HTTP success (2xx) and do nothing if the server return status code 4xx or 5xx.
I want to modify this behavior so that Javascript response will be evaluated even if the server return HTTP error. I'm thinking about binding my form with the event ajax:error, but then I have to do this for every AJAX form I have within my application. Anyone has an idea how to do this globally?
One more question: can I check if the content type returned by server is Javascript via JQuery?
Thanks!
Since all Rails UJS handlers need to have data-remote="true" defined, you could bind to the ajax:error event globally by doing
$('[data-remote="true"]').on('ajax:error', my_special_error_callback);
Then you need to ensure that you define your callback to check on the error codes:
function my_special_error_callback(event, data, status, xhr) {
if(status == 'error') {
alert('error');
responseText = JSON.parse(data.responseText)["errors"] // This is dependent on how you're returning your errors from Rails
}
Related
Question about how to send a jQuery callback with an onSuccess: refresh from a textInput when the user presses [Enter]. We use the [Enter] press to trigger a search callback.
Our GS Seaside app uses HAProxy. This means the onSuccess: script is handled by a different gem than the one that handles the callback. Because of this, users will sometimes get the refresh because the callback, which to them looks like a lost input (a browser F5 refresh shows the right state). If running single gem or in a VW / Pharo image this problem does not come up.
I can work around the problem by using...
async: false;
...but that prevents me from show any kind of waiting feedback (I normally use a busy gif).
So, the question is: in a multi-web server configuration, how can you code a callback to...
1 - show a busy gif
2 - trigger the search callback
3 - refresh the display when done
...all in that order.
Using a form submission callback is a problem because multiple text inputs can trigger the search, since the callback is 'set value + do search', by virtual of the default [Enter] press.
For the JS callback, I'm using...
self onKeyPress: (
(JSStream
on: '(window.event ? window.event.keyCode : event.which) == 13')
then: (canvas jQuery ajax callback: aBlock value: canvas jQuery this value))
It all works fine, except for the missing busy gif, due to the 'async: false'.
Any suggestions?
You can define a beforeSend and a complete handler to show and hide the loading indicator while the request is being processed. The global parameter set to false is meant to ignore your existing handlers to process request start and end (the mentioned spinner), and only use these defined in the particular instance of the JQAjax object.
((html jQuery ajax)
async: false;
global: false; "https://api.jquery.com/category/ajax/global-ajax-event-handlers/"
callback: aBlock value: canvas jQuery this value;
onBeforeSend: (html jQuery id: 'indicator') show;
onSuccess: ((html jQuery id: 'fieldId') load html: [:h | ]);
onComplete: (html jQuery id: 'indicator') hide;
That said, keep in mind that doing a synchronous AJAX call is discouraged since it will block the whole UI thread until the request is resolved.
So it's not completely clear how you manage the state in different worker images (gems, in this case) returning different things (probably because of having different sessions), so it's also not clear to me why doing an async XHR request will be served differently to doing it synchronously, I never experienced that.
From the small sample you mention, it can't be deduced what is the "refresh" part of your code. So maybe, providing more context will help us give you more accurate answers.
Fix ended up being trivial: needed to include 'event.preventDefault();' in the [Enter] key script. Seems obvious in hindsight.
if ((window.event ? window.event.keyCode : event.which) == 13) {
event.preventDefault();
};'
This problem is confirmed to be a narrow configuration symptom: GemStone with multiple gems. On every other configuration Seaside / javascript behaves as expected. I will follow this up as a vendor problem. Thanks to everyone that looked at it (this was also posted on other forums).
I'm trying to get a report from SSRS Rest API. I can see it when I navigate to the URL
https://myPC:443/ReportService?%2fSSRS%2fPatientèle&rs:Command=Embed&rc:LinkTarget=main&Hospital=CHRU%20Strasbourg
in chrome
When I navigate there in the browser I can see my report.
So I've tried to get the HTML from a controller:
[HttpGet]
public async Task<string> GetReportAsHTML()
{
using (var client = new HttpClient())
{
using (var result = await client.GetAsync("http://myPC:80/ReportService?%2fSSRS%2fPatientèle&rs:Command=Embed&rc:LinkTarget=main&Hospital=CHRU%20Strasbourg"))
{
if (result.IsSuccessStatusCode)
{
return await result.Content.ReadAsStringAsync();
}
}
}
return "";
}
It's returning 401 unauthorized, and the statement in the if is never reached.
Can someone please explain how I can resolve this problem so I get the correct response?
EDIT
I tried both, I mean in http & https, and both return the report without authentification. Http access (http://localhost:80/...) was even better because charts aren't displayed, only tables. With https access, I get the following picture instead of the charts:
If you need to embed reports, you can also consider doing so in an <iframe> - just point its src to the report URL.
<iframe src="https://myPC:443/ReportService?%2fSSRS%2fPatientèle&rs:Command=Embed&rc:LinkTarget=main&Hospital=CHRU%20Strasbourg "></iframe>
The added benefits are:
iframes usually share cookies with their parent, so if the report server needs such authentication it may work immediately
if the report is somehow interactive (say, it actually returns an html page with filters, dropodwns and the like) - your end user would get that too instead of static HTML that might even break when taken out of its context
You may also want to look into ready-made Blazor report viewer components - perhaps your reporting solution vendor already has one.
It seems like url in your code do not match the one you use through Chrome? Could this be the issue?
I trying to upload a file using pug, multer and express.
The pug form looks like this
form(method='POST' enctype="multipart/form-data")
div.form-group
input#uploaddata.form-control(type='file', name='uploaddata' )
br
button.btn.btn-primary(type='submit' name='uploaddata') Upload
The server code looks like this (taken out of context)
.post('/uploaddata', function(req, res, next) {
upload.single('uploaddata',function(err) {
if(err){
throw err;
} else {
res.json({success : "File upload sucessfully.", status : 200});
}
});
})
My issue is that while the file uploads successfully, the success message is not shown on the same page, ie: a new page is loaded showing
{success : "File upload sucessfully.", status : 200}
As an example for other elements (link clicks) the message is displayed via such javascript:
$("#importdata").on('click', function(){
$.get( "/import", function( data ) {
$("#message").show().html(data['success']);
});
});
I tried doing a pure javascript in order to workaround the default form behaviour but no luck.
Your issue has to do with mixing form submissions and AJAX concepts. To be specific, you are submitting a form then returning a value appropriate to an AJAX API. You need to choose one or the other for this to work properly.
If you choose to submit this as a form you can't use res.json, you need to switch to res.render or res.redirect instead to render the page again. You are seeing exactly what you are telling node/express to do with res.json - JSON output. Rendering or redirecting is what you want to do here.
Here is the MDN primer on forms and also a tutorial specific to express.js.
Alternatively, if you choose to handle this with an AJAX API, you need to use jquery, fetch, axios, or similar in the browser to send the request and handle the response. This won't cause the page to reload, but you do need to handle the response somehow and modify the page, otherwise the user will just sit there wondering what has happened.
MDN has a great primer on AJAX that will help you get started there. If you are going down this path also make sure you read up on restful API design.
Neither one is inherently a better strategy, both methods are used in large-scale production applications. However, you do need to choose one or the other and not mix them as you have above.
I'm using durandaljs 2.0. I've installed the durandal starter-kit as suggested and explained here. In the shell I'm returning router.activate({ pushState: true } ); as explained in the relevant documentation (see the bottom of the page).
Happily, the URL is indeed in a 'push state' format, e.g. http://localhost:61285/flickr - the problem is that when I refresh the page I get a 404 error telling me "the resource cannot be found". If I set push-state to false ({pushState: false} above) I get a hashed URL, e.g. http://localhost:61285/#flickr - and now a refresh does work. So, how do I set up a push state mode in durandaljs 2.0 that will work with refresh?
Thanks,
Elior
Maybe to late...but
just change the routes config.
simple as this :
routes.MapRoute(
name: "Default",
url: "{*url}",
defaults: new { controller = "Home", action = "Index" }
);
When you refresh the page, the browser will make a request to the server with that URL http://localhost:61285/flickr.
What's probably happening is that if you are using ASP.NET MVC, the server is trying to locate a Controller called flickr and it throws an exception because obviously there isn't any resource with that name.
In order to get rid of this exception you should configure the server to serve the same HTML of the APP but for unknown URL's. This can be achieved using IIS URL Rewrite in ASP.NET.
So after setting up properly the server, by requesting an unknown URL it would return the initial view for the app plus whatever you pass in the query string parameters so the router can do its job at client side.
In this blog post you will find more information about how to configure ASP.NET to handle this scenarios. In the article the author uses AngularJS, however it will be the same for Durandal.
RainerAtSpirit and margabit, you're both right, thank you. Here is how I implemented the server side:
First I should note that all the interaction with the server is done via WebApi controllers.
so, for example, if the URL is:
http://localhost:61285/home/category2/subCategory22 (for a localhost), the server tries to look for a controller called 'home' and an action in it called 'category2'. Since there's no such action, I get a 404 error.
What I wanted is that the server WILL call the 'home' controller, but send the rest of the URL as parameters to the client. My solution was to add a hash after the controller's name, so that the URL will look like this: http://localhost:61285/home/#/category2/subCategory22. If this would happen then the client will take care of the hashed part with no 404 error.
For this to happen:
I added the following to 'web.config':
<customErrors mode="On" defaultRedirect="Error">
<error statusCode="404" redirect="Error" />
</customErrors>
Then I create a controller named 'ErrorController' with the following class in it:
public class ErrorController : ApiController
{
[HttpGet, HttpPost, HttpPut, HttpDelete, HttpHead, HttpOptions, AcceptVerbs("PATCH"), AllowAnonymous]
public HttpResponseMessage Handle404()
{
string [] parts = Request.RequestUri.OriginalString.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
string parameters = parts[ 1 ].Replace("aspxerrorpath=","");
var response = Request.CreateResponse(HttpStatusCode.Redirect);
response.Headers.Location = new Uri(parts[0].Replace("Error","") + string.Format("#{0}", parameters));
return response;
}
}
what happens is that when the server get a URL with no relevant action as I mentioned above, it redirects it to this controller in the following format: http://localhost:61285/Error?aspxerrorpath=home/category2/subCategory22
as you can see, I manipulate this to add the hash and remove the unnecessary info: http://localhost:61285/home/#/category2/subCategory22 and the redirect the server to the 'home' controller.
You might wonder why I do all of this - the reason is that Durandal, a wonderful platform, enables me to use push state, but in order for that to happen I have to work-around the server getting a non-hashed URL and pass it to the client despite the fact there's no relevant controller/action; Durandal get's a hashed URL but removes the hash automatically and the user eventually sees a hash-less URL while Durandal provides all the necessary push state functionality.
Elior
I have a unique problem with passing an action message in a JSON result.
Right now, if I add an action message within my action (in a JSON action), I will pick that up in JavaScript and capture the action message and alert the user via JGrowl. However, I don't want the logic of adding an action message in each action. I have an underlying service project with a request context that is shared among the request, and I am able to add warning messages there. What I'd like to do is to transform those warning messages to action messages for use on my front end, but the action will never have to be aware of them. This is useful because I can insert warnings when accessing databases, or if there are hairy business rules, etc.
As I mentioned before, it already works when adding them directly in the action so I know the JSON result works fine and passes along the action messages correctly. We have an interceptor that is hit every time for managing this request context already, so I'd like to append on the action messages in this interceptor to the action being called.
However, the problem I'm finding is that I need to call actionInvocation.invoke() first as any warning messages will be generated as a result of that. After that, I check for the messages and attempt to apply them as action messages. These action messages never show up in my JSON response, so I'm wondering if it's possible to add those messages into the response in the interceptor AFTER the invoke() call. Here's the bulk of my intercept method:
try {
// Invoke the action.
String result = actionInvocation.invoke();
//add all warning messages as an action message to be displayed on that front end
if (CollectionUtils.isNotEmpty(context.getWarningMessages())) {
ActionSupport action = (ActionSupport) actionInvocation.getAction();
for (String s : context.getWarningMessages()) {
action.addActionError(s);
}
}
return result;
I tried adding the logic to add the action messages in the finally block instead of after the call to invoke() to no avail.
Thanks,
Andy
The result has been rendered by the time invoke returns.
You need to implement a PreResultListener as discussed in the "Writing Interceptors" docs.