Why isn't my data been added to the stash in mojolicious? - javascript

I'm having an issue with Mojolicious and the stash and I think I'm probably just not understanding the way it works?
I have a page with 2 combo boxes and when the first entry changes I wish to update the options in the second.
So I add an event handler like below, which then calls my controller sub routine 'devicecommandset' and then puts the result of a DBIx query into an array of hashes which I add to my stash.
I am then just render some benign text. My subroutine gets called and there is the expected contents in '#commandsets'. However I cannot see it in the stash on the browsers console ( I'm running in debug mode ).
Do I need to actually modify the DOM for the stash to be populated? Basically I'm just trying to get data back from my request to fill the combobox options.
In my template
$(document).ready(function() {
$('select:not([name*="command"])').live('change', function (e) {
$.get('devicecommandset', { device: $(this).attr("value") },
function (data) {
alert("Made it this far");
});
});
});
In my Controller
sub devicecommandset {
my $self = shift;
my $device = $self->param('device') || '';
my #commandsets = $self->db->resultset('CommandSet')->search_commandsets_by_devicename($device);
$self->stash(commandsets => \#commandsets );
print Dumper(#commandsets);
$self->render(text => 'success' );
}

You're printing a dumper to the log basically, not the browser. Your stash is not used in the render because you're not referencing it. Use inline render type and the "dumper" helper.
Try:
$self->stash(commandsets => \#commandsets );
$self->render( inline => '<%= dumper $commandsets %>' );

Related

Update a field on a Netsuite record using .set() not working

So I'm currently trying to update the title field on a NetSuite Quote record from an input on the front-end so a user can add the title themselves.
When submitting the quote I can set the title and see that it has updated the backbone model, however when the page loads the model has set the title back to null.
The functionality already exists with the memo area, and I have copied all the functions to replicated this for the title, however the memo continues to be updated, but the title does not.
I'm fairly new to the SuiteScripting and try to avoid it as much as I can, but I think the field isn't being updated and therefore not available in the model on other pages.
enter , update: function (record_type, id, data_model)
{
if (record_type && id)
{
this.recordId = id;
this.data = data_model;
this.record = this.getTransactionRecord(record_type, id);
//#property {Transaction.Model.Get.Result} currentRecord This property is used so when performing any update
//operation you can know what is the current state
//This property is only present when performing an update operation
this.currentRecord = this.get(record_type, id);
this.setPaymentMethod();
this.setLines();
this.setAddress('ship', this.data.shipaddress, 'billaddress');
this.setAddress('bill', this.data.billaddress, 'shipaddress');
this.setMemo();
// Set's our Quote title
this.setTitle();
// Set's our PO Number and SLC Number
this.setCustomFields();
}
}
/**
* Adds fields to the Sales Order when generated via Quotes (possibly via other methods as well)
*/
, setCustomFields: function ()
{
if (this.data.custbody_slc_number) {
this.record.setFieldValue('custbody_slc_number', this.data.custbody_slc_number);
}
if (this.data.otherrefnum) {
this.record.setFieldValue('otherrefnum', this.data.otherrefnum);
}
}
/**
* #method setTitle Sets the title attribute into the current transaction
* #return {Void}
*/
, setTitle: function ()
{
this.record.setFieldValue('title', null);
if (this.data.title)
{
this.record.setFieldValue('title', this.data.title);
}
}
//#method setMemo Sets the memo attribute into the current transaction
//This method does not use any parameters as it use this.data and this.record
//#return {Void}
, setMemo: function ()
{
this.record.setFieldValue('memo', null);
if (this.data.memo)
{
this.record.setFieldValue('memo', this.data.memo);
}
}
This is where the setTitle() function is created and called on update.
, submit: function () {
this.wizard.model.set('memo', this.$('[data-type="memo-input"]').val());
this.wizard.model.set('title', this.$('[data-type="title-input"]').val());
console.log(this.wizard.model);
return jQuery.Deferred().resolve();
}
And this is the submit function.
So on submit the model is being updated using the .set() function, however it is not saving this field to the record.
I've been banging my head against this for a while now and I can't see to figure out what different between the setMemo and the setTitle.
Any help would be amazing, I would love to understand how to add data to records from the front-end but it's quite a maze to work out whats going on.
Thanks!
UPDATE
I've traced the data path back from the view submit function, to the configuration, and then to the submit to the server.
I have can see that when logging the variable through these stages, the title field gets set and saved until the page reloads to the quote confirmation page where in the 'changed' section of the model the title is redefined to 'null'.
Here is the submit function that submits the model:
, save: function ()
{
_.first(this.moduleInstances).trigger('change_label_continue', _('Processing...').translate());
var self = this
, submit_opreation = this.wizard.model.submit();
submit_opreation.always(function ()
{
_.first(self.moduleInstances).trigger('change_label_continue', _('Submit Quote Request').translate());
});
return submit_opreation;
}
}
I believe that it's a suitescript issue and I don't have a lot of experience with suitescript
setTitle: function ()
{
// this.record.setFieldValue('title', null);
if (this.data.title)
{
this.record.setFieldValue('title', this.data.title);
}
}
This function sets the title to 'null' however when hard coding a value here or just removing the if statement the title is still set to 'null'.
As I'm new to SuiteScripting, could anyone also point out how I could log 'this.data'?
From what I can see the submit-function maps the input-values correctly to the (local) model ("I can [...] see that it has updated the backbone model"), but it does not finally save that model to the (remote) database after clicking on the submit button ("however when the page loads the model has set the title back to null."). This would usually be done by implementing the Backbone.Model.save - method.
You could therefore try to change your submit-function to the following:
submit: function () {
this.wizard.model.set('memo', this.$('[data-type="memo-input"]').val());
this.wizard.model.set('title', this.$('[data-type="title-input"]').val());
var dff = jQuery.Deferred();
// Send model state to database
this.wizard.model.save(null, {
success : function(data, response) {
console.log('success: model saved', data);
dff.resolve(data);
},
error : function() {
console.log('error: model not saved');
dff.reject();
}
}
return dff.promise();
}

how do i implement a "load more" button for meteor collection

******* UPDATE ********
I've asked another question using the suggestion below... but using Iron-Router RouteController
call method in Iron-Router RouteController from Template.tmpl.helpers() not working
Basically what I want to do is have a button that will load more records into my collection on the client. I have an unknown number of records in the db, but I only want the latest 500 or so sent on load to the client.
I'm trying to emulate a "continue search on server" functionality. I'm not looking for a pagination solution.
Suggestions?
------- edited to add code ------------
this is in my /server/publications.js file:
Meteor.publish('clients', function( requestOptions ){
check(requestOptions, Object)
var options = _.extend(opts, requestOptions)
return Clients.find(baseQuery, options)
})
and this is in my /lib/router.js file:
Router.route('/clients', {
name : 'clientList',
waitOn : function(){
return Meteor.subscribe('clients', {limit:500})
}
})
basically i want to show the last 500 new clients, but allow the end-user to "load more" or "load all". i'm not sure how to do that reactively with a subscription...
Attach this event handler to the template that hosts your button:
Template.clients.events({
"click .load-more-button": function (event, template) {
template.skipped += 500;
template.subscriptions.push(Meteor.subscribe("client", {
limit: 500,
skip: template.skipped
}));
}
});
Template.clients.created = function () {
this.subscriptions = [];
this.skipped = 0;
};
//Stop the subscriptions when template is destroyed
Template.clients.destroyed: function () {
_.invoke(this.subscriptions, "stop");
};
As long as your client side cursor does not have a limit set, it should work, however it also means that any clients loaded by other templates etc will appear on the list.

CKEditor: HowTo destroy instance on blur?

I have a single html page with multiple div elements on it. Each time a user clicks on on a div, it is replaced with an CKEditor on the fly:
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
});
Now, if the CKEditor instance loses its focus (blur event), I need to post the content to a separate script via ajax (if something changed) and destroy this instance:
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
if (e.editor.checkDirty())
// get data with e.editor.getData() and do some ajax magic
e.editor.destroy();
});
});
But this example won't work because, I don't know why, destory() will be called before checkDirty(). How can I get this working?
How about if you put the destroy() inside the if() statement? You could have an else clause that invokes destroy if nothing has changed. If something has changed, you can invoke destroy() within the if clause once the data has been transfered.
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
if (e.editor.checkDirty()) {
// get data with e.editor.getData() and do some ajax magic
if ( dataTransferComplete ) {
e.editor.destroy();
}
} else {
e.editor.destroy();
}
});
});
Or you could check a variable before invoking destroy(). Set that variable to true after the data transfer has been completed and in the else clause, that way destroy() won't be invoked until you've checked for changes and transfered any updated data.
$('.editable').click(function() {
editor = CKEDITOR.replace(this);
editor.on('blur', function(e)
{
var okToDestroy = false;
if (e.editor.checkDirty()) {
// get data with e.editor.getData() and do some ajax magic
okToDestroy = true;
} else {
okToDestroy = true;
}
if (okToDestroy )
e.editor.destroy();
});
});
This is an outline, I haven't tested the code, but if shows the concept.
Be Well,
Joe

