Can I use ActionCable to refresh the page? - javascript

I've recently been trying to create a live-scoring system for squash matches. I've managed to use ActionCable with Rails 5 to auto-update the score on the page, but I'd like to know if it's possible to tell Rails to refresh the page if a certain condition is met.
For example, if the game has finished, a different page is shown to say that the players are having a break between games. I need the page to refresh completely for this to happen.
In my database the boolean 'break' is marked as true when a game ends, and then the view uses a conditional if/else statement to decide what to show.
The code I use to update the score is attached below, I was thinking something along the lines of if data.break == true then the page will automatically refresh.
// match_channel.js (app/assets/javascripts/channels/match_channel.js)
$(function() {
$('[data-channel-subscribe="match"]').each(function(index, element) {
var $element = $(element),
match_id = $element.data('match-id')
messageTemplate = $('[data-role="message-template"]');
App.cable.subscriptions.create(
{
channel: "MatchChannel",
match: match_id
},
{
received: function(data) {
var content = messageTemplate.children().clone(true, true);
content.find('[data-role="player_score"]').text(data.player_score);
content.find('[data-role="opponent_score"]').text(data.opponent_score);
content.find('[data-role="server_name"]').text(data.server_name);
content.find('[data-role="side"]').text(data.side);
$element.append(content);
}
}
);
});
});
I don't know if this sort of thing is possible, and I'm not much good at anything Javascript related so I'd appreciate any help on this.
Thanks.

Reloading the current page is relatively straightforward. If you are using Turbolinks, you can use Turbolinks.visit(location.toString()) to trigger a revisit to the current page. If you aren't using Turbolinks, use location.reload(). So, your received function might look like:
received: function(data) {
if (data.break) {
return location.reload();
// or...
// return Turbolinks.visit(location.toString());
}
// your DOM updates
}
Either way is the equivalent to the user hitting the reload button, so it will trigger another GET, which calls your controller and re-renders the view.

Related

Executing a Function from a separate page

I'm not entire sure that this is possible, but here's what I'm looking at doing.
I have a list of buttons, that when pressed, modify a database that they are attached to, (for example, clicking button 1 would add 10 points to the score of player 1)
What I am looking to do in tandem, is to call a javascript function that lives on a separate page, a sort of a visual confirmation that the points were added to player 1's account.
Why am I organizing it like this?
I am running a Twitch channel where I am building a series of web views to display stats and information. I wish to control WHAT the audience sees from one page, and have the results display on a different page.
Here's what I have so far:
HTML (admin page):
<button class="addPoints" data-id="player1" data-name="Player 1">Player 1</button>
JS (admin page):
$(".addPoints").on('click', function(){
var $id = $(this).data('id');
var $name = $(this).data('name');
//AJAX MAGIC TO INSERT POINTS INTO DATABASE SO I CAN KEEP SCORE//
tallyPopup($id, $name);
});
HTML (display page):
<div class="displayScreen"></div>
JS (display page):
function tallyPopup(member, name){
$('.displayScreen').append(<div class='tallyPopup' id='"+member+"'><div class='portrait' id='"+member+"'></div><span class='name'>"+name+"</span><span class='score'>+10</span></div>);
$('.tallyPopup').animate({
opacity: 1
}, 3000, function(){
$(this).remove();
});
}
I know what I have does not connect the two pages, because I haven't the first clue on how to do that, if it's even possible. OR, is there a way for the Display HTML to check if the database has been updated and THEN run the tallyPopup function?
Thanks in advance.
You cannot call a function on another client (including your own clients) running your website.
To continuously check for points on the display page, use var intv = setInterval(function () {}, nMilliseconds) to repeatedly run a function until you call clearInterval(intv), which you might not do since you may want this to run forever, but perhaps only once every minute (nMilliseconds = 60000).
setInterval(function () { $.get('/point-count').then(tallyPopup) }, 60000)
tallyPopup would receive the data argument from the AJAX response.
Of course on the admin side you must fill in that line to update the amount of points via AJAX, either by PUT, POST, or PATCH. I would consider using PATCH just as a matter of semantics.
Also consider storing the return value of $(this) (var $this = $(this)) instead calling it multiple times, use promises, use CSS animations instead of $.animate (these perform much better). Consider making the element opaque and then visible (perhaps off screen when invisible), instead of using $.remove (also a performance improvement).

How to redirect user after successful login?

