Alternatives to using a submit button with jQuery - javascript

Currently I am using a ajax post back to pass data to a my controllers, wherein I do my magic from there. The problem I am having right now is that we have removed a submit button and instead attached a handler using .keyup(), on a smaller database this works great! However when moving it to a larger database, as you might of guessed, it causes all sorts of issues, from delayed responses to crashes etc. So it won't work. My question to stack overflow would but this: What's a user-friendly version to submit a form?
Since the front end user could potentially be searching for single character values (e.g 6 as in userid 6) I can't limit it to a minimum character submission. We don't want a delay timer as that could back fire in a couple different ways. so basically I've ruled out .delay(), .blur(), and .keyup().

If your requirement is to not use a submit button but rather process the input as the user types then I have used solutions like this in the past.
First define a function to manage the delay, something like this:
var inputDelay = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
Then for your actual ajax-call do something like this in your onkeyup event:
inputDelay(function () {
//AJAX-call goes here
}, 400)
Please note that this is not a universal solution for handling the order of responses from the server though. You might very well get several AJAX requests sent to the server and this code does nothing to make sure you only handle the latest call.

The best way to handle your situation would be to use a form, complete with a submit button. You would then add your event listener to the submit event of the form, cancel the post-back, and then do your processing. This would handle both the user clicking the submit button as well as pressing the enter key from inside the form.

I had the same issue as you.. What I did is the following:
I used on keyup.. and I fire the search if the user stayed still for .. say 300 millisecond or 500 millisecond..
While the user is typing I clear and reset the setTimeout().. so if the user stayed still for the timeout time the search will fire.
Additionally you can take the reference of the xHR object and cancel it if another request was to take place..
Do I understand your question correctly? if you need a written example I can write you a fiddle.

Related

MEAN / AngularJS app check if object already posted

I have thig angularJS frontend and I use express, node and mongo on the backend.
My situation looks like:
//my data to push on server
$scope.things = [{title:"title", other proprieties}, {title:"title", other proprieties}, {title:"title", other proprieties}]
$scope.update = function() {
$scope.things.forEach(function(t) {
Thing.create({
title: t.title,
//other values here
}, function() {
console.log('Thing added');
})
})
};
//where Thing.create its just an $http.post factory
The HTML part looks like:
//html part
<button ng-click="update()">Update Thing</button>
Then on the same page the user has the ability to change the $scope.things and my problem is that when I call update() again all the things are posted twice because literally thats what I'm doing.
Can someone explain me how to check if the 'thing' its already posted to the server just to update the values ($http.put) and if its not posted on server to $http.post.
Or maybe its other way to do this?
I see a few decisions to be made:
1) Should you send the request after the user clicks the "Update" button (like you're currently doing)? Or should you send the request when the user changes the Thing (using ngChange)?
2) If going with the button approach for (1), should you send a request for each Thing (like you're currently doing), or should you first check to see if the Thing has been updated/newly created on the front end.
3) How can you deal with the fact that some Thing's are newly created and others are simply updated? Multiple routes? If so, then how do you know which route to send the request to? Same route? How?
1
To me, the upside of using the "Update" button seems to be that it's clear to the user how it works. By clicking "Update" (and maybe seeing a flash message afterwards), the user knows (and gets visual feedback) that the Thing's have been updated.
The cost to using the "Update" button is that there might be unnecessary requests being made. Network communication is slow, so if you have a lot of Thing's, having a request being made for each Thing could be notably slow.
Ultimately, this seems to be a UX vs. speed decision to me. It depends on the situation and goals, but personally I'd lean towards the "Update" button.
2
The trade-off here seems to be between code simplicity and performance. The simpler solution would just be to make a request for each Thing regardless of whether it has been updated/newly created (for the Thing's that previously existed and haven't changed, no harm will be done - they simply won't get changed).
The more complex but more performant approach would be to keep track of whether or not a Thing has been updated/newly created. You could add a flag called dirty to Thing's to keep track of this.
When a user clicks to create a new Thing, the new Thing would be given a flag of dirty: true.
When you query to get all things from the database, they all should have dirty: false (whether or not you want to store the dirty property on the database or simply append it on the server/front end is up to you).
When a user changes an existing Thing, the dirty property would be set to true.
Then, using the dirty property you could only make requests for the Thing's that are dirty:
$scope.things.forEach(function(thing) {
if (thing.dirty) {
// make request
}
});
The right solution depends on the specifics of your situation, but I tend to err on the side of code simplicity over performance.
3
If you're using Mongoose, the default behavior is to add an _id field to created documents (it's also the default behavior as MongoDB itself as well). So if you haven't overridden this default behavior, and if you aren't explicitly preventing this _id field from being sent back to the client, it should exist for Thing's that have been previously created, thus allow you to distinguish them from newly created Thing's (because newly created Thing's won't have the _id field).
With this, you can conditionally call create or update like so:
$scope.things.forEach(function(thing) {
if (thing._id) {
Thing.update(thing._id, thing);
}
else {
Thing.create(thing);
}
});
Alternatively, you could use a single route that performs "create or update" for you. You can do this by setting { upsert: true } in your update call.
In general, upsert will check to see if a document matches the query criteria... if there's a match, it updates it, if not, it creates it. In your situation, you could probably use upsert in the context of Mongoose's findByIdAndUpdate like so:
Thing.findByIdAndUpdate(id, newThing, { upsert: true }, function(err, doc) {
...
});
See this SO post.
#Adam Zemer neatly addressed concerns I raised in a comment, however I disagree on some points.
Firstly, to answer the question of having an update button or not, you have to ask yourself. Is there any reason why the user would like to discard his changes and not save the work he did. If the answer is no, then it is clear to me that the update should not be place and here is why.
To avoid your user from loosing his work you would need to add confirmations if he attempts to change the page, or close his browser, etc. On the other if everything is continuously saved he has the peace of mind that his work is always saved and you dont have to implement anything to prevent him from loosing his work.
You reduce his workload, one less click for a task may seem insignificant but he might click it many time be sure to have his work save. Also, if its a recurrent tasks it will definitely improve his experience.
Performance wise and code readability wise, you do small requests and do not have to implement any complicated logic to do so. Simple ng-change on inputs.
To make it clear to him that his work is continuously save you can simply say somewhere all your changes are saved and change this to saving changes... when you make a request. For exemple uses, look at office online or google docs.
Then all you would have to do is use the upsert parameter on your mongoDB query to be able to create and update your things with a single request. Here is how your controller would look.
$scope.update = function(changedThing) { // Using the ng-change you send the thing itself in parammeter
var $scope.saving = true; // To display the saving... message
Thing.update({ // This service call your method that update with upsert
title: changedThing.title,
//other values here
}).then( // If you made an http request, I suppose it returns a promise.
function success() {
$scope.saving = false;
console.log('Thing added');
},
function error() {
//handle errors
})
};

