Overriding difficult view model - javascript

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).

Related

{"error": "Please use POST request"} - alternative solution to submitting a form

I'm working on a school project and have attempted to create a calculator that can be found at the following link:
http://jsfiddle.net/ae97vgxz/2/
And my JS is:
$(document).ready(function(){
// Setup variable as empty
var method = "";
// Detect when initial radio button is clicked
$("input[type=radio]").click(function() {
// Get the weight from the input box
var weight = $("#meatWeight").val();
// If the water method was clicked
if ($(this).hasClass("water")) {
var method = weight * 60;
// Show me what the value is (can be removed)
alert(method);
// If the fridge method was clicked
} else if ($(this).hasClass("fridge")) {
var method = weight * 793;
// Show me what the value is (can be removed)
alert(method);
}
});
When you use it, if you enter a weight first, then select a method of defrosting you will get the correct answer in an alert window. However, if you press the 'calculate' button you get the following message -
{"error": "Please use POST request"}
From doing some of my own research, I believe this is because I am trying to submit a form and JSFiddle doesn't let you do that. If I try on a local environment in Chrome, again there is no output.
I am very limited by my JS knowledge (as I'm sure you can see) so I just can't fathom out a solution. Can anyone suggest what I am doing wrong and what the solution might be?
Thanks!
You have a mistake here:
function defrost(weight) {
return (makeTime(method));
}
It should be:
function defrost(weight) {
return (makeTime(weight));
}
Also, you should change the makeTime function or it won't work. The parseInt clause should be like this:
parseInt(time / 60);
Your current method of submitting the form is GET
<form id="defrostCalculator" name="defrostCalculator" onsubmit="callbackDefrost(this.elements.meatWeight.value); return false;" method="GET">
If the destination requires POST, use POST.
<form ... method="POST">
Your code has a couple of minor issues. However, the major issue you have is that if you want to use a non-AJAX form on jsfiddle, you have to change all your buttons to have the attribute:
type="button"
instead of what you currently have:
type="submit"
When you do <input type="submit", jsfiddle.net squawks and fails because you cannot submit form information to their servers. Luckily, you do not need any AJAX or server interaction. So your simple calculator can just use simple buttons.

JavaScript variable passed to PHP

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'];

Removing an <option> from a <select> tag according to previously selected <option> tag? (POLL Example)for

I have 50 rows of data and i want users to give them points by 1 to 50. I put dropdown boxes near them with options 1/50. But all i want is when a user selects 15(for example) for a row, 15 will be deleted from all other select tags of other rows. I am not as good as you in JavaScript. How can i accomplish this?
Hi casablanca i couldnt make he script you sent work. I need it to work on just one select tag so i give select tag an ID and an ID for the form too. I edit the scripts getElementsByTagName with getElementsByTagID (select tag's ID) to effect only one select tag. But the function doesnt triggered?
This might not be a very good idea, because it is very difficult for the user to modify choices -- for example, if I want to give 15 to a different option, I need to change the old one to something else and then change the new one to 15. Also, once all points have been assigned, it's impossible to make any changes because all options are gone.
A better idea would be to let the user do whatever he/she wants and then validate the form in the end. You could do that like this:
function validate() {
var used = []; // This array stores already used options
var rows = document.getElementById('myForm').getElementsByTagName('select');
for (var i = 0; i < rows.length; i++) {
var points = rows[i].value;
if (used[points]) {
// This value was already used
alert('Please choose a different value.');
rows[i].focus();
return false;
} else {
// This value was not used before; mark it as used now
used[points] = true;
}
}
return true;
}
And call this function in the onsubmit handler of your form:
<form id="myForm" onsubmit="return validate();">
EDIT1: id -> class
give each option the class of the number it is
<option class="15">15</option>
<option class="16">16</option>
etc.
Then jquery can remove() an item by class
$('.15').remove();
obviously have to do an on change and get the value just set. "remove()" is nice in this instance because I believe it will yank every instance of that class.
EDIT3: upon further consideration the above method would be further complicated by the need to not remove the "selected" option. Not going to figure out the exact method but I think changing the class from "15" to "selected15" with a $(this).append() or something of the sort before calling the remove would get the job done fairly safely.
EDIT2:
As noted by casblanca below this is not an ideal user interface at all for this type of input. You may want to look into this: http://www.utdallas.edu/~jrb048000/ListReorder/
Allows user to drag and drop items in a list to reorder them. Much more natural.

Clear default values using onsubmit

I need to clear the default values from input fields using js, but all of my attempts so far have failed to target and clear the fields. I was hoping to use onSubmit to excute a function to clear all default values (if the user has not changed them) before the form is submitted.
<form method='get' class='custom_search widget custom_search_custom_fields__search' onSubmit='clearDefaults' action='http://www.example.com' >
<input name='cs-Price-2' id='cs-Price-2' class='short_form' value='Min. Price' />
<input name='cs-Price-3' id='cs-Price-3' class='short_form' value='Max Price' />
<input type='submit' name='search' class='formbutton' value=''/>
</form>
How would you accomplish this?
Read the ids+values of all your fields when the page first loads (using something like jquery to get all "textarea", "input" and "select" tags for example)
On submit, compare the now contained values to what you stored on loading the page
Replace the ones that have not changed with empty values
If it's still unclear, describe where you're getting stuck and I'll describe more in depth.
Edit: Adding some code, using jQuery. It's only for the textarea-tag and it doesn't respond to the actual events, but hopefully it explains the idea further:
// Keep default values here
var defaults = {};
// Run something like this on load
$('textarea').each(function(i, e) {
defaults[$(e).attr('id')] = $(e).text();
});
// Run something like this before submit
$('textarea').each(function(i, e){
if (defaults[$(e).attr('id')] === $(e).text())
$(e).text('');
})
Edit: Adding some more code for more detailed help. This should be somewhat complete code (with a quality disclaimer since I'm by no means a jQuery expert) and just requires to be included on your page. Nothing else has to be done, except giving all your input tags unique ids and type="text" (but they should have that anyway):
$(document).ready(function(){
// Default values will live here
var defaults = {};
// This reads and stores all text input defaults for later use
$('input[type=text]').each(function(){
defaults[$(this).attr('id')] = $(this).text();
});
// For each of your submit buttons,
// add an event handler for the submit event
// that finds all text inputs and clears the ones not changed
$('input[type=submit]').each(function(){
$(this).submit(function(){
$('input[type=text]').each(function(){
if (defaults[$(this).attr('id')] === $(this).text())
$(this).text('');
});
});
});
});
If this still doesn't make any sense, you should read some tutorials about jQuery and/or javascript.
Note: This is currently only supported in Google Chrome and Safari. I do not expect this to be a satisfactory answer to your problem, but I think it should be noted how this problem can be tackled in HTML 5.
HTML 5 introduced the placeholder attribute, which does not get submitted unless it was replaced:
<form>
<input name="q" placeholder="Search Bookmarks and History">
<input type="submit" value="Search">
</form>
Further reading:
DiveintoHTML5.ep.io: Live Example... And checking if the placeholder tag is supported
DiveintoHTML5.ep.io: Placeholder text
1) Instead of checking for changes on the client side you can check for the changes on the client side.
In the Page_Init function you will have values stored in the viewstate & the values in the text fields or whichever controls you are using.
You can compare the values and if they are not equal then set the Text to blank.
2) May I ask, what functionality are you trying to achieve ?
U can achieve it by using this in your submit function
function clearDefaults()
{
if(document.getElementById('cs-Price-2').value=="Min. Price")
{
document.getElementById('cs-Price-2').value='';
}
}

Dynamically added JavaScript not finding dynamically added fields in IE

I have a table which has a button to "Add Rows". This button adds a row dynamically with JQuery. It works by copying the first ... and then replacing all the id=".." with an incremented number.
The problem is that the rows have a YUI AutoComplete which looks like the following:
<td>
<input type="hidden" name="location_num[0]" value="508318" maxLength="25" style="width:230px" id="location_num[0]"/>
<input type="textbox" name="location_numDisplayDesc[0]" value="WINNIPEG" maxLength="25" style="width:230px" id="location_numDisplayDesc[0]"/>
<div id="Container_location_num[0]" style="display:inline;"></div>
<script type="text/javascript">
// Initialize autocomplete
var location_numAC = new YAHOO.widget.AutoComplete(
"location_numDisplayDesc[0]",
"Container_location_num[0]",
locationDataSource,
acConfig);
location_numAC.useShadow = true
location_numAC.useIFrame = true
location_numAC.dataErrorEvent.subscribe(acErrorFunction);
// Format results to include the reference number
location_numAC.formatResult = function(resultItem, query) {
return resultItem[0];
};
// Clear key before request
location_numAC.dataRequestEvent.subscribe(function fnCallback(e, args) {
YAHOO.util.Dom.get("location_num[0]").value = ""; });
// Set key on item select
location_numAC.itemSelectEvent.subscribe(function(event, args) {
YAHOO.util.Dom.get("location_num[0]").value = args[2][1];
});
// Clear key when description is cleared
location_numAC.textboxBlurEvent.subscribe(function fnCallback(e, args) {
if (isEmpty(YAHOO.util.Dom.get("location_numDisplayDesc[0]").value)) {
YAHOO.util.Dom.get("location_num[0]").value = "";
} // end if
});
</script>
</td>
This code works fine in Firefox and the newly created AutoCompletes work, but in IE (6 & 7) I am getting an error that means that the location_num_AC is not being created successfully. I believe that it's because that it's not reading the newly created inputs or div as it should. I've tried wrapping the javascript with
$("Container_location_num[0]").ready(function {...});
but that didn't seem to work. Does anyone have any other ideas?
Form fields that are inserted into the DOM in IE don't add to the forms collection as you might expect.
Normally you can refer to a form field one of two ways:
document.forms[0]["myFormName"];
document.forms[0][12];
That is, by its form field name or by its index. But when you add a form field to the DOM in IE you can't refer to it by name, only by its index. If your code (or any supporting code) is looking for a form field in the collection by its name you've obviously got a problem.
If your only key is the name you can loop through all the form fields by index and find what you're looking for, but that's obviously going to be a linear operation. You can also loop through and find which form fields are indexed numerically but not by name and update the form object yourself.
I don't have enough detail to know how (or if) this is occurring in your project, but it's one of those IE quirks that sounds like it might be playing a role since you're adding fields dynamically.

Categories