Update:
After implementing the below suggestion by Rob Sedgwick, it has become apparent that the redirect only works when the user manually "F5" refreshers the browser (Chrome). What I need to achieve is that this happens automatically in the code so the redirect happens without the user having to hot refresh. Thanks for help with this last part.
At the moment ManageTodosView from what I understand is the first action after the user has been logged in. It prints a list of to do items set up by the user. A working example can be found here http://parseplatform.github.io/Todo/ and the code is https://github.com/ParsePlatform/Todo
I'm using to code to really get user logins to work, I'm not to worries about what the output of the rest of the code is because the long term plan will be to remove it, for the time being its helpful to keep in place to show that the app functioning correctly.
I'm using this code as a base to build a web app. At the moment, once the user is logged in, they are displayed data on the same page.
I want to be able to change this so that after they login, the user is redirected to a different page and the information is then displayed to them there.
The reason for this is that the index page is just a landing/login page and I want to redirect them to a more structured HTML page with menus, etc.
Within the JS code, do I just put in a redirect, something like:
self.location="top.htm";
to this area of the code?
// The main view for the app
var AppView = Parse.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: $("#todoapp"),
initialize: function() {
this.render();
},
render: function() {
if (Parse.User.current()) {
new ManageTodosView();
} else {
new LogInView();
}
}
});
I have added the JS code to this JSFiddle
Update:
To address the issue of the page needing a manual fresh before the redirect works, insert
window.location.href="/someurl";
into the following code section within the todoe.js file and comment out the new ManageTodosView(); code.
Parse.User.logIn(username, password, {
success: function(user) {
window.location.href="user_home.html";
//new ManageTodosView();
self.undelegateEvents();
delete self;
},
Try this one also
window.open('url','_parent');
I would suggest a more robust template for integrating Parse Todo samples with real apps. 'Marionette' offers lots of value in real world.
If you take the time to look over the app's structure and then look at the 'loginSuccess' function (scroll to very bottom of link), its pretty straightforward to redirect. You can either use the router as shown OR you can use the Marionette aggregated events which would look like:
vent.trigger('header:loggedIn', _user);
somewhere else in any module within the app....
vent.on('header:loggedIn', function (user) {
that.setViewNew(user);
});
...
setViewNew : function (user) {
User = user;
var viewOptionsUser = {
collection : this.roleList,
events : {
'keypress #new-todo': 'createTaskOnEnter'},
parentUser : User};
this.headerRegion.show(new HeaderView(viewOptionsUser));
this.mainRegion.show(new RoleRoleListCompositeView(viewOptionsUser));
}
Try something like this to redirect your user
window.location = 'www.google.com'

History.js getState() at pageload

I'm a little confused about how History.js works at page-load. I've done a few experiments but the results seem indeterministic.
My website is a search engine and the query is stored in the URL parameters: ?Query=cats. The site is written purely in javascript. History.js works great when I do a new search, the new query is updated, and the state is pushed.
My problem is how to create an initial state if the user manually enters in a URL including a Query parameter. Every way I try to do this ends up resulting in running the search query twice in some case. The two use-cases that seem to conflict are:
User manually enters URL (mydomain.com?Query=cats) into address bar and hits enter.
User navigates to an external page, and then clicks the back button
In both cases, the javascript loads, and therefore looks to the URL parameters to generate an initial state.
However, in the second case, History.js will trigger the statechange event as well.
Necessary code:
History.Adapter.bind(window,'statechange',function() { // Note: We are using statechange instead of popstate
var s = History.getState();
if(s.data["Query"]){
executeQuery(s.data);
}
});
and in $(document).ready I have
// Get history from URL
s = getQueryObjectFromUrl(location.href);
if(s["Query"]){
History.pushState(s,'',$.param(s))
}
Is there a better way to handle creating an initial state from URL parameters?
As I had a similar problem to to yours, what i did was to define the function bound to a statechange as a named function, and then all I had it running when the page load as well.
It worked better than trying to parse the URI or anything else, hope it helps.
This is the way I chose to do it (based on Fabiano's response) to store the initial state parameters
var renderHistory = function () {
var State = History.getState(), data = State.data;
if (data.rendered) {
//Your render page methods using data.renderData
} else {
History.replaceState({ rendered: true, renderData: yourInitData}, "Title You Want", null);
}
};
History.Adapter.bind(window, 'statechange', renderHistory);
History.Adapter.onDomLoad(renderHistory);
Of course if you are using a different on DOM load like jquery's you can just place renderHistory(); inside of it, but this way doesn't require any additional libraries. It causes a state change only once and it replaces the empty initial state with one containing data. In this way if you use ajax to get the initData inside the else, and it will not need to get it the next time the person returns to the page, and you can always set rendered to false to go back to initial page state / refresh content.

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.

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.

Categories

Resources