Continually check for an Oracle record at page load

I basically have a page that when loads, reads an Oracle SQL table for a specific record id that may not currently exist at the point as it may take up to a minute to insert this specific record into the table.
Based on this, I need a means of showing a "Loading Image" while it waits for the record to exist, so has to wait. Once it does, I want to remove the loading image and present the user with the record details. I am using Oracle Application Express 4.2 for this.
My question is not so much the loading/hiding of the image but how to continually check for the record within the Oracle table, during page load.
Either I receive the record successfully and then hide the image or say after 1 minute, I dismiss the checking of the record and present the user with a message indicating that no record was found.
Sorry for my english. I will try help you.
Make your "Loading image" always visible on the page. There is no need to show it on load, you only need to hide it at proper moment.
Add Application Process to your application. Name it for example "GET_MY_ROW". Process must check your event, and return some flag, for example 1 or 0.
Example:
declare
l_cnt number;
begin
select count(*)
into l_cnt
from table1 t
where id = 12345;
if l_cnt > 0 then
htp.p(1);
else
htp.p(0);
end if;
end;
3.3 Add javascript code as page load event (for example by Dynamic Actions):
Javascript code:
var myInterval = setInteral(function {
var get = new htmldb_Get(null,$v('pFlowId'),'APPLICATION_PROCESS=GET_MY_ROW',$v('pFlowStepId'));
get.GetAsync(function(pRequest) {
if (pRequest.readyState == 4) {
if (pRequest.responseText == 1) {
alert('Record loaded successfully');
// add function call, hiding your "Loading image" here
clearInterval(myInterval);
}
};
});
get = null;
}, 5000); //check every 5 seconds
setTimeout(function() {
alert('Sorry, no record was found. Try again later.');
clearInterval(myInterval);
}, 60000); // fail after 1 minute
Since NoGotnu already answered, I'll put this here:
Is there any reason for the procedure to be called through a job? Is it the only way to create the required record? Is the job called anywhere else? Why not call the procedure directly when the required page has been submitted and show the loading icon there? When it finishes, the user knows it has finished. That would involve a lot less fiddling around as you can make apex show a processing graphic on page submit. You could then just inform the user on the other page that the process has not been ran yet and they'd have to do that first.
Secondly, while NoGotnu's answer will work, I'd like to point out that in apex 4.2 you should use the apex.server namespace instead of the never documented htmldb_Get construction. apex.server.process is a clean implementation of the jQuery ajax setup.
NoGotnu's code translated:
apex.server.process( "GET_MY_ROW"
, null
, { dataType: text
, success: function(pData){
if (pData == 1) {
clearInterval(myInterval);
alert('Record loaded successfully');
};
}
}
);
The call doesn't really need to be async though, but ok.
Another option would be to implement a "long poll" instead of firing the ajax event every 5 seconds. A long poll will just initiate a call to the server and wait for a response. As long as the server is busy, the client will wait. To achieve this you could use dbms_alert, as suggested in Waiting for a submitted job to finish in Oracle PL/SQL?
You'd signal an alert in the plsql code of the job, and in the ondemand process code register an interest in the alert and use waitone/any with a 60 second timeout. Presto long poll.