how to wait for loading data into the store

There are menu button ("clients"), tree panel with clients list (sorted by name) and viewer with selected client details. There is also selectionchange action..
My task - on button click switch to client view and select and load details for first client every time button has been clicked. My problem - store is not loaded, how waiting until ext js will autoload data to the store?
my controller code:
me.control({
'#nav-client': {
click: me.onNavClientClick
},
...
'clientlist': {
// load: me.selectClient,
selectionchange: me.showClient
}
});
onNavClientClick: function(view, records) {
var me = this,
content = Ext.getCmp("app-content");
content.removeAll();
content.insert(0, [{xtype: 'clientcomplex'}]);
var first = me.getClientsStore().first();
if (first) {
Ext.getCmp("clientList").getSelectionModel().select(me.getClientsListStore().getNodeById(first.get('clientId')));
}
},
...
Two main questions:
is it good solution in my case? (to select first client in tree panel)
var first = me.getClientsStore().first();
// i use another store to get first record because of i dont know how to get first record (on root level) in TreeStore
...
Ext.getCmp("clientList").getSelectionModel().select(me.getClientsListStore().getNodeById(first.get('clientId')));
i know this code works ok in case of "load: me.selectClient," (but only once),
if i place this code on button click - i see error
Uncaught TypeError: Cannot read property 'id' of undefined
because of me.getClientsListStore() is not loaded.. so how to check loading status of this store and wait some until this store will be completely autoloaded?..
Thank you!
You can listen the store 'load' event. Like this:
...
onNavClientClick: function(view, records) {
var me = this;
// if the store isn't loaded, call load method and defer the 'client view' creation
if (me.getClientsStore.getCount() <= 0) {
me.getClientsStore.on('load', me.onClientsStoreLoad, me, { single : true});
me.getClientsStore.load();
}
else {
me.onClientsStoreLoad();
}
},
onClientsStoreLoad : function () {
var me = this,
content = Ext.getCmp("app-content");
content.removeAll();
content.insert(0, [{xtype: 'clientcomplex'}]);
var first = me.getClientsStore().first();
if (first) {
Ext.getCmp("clientList").getSelectionModel().select(me.getClientsListStore().getNodeById(first.get('clientId')));
}
},
...

