I'm creating a list with a set of checkboxes similar to the Trello tool, the data is extracted from the MySql database.
When the user selects one of the checkboxes to mark the activity as completed, I use an XMLHttpRequest request to make the asynchronous call of the php route and edit the task status to "completed" in the database. (Thus avoiding page refresh).
Also, I have a bootstrap progress bar that counts the number of rows in the table according to status "completed", "pending" and "total", and I use this data to populate the progress bar dynamically.
However, this bar is only updated when I enter the page or refresh it, I would like it to be updated along with the XMLHttpRequest request that is triggered every time the checkbox undergoes any change.
My question is: Is there any way to fill in the bar progress information according to XMLHttpRequest requests?
//CODE RESPONSIBLE FOR MAKING THE ASYNCHRONOUS REQUEST TO EDIT THE STATUS OF THE CHECKBOX IN THE DATABASE
function checkRealizado(url) {
let checkRealizado = new XMLHttpRequest();
checkRealizado.open('POST', url)
checkRealizado.onreadystatechange = () => {
if(checkRealizado.readyState == 4 && checkRealizado.status == 200) {
}
}
checkRealizado.send()
}
//CHECKBOX RESPONSIBLE FOR ACTIVATING THE XMLHTTPREQUEST FUNCTION
<label class="marcarConcluido" id="marcarConcluido" for="chkPassport">
<input onclick="checkRealizado('/tarefa?acao=checkRealizado&id='+<?= $checklist->id //DATA EXTRACTED FROM THE DATABASE, ALL OK HERE ?>)" class="form-check-input" id="chkPassport" style="margin-right: 8px" type="checkbox">
<?= $checklist->checklist //DATA EXTRACTED FROM THE DATABASE, ALL OK HERE ?>
</label>
//HTML CODE OF MY PROGRESS BAR
<div id="progressBar" class="progress text-muted" style="height: 6px;" role="progressbar" aria-label="Basic example" aria-valuenow="25" aria-valuemin="0" aria-valuemax="<?= $this->view->getLinhasCheck ?>">
<progress class="text-muted progress-bar w-100" style="background-color: #fff; height: 6px;" value="<?= $this->view->getLinhasCheckRealizada ?>" max="<?= $this->view->getLinhasCheck ?>"></progress>
</div>
/// $this->view->getLinhasCheckRealizada This variable is responsible for filling in the data according to the number of rows in the database table, everything working so far
// $this->view->getLinhasCheck This variable is responsible for filling in the data according to the number of rows in the database table, everything working so
Break your problem down into parts.
First part? How do we set the width of the progress bar? The Bootstrap docs show that is easy - just set an inline with the percentage, like style="width: 25%".
Next part: How do we get that percentage? You mention you are calculating that on the back end already, depending on the rows completed etc. OK, so your PHP code already has this.
How do we get that value to the front end? You'll have to return it in the response to your XMLHttpRequest, right? OK so how do we return and use data in an XMLHttpRequest? A quick search shows us many examples here on SO, here's one: we can just use this.responseText.
How do we dynamically update the progress bar once we have the new percentage value? Again, searching here turns up many examles, here's one:
document.getElementsByClassName('progress-bar').item(0)
.setAttribute('style', 'width:' + Number(percent) + '%');
So put it all together.
Your Javascript
function checkRealizado(url) {
let checkRealizado = new XMLHttpRequest();
checkRealizado.open('POST', url)
checkRealizado.onreadystatechange = () => {
if(checkRealizado.readyState == 4 && checkRealizado.status == 200) {
// Response to your request is the percentage
let percentage = this.responseText;
// Update the progress bar
document.getElementsByClassName('progress-bar').item(0)
.setAttribute('style', 'width:' + Number(percentage) + '%');
document.getElementsByClassName('progress-bar').item(0)
.setAttribute('aria-valuenow', percentage);
}
}
checkRealizado.send()
}
Your PHP
// ... your code which updates rows, and calculates percentage
// You just want to echo the result so the XMLHttpRequest gets the
// result. So last thing you do, assuming success is:
echo $percentage;
Notes
You might want to consider returning JSON from your request, so that you can include more than just the percentage - maybe something like:
{
"status": "OK",
"message": "",
"percentage": 42
}
For eg if the request fails you might want to return/display an error message. Currently if that happens the code will just try to use that error msg as a percentage, which won't work. Some searching turns up plenty of answers here about how to use JSON with XMLHttpRequest, eg: Parsing JSON from XmlHttpRequest.responseJSON
You might want to look at using fetch() instead of XMLHttpRequest: Fetch API vs XMLHttpRequest
Related
I am trying to replace some text in an input field using JS but the view model overrides my commands each time. This is the HTML I start with:
<td class="new-variants-table__cell" define="{ editVariantPrice: new Shopify.EditVariantPrice(this) }" context="editVariantPrice" style="height: auto;">
<input type="hidden" name="product[variants][][price]" id="product_variants__price" value="25.00" bind="price" data-dirty-trigger="true">
<input class="mock-edit-on-hover tr js-no-dirty js-variant-price variant-table-input--numeric" bind-event-focus="onFocus(this)" bind-event-blur="onBlur(this)" bind-event-input="onInput(this)">
</td>
I run this JS:
jQuery('#product_variants__price').siblings().removeAttr('bind-event-focus');
jQuery('#product_variants__price').siblings().removeAttr('bind-event-input');
jQuery('#product_variants__price').siblings().removeAttr('bind-event-blur');
jQuery('#product_variants__price').siblings().focus()
jQuery('#product_variants__price').siblings().val("34.00");
jQuery('#product_variants__price').val("34.00");
And I'm left with the following HTML:
<td class="new-variants-table__cell" define="{ editVariantPrice: new Shopify.EditVariantPrice(this) }" context="editVariantPrice" style="height: auto;">
<input type="hidden" name="product[variants][][price]" id="product_variants__price" value="34.00" bind="price" data-dirty-trigger="true">
<input class="mock-edit-on-hover tr js-no-dirty js-variant-price variant-table-input--numeric">
</td>
The problem is that each time I click the input field the value is reverted to what it was when the page loaded.
I've also tried running the command in the parent td along with my value change, to simulate the editing of a variant and preventing default with no success:
jQuery('#product_variants__price').siblings().bind('input', function (e) {
e.preventDefault();
return false;
});
jQuery('#product_variants__price').siblings().bind('focus', function (e) {
e.preventDefault();
return false;
});
jQuery('#product_variants__price').siblings().focus()
jQuery('#product_variants__price').siblings().val("£34.00");
jQuery('#product_variants__price').val("£34.00");
jQuery('#product_variants__price').siblings().keydown()
Parent td function:
new Shopify.EditVariantPrice(jQuery('#product_variants__price').parent())
So how can I successfully edit this value in the inputs and also update the Shopify view model?
You can try this for yourself by going here:
https://jebus333.myshopify.com/admin/products/2521183043
login jebus333#mailinator.com
password shop1
EDIT: I've tried to find the view model on the page but with no success. Plus, there are no network calls when editing the values in the input fields, leading me to believe the values are being pulled back from somewhere on page.
Try this:
var old = Shopify.EditVariantPrice.prototype.onFocus;
Shopify.EditVariantPrice.prototype.onFocus = function(t) {
this.price = '50.00'; // Use the price you want here
old.call(this, t);
};
jQuery('#product_variants__price').siblings().triggerHandler("focus");
jQuery('#product_variants__price').siblings().triggerHandler("blur");
If it works for you, it's possible that the following will be sufficient:
Shopify.EditVariantPrice.prototype.onFocus = function(t) {
this.price = '50.00'; // Use the price you want here
};
Well, there is a kind of a dirty solution...
First of all you'll need a sendkeys plugin. In fact that means you'll need to include this and this JS libraries (you can just copy-paste them in the console to test). If you don't want to use the first library (I personally find it quite big for such a small thing) you can extract only the key things out of it and use only them.
The next step is creating the function which is going to act like a real user:
function input(field, desiredValue) {
// get the currency symbol while value is still pristine
var currency = field.val()[0];
// move focus to the input
field.click().focus();
// remove all symbols from the input. I took 10, but of course you can use value.length instead
for (var i = 0; i < 10; i++) field.sendkeys("{backspace}");
// send the currency key
field.sendkeys(currency);
// send the desired value symbol-by-symbol
for (var i = 0; i < desiredValue.length; i++) field.sendkeys(desiredValue[i]);
}
Then you can simply call it with the value you wish to assign:
input($("#product_variants__price").next(), "123.00");
I did not really manage to fake the blur event because of lack of the time; that is why I was forced to read the currency and pass .00 as a string. Anyway you already have a way to go and a quite working solution.
Looks like you're trying to automate editing of variant prices of products in Shopify's admin panel.
Instead of playing around with the DOM of Shopify's admin page, I'll suggest using Shopify's bulk product editor which lets you set prices of all variants in a single screen. I feel that you'll have better luck setting the variant prices using JavaScript on the bulk product editor page.
Clicking on the 'Edit Products' button as shown in the screenshot below will open the bulk product editor.
Also check if browser based macro recording plugins like iMacro can be of your help (you can also code macros with JS in iMacro).
Given I have a model Post which can have up to n Comments (the number is controlled by the Backend) and I have a view which allows to add a Comment via a AJAX request. What is the best way to tell the view upon the nth request to disable the Add-Comment-Form?
The nth request is successfull so status code should still be 200/201 but the backend already "knows" that the nth + 1 call will invalidate the Post, so i want to somehow tell this to the view so it can take action before the user experiences the (catched) error upon nth + 1 submit.
By now the backend renders html which is then simply attached to a div in the DOM, with JSON I might add an additional field but then would move the templating to the view again.
If someone has an idea for an elegant solution?
Try having your server render javascript value for comment count and max comments. Then you can increment the count value in your success function as well as perhaps render the html comment.
Something like
var commentCount = *value from server*;
var maxComments =*value from server*;
$('#mybutton').click(function(){
$.ajax({
// your code here
})
. success(function (response) {
// process response
commentCount ++;
if( commentCount >= maxComments)
$('#mybutton). prop('disabled', true);
});
Just hide the "Add comment" button by JavaScript, when it will be "nth + 1". Or remove eventListener from button and change caption to smth like "You reached max comments".
Keep a track of the number of clicks. After the n th click, you can change the disabled attribute of your button to true.
$(".myButton").attr("disabled",true);
I am pretty new to coding php and javascript but have manged to scrape my way through so far. However, I have hit a wall. I'm not 100% sure that what I'm trying to do can be done, or that I'm even attempting it in an effective way.
I have a dynamically filled table, made of rows from a SQL statement using php. On each row is a radio button, each one given a unique value based on the row number (a unique value in one of the database columns). I am attempting to program a button that enables the user to pass a selected radio button value to a separate php enabled page that will allow the user to edit the row information using the unique row value. Also, i used the confirm option, because I would also like to add an if else statement that allows the user to cancel, if the wrong row was selected (I haven't attempted that yet because I haven't been able to get the value to pass).
Page button
<input type="button" id="edit_order_button" value="Edit Order"></input>
JQuery page
$(document).ready(function(){
$("#edit_order_button").click(function(){
var selected = $("input[name ='order_edit_select']:checked").val();
var r = confirm("Confirm Order Number to edit: " + selected);
$.post("php/editOrder.php", {
selected1: selected
}, function(data,status) {
//alert("Data: " + data + "\nStatus: " + status);
window.open("php/editOrder.php","Edit Order","menubar=1,resizable=1,width=750,height=600, left=250, top=50");
});
});
});
PHP end destination
<?php
//Login to database (usually this is stored in a separate php file and included in each file where required)
require('config.php');
$selected2= isset($_POST['selected1']) ? $_POST['selected1'] : ''; // Fetching Values from URL
echo "Selected Row number is ".$selected2.".";
mysqli_close($connection); // Connection Closed.
?>
I have tried a few things but have so far been unsuccessful in getting the new window to load with the value of the radio button passed. Currently, the window loads, but the new window only displays the echo text, no variable. When I run with the alert popup, the data shows me the value of the selected row but it does not seem to be posting to the new page prior to the window.open command. Any tips would be appreciated, or if i'm approaching the problem from a wrong angle, any insights would also be great. I have also tried debugging, thinking I was missing a parentheses or semicolon but I wasn't able to find anything. Thanks
Looks to me like you can get rid of the post statement altogether and just pass the selection to the newly opened window.
$(document).ready(function(){
$("#edit_order_button").click(function(){
var selected = $("input[name ='order_edit_select']:checked").val();
// only open window if order okayed
if ( confirm("Confirm Order Number to edit: " + selected) ) {
window.open("php/editOrder.php?selected1=" + selected,"Edit Order","menubar=1,resizable=1,width=750,height=600, left=250, top=50");
}
});
});
php/editOrder.php:
$selected2 = $_GET['selected1'];
echo "Selected Row number is $selected2.";
Used a method to create some +1 and -1 buttons on a page. (Like a scoreboard)
jQuery - Increase the value of a counter when a button is clicked
However I'm looking for a way to sync the results so that they don't go back to default after each refresh.
Any ideas?
<span id="output">-99</span>
<button class="button right" id="target" type="button">+</button>
<button class="button right" id="targetminus" type="button">-</button>
$('#target').click(function() {
$('#output').html(function(i, val) { return val*1+1 });
});
$('#targetminus').click(function() {
$('#output').html(function(i, val) { return val*1-1 });
});
You need to store the result in the server, in a database, a local file or whichever place suits you best. What you need to do is:
Client side:
Perform an AJAX request to a URL that will increment or decrement the current value. Don't send the current value, because someone else could have modified it since you've loaded the page.
Block your buttons until the AJAX request finishes.
Refresh the current value with the value received by the server
Unblock the buttons.
Server side:
Increment or decrement your current stored value depending on the request send by the browser. This operation must be transactional.
Send the new, updated value to the browser that made the request.
This is not the place to explain how to perform every step. It would depend on your server, your server technology, or whether you have a database or not. I suggest you to learn more about client-server web applications.
I guess you can create a cookie and then read it when you reload. something like this :
function bakeCookie(score) {
document.cookie = "score =" + score + "; path=/";
}
And you can read it when you load the page and set the value.
function eatCookie() {
var score = document.cookie.replace(/(?:(?:^|.*;\s*)score\s*\=\s*([^;]*).*$)|^.*$/, "$1");;
}
Hope it helps.
I'd personally use cookies, There's a really nice little JQuery plugin for them here:
https://github.com/carhartl/jquery-cookie
Also I'd maybe use data attributes on my buttons and tag them with a css class. This way you can then reuse your function to add subtract anything tagged with this data attribute and you can put in any number in the data-val attribute, e.g. 5, -5, 10, -10 etc.
<button class="button right target" type="button" data-val="1">+</button>
<button class="button right target" type="button" data-val="-1">-</button>
$('.target').click(function() {
var val = $(this).data('val');
var score = $.cookie('Score');
$.cookie('Score', score + val);
$('#output').html($.cookie('Score'));
});
I'm working on a form that adds up the totals selected (via checkboxes). In my JavaScript file, build.js, the totals are added together. On my PHP page, the code takes the items selected on the previous form/HTML page and passes them to what is shown on the PHP page. I want to be able to take the total that was added up via JavaScript on the form page and bring it over to be listed as a total underneath all the options that were selected.
My knowledge of PHP and JavaScript are very rudimentary. This is the first real form I have created in either of these languages. I have poured over this site and the internet in general and have not been able to get any of the options I've found to work. I think I just lucked out on getting the form this far, so I apologize if my code isn't very clean!
Any help would be amazing, as specific as possible please. Here is my code:
The JavaScript that adds the total:
$(document).ready(function() {
$("input[type=checkbox]:checked").attr("checked", false);
function recalculate() {
var sum = 0;
$("input[type=checkbox]:checked").each(function() {
sum += parseInt($(this).attr("rel"));
});
$("#output").html(sum);
}
$("input[type=checkbox]").change(function() {
recalculate();
});
});
Code written on the form itself that shows the total:
<span id="output" class="total"></span><BR><BR>
Code written on the PHP page:
<b>Estimate:</b>
<?php
$aTruck = $_POST['formSelected'];
if(empty($aTruck))
{
echo("You didn't select a truck.<BR><BR>");
}
else
{
$N = count($aTruck);
echo("<h3>Truck Type: ");
for($i=0; $i < $N; $i++)
{
echo($aTruck[$i] . " ");
}}
$aAddons = $_POST['formAddons'];
if(empty($aAddons))
{
echo("You didn't select any options.");
}
else
foreach ($aAddons as $v)
{
echo "<h3> $v </h3>";
}
?>
If I'm not mistaken, the reason I can't currently pass the total is because of something I read on here: the PHP is run on the server while the JavaScript runs on the user's end. My options are thus to send the total in the form (possibly as a hidden variable, which I can't figure out either), pass it along in Ajax (I don't know if the server I'm on is capable of this- possibly so and it's all use error!), or use an XMLHttpRequest. I've tried anything I could find on any of those and either do not have the right variable listed inside, am placing it in the wrong spot, or it's just plain wrong.
As I mentioned, I've poured over the forums for everything I can that's related to this and nothing I've found is specific enough for the tiny bit of understanding I have. Among other things I've tried: Pass a javascript variable value into input type hidden value and Pass Javascript Variable to PHP POST along with using an XMLHttpRequest, using Ajax, passing it as a hidden variable (which I'm leaning towards but don't think I'm implementing correctly) and a ton more- it's pretty much all I did all day at work yesterday so I'm not trying to be redundant with my question- I just can't figure out where I'm going wrong.
It looks like you hit upon it right here:
send the total in the form (possibly as a hidden variable)
Since you're talking about one page posting to another page, and that other page showing the results, then there's no need for AJAX here. You can just use a form value like any other. The "hidden variable" in this case is actually an input element:
<input type="hidden" name="sum" />
In your JavaScript where you're displaying the sum on the first page:
$("#output").html(sum);
You can also set that sum to the form element's value:
$("#output").html(sum);
$("input[name=sum]").val(sum);
As long as that input is inside the same form as the other input elements (like formSelected and formAddons) then when the first page posts to the second page, the code in the second page can access the sum value the same way:
$_POST["sum"]
In your form you should add a hidden input like this :
<input type="hidden" name="sum" value="">
Then in your recalculate() (javasript) function, you should change the value of this input once you calculated everything :
function recalculate() {
var sum = 0;
$("input[type=checkbox]:checked").each(function() {
sum += parseInt($(this).attr("rel"));
});
$("#output").html(sum);
// Change the hidden input value
$("input[name='sum']").val(sum);
}
Now, when your form is submitted, you should access the sum value, server side (PHP), with a simple :
$sum = $_POST['sum'];