Filling log in form with zombie in node.js

Evening! I'm trying to log in into a website with zombie.js, but I don't seem to be able to make it work.
Oh and the website is in Finnish, but it's not very hard to understand, two text fields and a button. First is for username, second for password and the button is the log in button.
At the moment my log in code is as follows:
var Browser = require("zombie");
browser = new Browser();
browser.visit("https://www.nordnet.fi/mux/login/startFI.html?cmpi=start-loggain",
function () {
// Here I check the title of the page I'm on.
console.log(browser.text("title"));
// Here I fill the needed information.
browser.document.getElementById("input1").value ="MYUSERNAME";
browser.document.getElementById("pContent").value ="MYPASSWORD";
// And here it fails. I try to submit the form in question.
browser.document.getElementById("loginForm").submit();
setTimeout(function () {
// This is here to check that we've submitted the info and have been
// redirected to a new website.
console.log(browser.text("title"));
}, 2000);
});
Now I know that I maybe should have used zombie's own "fill" method, but I tried that with no luck so I tried something new.
All I get from this is an error:
Y:\IMC\Development\Web\node_modules\zombie\lib\zombie\forms.js:72
return history._submit(_this.getAttribute("action"), _this.getAttribute(
^
TypeError: Cannot call method '_submit' of undefined
Now if I log that browser.document.getElementById("loginForm") it clearly does find the form, but alas, it doesn't like it for some reason.
I also tried the "conventional" method with zombie, which is using that log in button on the web page and pressing it. The problem is that it's not actually a button, just an image which has a link attached to it, and it's all inside <span>. And I have no idea how I can "click" that button.
It has no ID on it, so I can't use that, then I tried to use the text on it, but because it has umlauts on it I can't get it to work. Escaping the ä with /344 only gave an error:
throw new Error("No BUTTON '" + selector + "'");
^
Error: No BUTTON 'Kirjaudu sisään'
So yeah, that didn't work, though I have no idea why it doesn't recognize the escaped umlaut correctly.
This is my first question, the second one is a minor one, but I though why not ask it here too now that I've written this text.
If I get all this to work, can I somehow copy the cookie that this log in gives me, and use that in my YQL for screen scraping? Basically I'm trying to scrape stock market values, but without the log in the values are 15min deferred, which isn't too bad, but I'd like it to be live anyhow.
After couple of tests using zombie I came to the conclusion that it's still to early to use it for serious testing. Nevertheless, I came up with working example of form submit (using regular .submit() method).
var Browser = require("zombie");
var assert = require("assert");
browser = new Browser()
browser.visit("http://duckduckgo.com/", function () {
// fill search query field with value "zombie"
browser.fill('input[name=q]', 'mouse');
// **how** you find a form element is irrelevant - you can use id, selector, anything you want
// in this case it was easiest to just use built in forms collection - fire submit on element found
browser.document.forms[0].submit();
// wait for new page to be loaded then fire callback function
browser.wait().then(function() {
// just dump some debug data to see if we're on the right page
console.log(browser.dump());
})
});
As you can see, the clue is to use construct browser.wait().then(...) after submitting the form, otherwise browser object will still refer to the initial page (the one passed as an argument to visit method). Note: history object will contain address of page you submitted your form to even if you don't wait for the page to load - it confused me for a bit, as I was sure that I should already see the new page.
Edit:
For your site, the zombie seems to be working ok (I could submit the form and get "wrong login or password" alert). There are some JS errors but zombie isn't concerned with them (you should debug those however to see if the script are working ok for regular users). Anyhow, here's the script I used:
var Browser = require("zombie");
var assert = require("assert");
browser = new Browser()
browser.visit("https://www.nordnet.fi/mux/login/startFI.html?cmpi=start-loggain", function () {
// fill in login field
browser.fill('#input1', 'zombie');
// fill in password field
browser.fill('#pContent', 'commingyourway');
// submit the form
browser.document.forms[0].submit();
// wait for new page to be loaded then fire callback function
browser.wait().then(function() {
console.log('Form submitted ok!');
// the resulting page will be displayed in your default browser
browser.viewInBrowser();
})
});
As side note: while I was trying to come up with working example I've tried to user following pages (all have failed for different reasons):
google.com - even though I filled query box with a string and submitted the form I didn't get search results . Reason? Probably google took some measures to prevent automatic tools (such as zombie) to browse through search results.
bing.com - same as google - after submitting the form I didn't get search results. Reason? Probably same as for google.
paulirish.com - After filling in the search query box and submitting the form zombie encountered script errors that prevent it from completing the page (something about missing ActiveX from charts script).
perfectionkills.com - Surprisingly here I've encountered the same problems as with Paul Irish site - page with search results couldn't be loaded due to javascript errors.
Conclusion: It's not so easy to force zombie into doing your work after all... :)

Javascript Auto Update

In my stream page, I have one current song script, but it doesn't update... The user needs to refresh the page.
the script is:
<script name="whasong" id="whasongid" src="http://xxxx.xxxx.net/js/song/u4:2134" type="text/javascript">
You appear to have javascript turned off.
</script>
src="http://xxxx.xxxx.net/js/song/u4:2134" code:
document.write('SONG NAME');
Is it possible to autoupdate just this script without refreshing the whole page ?
You might be in need of Ajax functionality. Do you mean that, at the end of a song playing, you want to modify the page content to reflect the song change? If it is so, you might gain a lot by looking into a JavaScript framework like jQuery or Backbone.js and bind some behavior to the state change in question. So you will want the song change (for example) to trigger a certain function you will have prepared, which will use jQuery to query the server via Ajax to update the page, and change the DOM accordingly.
EDIT: If instead of waiting for an explicit state change in your web page, you wanted to update it periodically, you might be looking for such a strategy :
function updatePage(/* arguments */) {
// Call the server to get some data ...
// Update the page accordingly ...
if (/* continue updating the page? */) setTimeout(60000 /* 60 seconds */, updatePage);
}
If you were using jQuery for your Ajax needs for instance, that function could look like so :
function updatePage(/* arguments */) {
jQuery.get("/your/url", {foo: "bar"}, function(data) {
// Update the page accordingly ...
if (/* continue updating the page? */) setTimeout(60000 /* 60 seconds */, updatePage);
});
}
If you want some more specific help, don't hesitate to be more precise as to what your issue is.

How do I limit mouse click rate in a Rails app?

I've written a timecard app, and I have a page where people can "punch" in and out. Each person has a button which makes an Ajax call back to change their status. A couple of folks have figured out that it's fun to click on a person's button a hundred times in a row, so that the report page for clock in/out times gets really goofy.
I'd like to put a simple Javascript function in the punch page which would limit the "clickability" of the page to something like once every 5 seconds. I've found a couple of other threads here talking about something like that, but I don't know enough about JS to graft those suggestions into how Rails is making the Ajax calls.
Well, there are several possibilities; depending on how much control you'd like.
First you could limit in the JS how often the request can be made. As mentioned earlier.
Also, you could make it part of the model logic, not accepting updates within a certain time (also mentioned earlier)
My suggestion would be to rate limit the request per user. There is a nice rack middleware for that: http://datagraph.rubyforge.org/rack-throttle/. If needed you could add customisations, but it's a good start I think.
Couldn't you check the updated_at field on your record? It won't limit the "mouse" click rate, but it will keep your record from getting updated more than x many times per minute/hour.
For example, in your model, you could have something like:
class TimeRecord < ActiveRecord::Base
validate :throttled_updates, on: :update
private
def throttled_updates
errors.add(:updated_at, "must be more than 5 minutes ago") if self.updated_at < 5.minutes.ago
end
end
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to
Option: disable-with
You can disable the button after an onclick event. The user user would have to reload the page to reclick. Otherwise you'll need some JS to re-enable after a timeout.
# assumes you use jQuery
link_to 'Toggle', toggle_path, :remote => true, :onclick => '
var e = $(this);
if(e.data("busy") == "yes"){
return false;
} else {
e.data("busy", "yes");
setTimeout(function(){
e.data("busy", "no");
}, 5000);
}
'.gsub("\n", '')
Check http://jsfiddle.net/u42gQ/1/

Categories

Resources