I've got this helper and event map with a Session. The event is listening for a button click then grabs the input text from the input field. Then it sets the Session. The helper gets the Session and does a find() on the collection for CampYear (one of the collection fields).
Session.setDefault('keyCampYear', '');
Template.camp.helpers({
'Query': function () {
var cy = Session.get('keyCampYear');
return Programs.find({CampYear: cy}).fetch();
}
});
Template.query.events({
'click #camp-year-button': function(event) {
var campYearTemp = document.getElementById('camp-year').value;
Session.set('keyCampYear', campYearTemp);
}
});
The output is set up with a spacebars {{#each}} to present a table:
<tbody>
{{#each Query}}
<tr>
<td>{{Fname}}</td>
<td>{{Lname}}</td>
<td>{{CampYear}}</td>
</tr>
{{/each}}
</tbody>
This works as expected. I now need to add another text field and button to query the database for a different field, and output to the same spacebars {{#each}}. But I'm not able to get it working. Adding another event map, Session and helper doesn't seem to work. Is there a way I can get another input field with its button to search for a different field in the collection and output on the same spacebars {{#each}}?
NON-WORKING CODE BELOW:
Add text box and button:
<input type="text" id="donate">
<button id="donate-button">GO</button>
Add new event map, helper and Session:
Session.setDefault('keyDonate', '');
Template.camp.helpers({
'Query': function () {
var don = Session.get('keyDonate');
return Programs.find({DONATE: don}).fetch();
}
});
Template.query.events({
'click #donate-button': function() {
var donateTemp = parseInt(document.getElementById('donate').value, 10);
Session.set('keyDonate', donateTemp);
}
});
What ends up happening is that the entire Mongo collection loads in the browser by default, without the user clicking either button. The first button and text box are inoperable, the newly created button and text box work, but very sluggishly.
Looks like I found a solution. I'm not familiar with Meteor enough to know that this is THE solution, but here's what I did. First, to prevent the entire collection from loading on the page, both Sessions had to be set to a default of undefined:
Session.setDefault('key', undefined);
Rather than:
Session.setDefault('key', '');
I could default one Session like this with a '' value and it didn't seem to mind, with two set like this, things went haywire. The next change was to add another Spacebars {{#each}} inside the table just below the first one.
{{#each Query}}
<tr>
<td>{{Fname}}</td>
<td>{{Lname}}</td>
<td>{{CampYear}}</td>
</tr>
{{/each}}
{{#each Query2}}
<tr>
<td>{{Fname}}</td>
<td>{{Lname}}</td>
<td>{{CampYear}}</td>
</tr>
{{/each}}
Each helper now needs to link to the appropriate {{#each}}. The third part was that I was forced to set one Session back to undefined while setting the other to prevent stacking the tables on top of each other.
Template.query.events({
'click #donate-button': function() {
var donateTemp = parseInt(document.getElementById('donate').value, 10);
Session.set('keyCampYear', undefined);
Session.set('keyDonate', donateTemp);
}
});
Template.query.events({
'click #camp-year-button': function() {
var campYearTemp = document.getElementById('camp-year').value;
Session.set('keyDonate', undefined);
Session.set('keyCampYear', campYearTemp);
}
});
Related
In a Wordpress installation I have a custom post type and a metabox with custom post_meta fields.
Parent dropdown field in Gutenberg's sidebar used to be a <select> element. I have some scripts that onChange trigger the hide/show of different fields making them conditional, depending whether the page being edited is a parent or child:
$(document).on("change", ".editor-page-attributes__parent select", function(){
toggleFields();
}
The options in the <select> have IDs as values so I could get Title and ID for the selected parent and dynamically show some data in the metabox for my user:
var dropdown = $('.editor-page-attributes__parent select');
var parentName = dropdown.find(':selected').text();
var parentId = dropdown.val();
Since v5.6 Wordpress has replaced that select element with a Combo Box. I have tried to get the same data onChange and only had some success using blur:
$(document).on("blur", ".editor-page-attributes__parent .components-combobox-control__input", function(){
toggleFields();
var parentName = dropdown.val();
})
I was only able to get the page title since this combobox now has an input element that's missing IDs, like this one:
<input id="components-form-token-input-0" type="text" class="components-combobox-control__input components-form-token-field__input" value="Page Name Here">
I have also tried doing an Ajax, to retrieve the ID using get_page_by_title() but it does not always work because pages might have the same title and editor also adds dashes and spaces in names for hierarchy levels.
How can I get the associated ID of the page selected in the Parent Combobox on change?
After some time reading the Editor Documentation I found the proper solution. This is how you can listen to changes and get the id of the Parent Combobox select in the WP Editor:
wp.data.subscribe( function () {
var newParent = wp.data.select('core/editor').getEditedPostAttribute('parent');
console.log(newParent);
} );
wp.data.subscribe is called every single time a change occurs in the current state while editing posts in the editor, so the listener function is called every single time. We can avoid that with a simple variable check when there is an actual change to the field we want. It behaves as Redux subscribe without unsubscribe and only one listener.
To also check for the current post type we are editing we can use getCurrentPostType:
wp.data.select('core/editor').getCurrentPostType();
This is the full code for this problem for future reference:
if (wp.data.select('core/editor').getCurrentPostType() == 'cpt_name') {
const getPostParent = () => wp.data.select('core/editor').getEditedPostAttribute('parent');
// set initial parent
let postParent = getPostParent();
wp.data.subscribe(() => {
// get current parent
const newPostParent = getPostParent();
// only if parent changes
if( postParent !== newPostParent ) {
// Do what we want after parent changed
toggleFields();
}
// update the variable
postParent = newPostParent;
});
}
The backend is Python with Django Models. Below is what I have in the User Inteface.
When the user clicks the "edit" pen I want a textbox input to show up right beneath the previous value, also a "submit changes" button pops up at the top of the page. The user needs to be able to edit as many properties as desired and then submit all changes at once. My object has over 75 properties so the javascript will get really long and cumbersome if I create a unique function for each property. So far I have this:
html:
<button id="edit_submit" type="submit" form="job_text_edit">Commit Changes</button>
<form id="job_text_edit" action="pass to backend"></form>
<table class="z-depth-3">
<tr>
<td style="width: 200px">Job Name:</td>
<td>
{{job.job_name}}<i class="tiny material-icons" onclick="jobEdit()">edit</i>
<input id="job_name_i" name="job_name_i" type="text" form="job_text_edit">
</td>
</tr>
<tr>
<td>Work Schedule:</td>
<td>
{{job.work_schedule}}<a><i class="tiny material-icons" onclick="jobEdit()">edit</i></a>
<input id="work_schedule_i" name="work_schedule_i" type="text" form="job_text_edit">
</td>
</tr>
</table>
javascript:
<script>
$(document).ready(function(){
$('#edit_submit').hide();
$('#job_name_i').hide();
$('#work_schedule_i').hide();
})
function jobEdit(){
$('#edit_submit').show();
$('#job_name_i').show();
$('#work_schedule_i').show();
}
</script>
The problem is that when you click any "edit" pens, all the edit boxes will pop up. Is there a way to let the function know which one was clicked so I can implement conditional statements in the function to show only the necessary boxes? I tried passing in a string with the input id but the function throws errors when given string arguments. Any help is greatly appreciated!
when you pull data from the back end, you need to pull through an id or some unique value also.
This value would be unique to each indiviudal row you show in your screenshot above, or an element in that row.
Then when you render your html, append the id (unique value) to the end of the current id (html id on the element)
So for example, where you have input id="job_name_i", you can add (append) the unique value (id) to the end of it upon rendering.
You can then, instead of passing through your function call in the onclick (and defining the onClick in the html), you can set up an event listener in the init part of your javascript like so:
$("[id^=job_name_i]").on('click', event => {
const clickedElement = $(event.target);
});
The above will listen for a click on any element that begins with
job_name_i (remember your unique value will be appended to the end of it.
So the above would go inside the below block.
$(document).ready(function(){
});
You now have access to the specific clicked element on the page, to do as you need, adding stuff below or above it. So you can access the ID with by using event.target.id and you can pass that in to your function.
Something like the following.
function jobEdit(id){
$(id).show();
// OR
$(someelement + id).show();
}
My page fires off an ajax query, where the MySQL Db is queried and the results are returned. (all successful).
Those results are formatted for output as a shopping gallery/catalogue and also as an accordion filter menu. So I can filter the shopping catalogue display. eg say I want to see only items that are red.
All is working so far.
My problem is with the filter accordion menu - dynamically created in js.
When I click on any selectable item in the tab-content, nothing happens. This means the parameter that should be sent, isn't being sent.
If I hard code the accordion filter or even load it with my server-side language, into the html directly, the filtering does send off the parameter and so the shopping catalogue is adjusted accordingly but, in that scenario, I am unable to dynamically change the filter menu.
I think the code I shall post below is the relevant code that recognises changes in the originally loaded content and fires off the ajax but (I think) it doesn't understand any changes to textboxes in the dynamically loaded content.
Please help me to understand what I need to add that will make dynamically loaded content fire-off to the ajax calls.
var $checkboxes = $("input:checkbox");
function update_nav_filter(opts) {
$.ajax({
type: "POST",
url: "/php-queries/product-filter-query.php",
dataType: 'json',
cache: false,
data: {
filterOpts: opts
},
success: function(records) {
//console.log(records);
//alert('SUCCESS!');
// alert(records);
$('#filters_div').html(makeFilter(records));
}
});
}
$checkboxes.on("change", function() {
//alert('there is a change is checkbox status'); // working on page load but not when any checkbox is clicked-on
var opts = getCatalogueFilterOptions();
updateCatalogue(opts);
update_nav_filter(opts);
});
$checkboxes.trigger("change");
Any help greatly appreciated.
I have created an event listener.
Following page-load, I select an item in the JS generated nav filter. eg pedal_bins in the sub_category section. I am then shown a display of pedal_bins. :)
Then I select 'kettles', another sub_category but I can only see the last sub_category that I click on. The pedal_bins disappear.
How best can I build and remove items with a single click? Store in a session parameter and then
a. remove the latest click if it matches whats in the session
b. add the latest click if its not already in the session
Then submit whatever the array is at that stage?
Or, is there a better way to run this?
Here's the listeneer
enter code here
document.getElementById("filtering_div").addEventListener("click",function(e) {
// e.target was the clicked element
if (e.target && e.target.matches("input")) {
var parameter = e.target.id;
//console.log("Anchor element", parameter , " was clicked" );
var opts = getCatalogueFilterOptions(parameter);
console.log(opts);
// update_nav_filter(opts);
updateCatalogue(opts);
}
});
You have a "delegation" problem. When you create a dynamic element, in order to be able to act on the newly created element, you have to reference it as a child element that was originally loaded with the DOM.
For example, if you have an element called <div id="top"></div> and you create a dynamic element, let's say <button id="test">Click</button> in there, you'll have to refer to that div when adding an event listener.
$("#top").on('click', '#test', function(){
//event related code goes here.
});
Here is a fiddle I created that explains the whole thing with some examples.
If you have any questions about it, please let me know.
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).
I'm attempting to rebind the listview data after changing the template, based on a DropDownList value. I've included a JSFiddle for reference. When I rebind currently the values in the template are undefined.
Thanks!
JSFiddle link
I was thinking the best way to handle it would be in the 'select' or 'change' function:
var cboDetailsCategory = $("#detail").kendoDropDownList({
data: [
"All",
"Customer",
"Location",
"Meter",
"Other"],
select: function (e) {
var template = $("#" + e.item.text()).html();
console.log("template", template);
$("#details").html(template);
},
change: function (e) {
},
please refer to the JSFiddle link and this graphic as a visual
Here is a lengthier workflow:
User completes a name search and clicks a search button.
Name results are populated in a listview, rendered individually as button controls using a template.
User then clicks one of the name results (shown as the button text).
A dropdownlist of categories ('All' <--default , 'Location', 'Customer'...) gives the user the ability to target what subject of data they want to see. 'All' is the default, showing all details about the selected name.
So by default the 'All' template is populated.
If user wants to see the 'Location' details (template) they select it from the dropdownlist.
The template shows but the values are all blank. The only way to populate it is to click the name (button) again.
I want to remove the need for having to re-click the button (name) to populate the template ('Location', etc...).
I have put together a JSFiddle showing the structure. Though due to the data being private and served over secure network I cannot access it.
Refer to JSFiddle:
I believe the issue is that the onclick event grabs the data-uid and passes it to the initial default template (named 'All' but it's not included in code as it's lengthy). When the user changes the dropdownlist (cboDetailsCategory) and selects a new template I lose the data.
Thanks for your help. I'm really stuck on this and it's a current show stopper.
There isn't an officially supported way to change templates, without destroying the listview and rebuilding it. However, if you don't mind poking into into some private api stuff (be warned I can't guarantee that kendo won't break it without telling you) you can do this
var listview = $("#MyListview").getKendoListView();
listview.options.template = templateString;
listview.template = kendo.template(listview.options.template);
//you can change the listview.altTemplate the same way
listview.refresh(); //redraws the elements
if you want to protect against unknown API changes you can do this, which has A LOT more overhead, but no risk of uninformed change (untested!)
var listview = $("#MyListview").getKendoListView(),
options = listview.options;
options.dataSource = listview.dataSource;
listview.destroy();
$("#MyListview").kendoListView(options);
Here's the solution, thanks for everyone's help!
JSFiddle Link
The issue was where I was setting the bind:
$("#list").on("click", ".k-button", function (e) {
var uid = $(e.target).data("uid");
var item = dataSource.getByUid(uid);
var details = dropdown.value();
var template = $("#" + details).html();
$("#details").html(template);
kendo.bind($("#details"), item);
currentData = item;
});