jQuery ajax function not working in Safari (Firefox, Chrome, IE okay)

I'm no javascript wiz, but am a bit puzzled as to how this is working in three major browsers, but not Safari... is there something wrong with this code? Basically I'm just using this in conjunction with a php/mysql callback at the given url to track link clicks.
Drupal.behaviors.NodeDownloadCounter = function() {
$('a.ndc-link').click(function() {
$.post('http://www.pixeledmemories.com/node-download-counter/log/' + this.name);
return true;
});
};
Using Drupal behaviors here instead of
$(document).ready(function() {
(correct Drupal method) but I've tried it both ways and it doesn't make a difference.
I've also tried removing "return true", but with no effect.
Okay, further testing reveals that having the click trigger an alert DOES work in Safari:
$('a.ndc-link').click(function() {
alert('testing (ignore)');
$.post('http://www.pixeledmemories.com/node-download-counter/log/' + this.name);
return true;
});
But still nothing being logged to mysql. Here is my callback function:
function node_download_counter_log($nid)
{
global $user;
$timestamp = time();
$title = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $nid));
db_query("INSERT INTO {node_download_counter} (nid, title, download_count, last_download, last_uid) VALUES (%d, '%s', %d, %d, %d)
ON DUPLICATE KEY UPDATE download_count=download_count+1, last_download = %d, last_uid = %d", $nid, $title, 1, $timestamp, $user->uid, $timestamp, $user->uid);
db_query("INSERT INTO {node_download_counter_log} (nid, title, uid, timestamp) VALUES (%d, '%s', %d, %d)", $nid, $title, $user->uid, $timestamp);
}
Sounds like the problem is the browser is changing the page before the data post can be finished. You can try adding return false to see if it starts working then. If it does, you are going to need to add a short delay before following the link.
UPDATE:
Since it works try adding the following before "return true;"
if(jQuery.browser.safari){
setTimeout("window.location.href= '"+this.href+"'",500);
return false;
}
Okay, based on our conversation on comments above, try
$('a.ndc-link').click(function() {
var href = this.href;
$.post('http://www.pixeledmemories.com/node-download-counter/log/' + this.name,
function() {
window.location.href = href;
}
);
return false;
});
Firs,t you have to be careful not to attach your handler more than once to each 'a.ndc-link', one way to do it is to tag the elements with a custom class.
Drupal.behaviors.NodeDownloadCounter = function() {
$('a.ndc-link:not(.node-download-counter-processed)').addClass('node-download-counter-processed').click(function(event) {
// ...
});
};
One reason I see for this not to work is that, because it closes the page to open the link target, Safari will cancel the $.post request before it is actually sent to the server. Returning false and calling event.preventDefault (event being the first argument of your event handler) should prevent this from happening but will also prevent the browser to actually load the link's target. One way to solve this is to defer the page change until the POST request is complete.
Drupal.behaviors.NodeDownloadCounter = function() {
$('a.ndc-link:not(.node-download-counter-processed)').addClass('node-download-counter-processed').click(function(event) {
var link = this;
event.preventDefault();
$.post('http://www.pixeledmemories.com/node-download-counter/log/' + this.name, function() {
window.location.href = link.href;
});
return false;
});
};
But this will only works if there is no error in the POST request.
A better solution would be to hijack the server-side handler for the link target to add the click logging and then call the original handler.

Categories

Resources