I would like to run some specific code around put() and add() for Dojo stores.
The problem I am having is that for JSON REST stores, in JsonRest.js add() is just a function that calls put():
add: function(object, options){
options = options || {};
options.overwrite = false;
return this.put(object, options);
},
So, if I use aspect.around() with add(), my code ends up being executed twice IF I apply my code to stores created with a store that implements add() as a stub to put().
Please note that I realise that most stores will do that. I just want my solution to be guaranteed to work with any store, whether there is method nesting or not.
Dojo's own Observable.js has the same problem. This is how they deal with it:
function whenFinished(method, action){
var original = store[method];
if(original){
store[method] = function(value){
if(inMethod){
// if one method calls another (like add() calling put()) we don't want two events
return original.apply(this, arguments);
}
inMethod = true;
try{
var results = original.apply(this, arguments);
Deferred.when(results, function(results){
action((typeof results == "object" && results) || value);
});
return results;
}finally{
inMethod = false;
}
};
}
}
// monitor for updates by listening to these methods
whenFinished("put", function(object){
store.notify(object, store.getIdentity(object));
});
whenFinished("add", function(object){
store.notify(object);
});
whenFinished("remove", function(id){
store.notify(undefined, id);
});
My question is: is there a simple, "short" way to change my existing code so that it checks if it's within a method, and avoid running the code twice?
I gave it a go, but I ended up with clanky, hacky code. I am sure I am missing something...
Here is my existing code:
topic.subscribe( 'hotplate/hotDojoStores/newStore', function( storeName, store ){
aspect.around( store, 'put', function( put ){
return function( object, options ){
return when( put.call( store, object, options ) ).then( function( r ) {
var eventName;
var identity = store.idProperty;
eventName = object[ identity ] ? 'storeRecordUpdate' : 'storeRecordCreate';
topic.publish( eventName, null, { type: eventName, storeName: storeName, objectId: r[ identity ], object: object }, false );
} );
}
});
aspect.around( store, 'add', function( add ){
return function( object, options ){
return when( add.call( store, object, options ) ).then( function( r ) {
var identity = store.idProperty;
topic.publish('storeRecordCreate', null, { storeName: storeName, storeTarget: storeTarget, objectId: r[identity], object: object }, false } );
});
}
});
});
This is my attempt...
What I don't really "get" about my attempt is whether it's 100% safe or not.
If store.add() is called twice in a row, is is ever possible that inMethod is set to true by the first call, and that the second add() call then finds it already set to true because the first one hasn't managed to set it to false yet?
This would only really be possible if nextTick() is called in between the two calls I assume?
Or am I just completely confused by it all? (Which is very possible...)
topic.subscribe( 'hotplate/hotDojoStores/newStore', function( storeName, store ){
var inMethod;
aspect.around( store, 'put', function( put ){
return function( object, options ){
if( inMethod ){
return when( put.call( store, object, options ) );
} else {
inMethod = true;
try {
return when( put.call( store, object, options ) ).then( function( r ) {
var eventName;
var identity = store.idProperty;
eventName = object[ identity ] ? 'storeRecordUpdate' : 'storeRecordCreate';
topic.publish( eventName, null, { type: eventName, storeName: storeName, objectId: r[ identity ], object: object }, false );
});
} finally {
inMethod = false;
}
}
}
});
aspect.around( store, 'add', function( add ){
return function( object, options ){
if( inMethod ){
return when( add.call( store, object, options ) );
} else {
inMethod = true;
try {
return when( add.call( store, object, options ) ).then( function( r ) {
var identity = store.idProperty;
topic.publish('storeRecordCreate', null, { type: 'storeRecordCreate', storeName: storeName, objectId: r[identity], object: object }, false );
});
} finally {
inMethod = false;
}
}
}
});
aspect.around( store, 'remove', function( remove ){
return function( objectId, options ){
return when( remove.call( store, objectId, options ) ).then( function( r ) {
topic.publish('storeRecordRemove', null, { type: 'storeRecordRemove', storeName: storeName, objectId: objectId }, false );
});
};
});
});
Related
I'm probably not using promises correctly, but I'm calling a service (defined in a separate factory) into my controller like so,
_.each( $scope.aap.idList, function( id ){
MyService.getItems( $stateParams.name, id ).$promise.then(
function( scan ){
if( scan.length !== 0){
addToList( scan );
}
})
});
var addToList = function ( item ) {
$scope.aap.scans.push( _.clone( item ) );
return $scope.aap.scans;
};
The service is defined as:
service.getItems = function ( name, id ) {
var Items = $resource( '/proj/rs/pr/elems/getItems', {
id: Id,
name: name
}, {
query: {
method: 'GET',
isArray: true,
transformResponse: function ( scans ) {
var items = angular.fromJson( scans );
_.each( items, function ( scanItem ) {
scanItem.checked = false;
} );
return items;
}
}
} );
return Items.query();
Now, in MyService.getItems I can add a console.log( scan ) and see the appropriate objects being passed through, but it won't add these items to my list. Is there some other way I should be going about this?
Hello I'm parsing some data from my database into a table using aui-datatable plugin however I would like to add some basic sorting in my columns. Bellow you may see the function "renderProducts" which renders the data-table. Furthermore bellow is a sample column object that is being passed into the table renderer.
var API= (function(Y){
var settings = {
serviceURL : null
};
var getServiceAttribute = function( attribute ) {
return '_openpimadmin_WAR_OpenTest_' + attribute;
}
var getServiceURL = function( service ) {
return settings.serviceURL + '&p_p_resource_id=' + service;
};
var service = function( service, dataSet, handlers ){
var serviceData = {};
var serviceHandlers = {
start : function(){},
success : function(){},
failure : function(){},
end : function(){}
};
for (prop in dataSet) {
serviceData[getServiceAttribute(prop)] = dataSet[prop];
}
for (prop in handlers) {
if ( serviceHandlers.hasOwnProperty(prop) && typeof handlers[prop] === 'function') {
serviceHandlers[prop] = handlers[prop];
}
}
Y.io(
getServiceURL( service ),
{
method : 'POST',
data : serviceData,
on : {
start : function( transactionID ) {
serviceHandlers.start(transactionID);
},
success : function( transactionID, response ) {
var parsed = Y.JSON.parse(response.responseText);
if (parsed.success === true){
serviceHandlers.success( transactionID, parsed );
} else {
console.log('Service [' + service + '] error: ' + parsed.error);
}
},
failure : function( transactionID, response ) {
serviceHandlers.failure( transactionID, response );
},
end : function( transactionID ) {
serviceHandlers.end( transactionID );
}
}
}
);
}
return {
services : {
getProducts : function( dataSet, handlers ){
dataSet = dataSet || {};
handlers = handlers || {};
service( 'getProducts', dataSet, handlers );
},
getProductsTableAttributeHeaders : function( dataSet, handlers) {
dataSet = dataSet || {};
handlers = handlers || {};
service( 'getProductsTableAttributeHeaders', dataSet, handlers );
},
},
views : {
renderProducts : function( el, columns, dataSet ) {
Y.one(el).get('childNodes').remove();
new Y.DataTable.Base({
columnset : columns,
recordset : dataSet,
width: '100%'
}).render(el);
}
},
get : function( prop ) {
return settings[prop];
},
set : function( options ) {
settings = Y.merge( settings, options );
}
};
})( YUI().use('node', 'io', 'aui-datatable', 'datatable-sort', function(Y){return Y;}) );
Column object:
Object {key: "name", label: "Όνομα", allowHTML: true, emptyCellValue: "<em>(not set)</em>", sortable: true}
The issue I'm facing is that sorting is nowhere to be seen, columns dont seem interactive and the user is not able to sort them, although the table is being rendered fine.
Thank you in advance.
PS: I'm new to YUI().
You need to make sure that sortable: true set for column that need sorting.
Here is a real world example with sorting feature
It seems this was the problem:
new Y.DataTable.Base({
columnset : columns,
recordset : dataSet,
width: '100%'
}).render(el);
Should have been like this:
new Y.DataTable({
columnset : columns,
recordset : dataSet,
width: '100%'
}).render(el);
I got this response from ajax
{
"laborCostIndex":0.0,
"laborDailyWage":0.0,
"laborHourlyWage":0.0,
"laborMonthlyWage":0.0,
"laborLeave":0.0,
"laborBonus":0.0,
"laborSSS":0.0,
"laborPagIbig":0.0,
"laborPhilHealth":0.0,
"laborEMP":0.0,
"laborTotalMonthlyRate":110.0,
"laborTotalDailyRate":4.230769230769231,
"laborTotalHourlyRate":0.5288461538461539
}
I'm trying to access the element inside through this:
response.laborCostIndex and response['laborCostIndex'] but seems doesn't work for me.
The ajax is from the xeditable here is the code:
UPDATE: posted the whole ajax
$('.laborCostIndex').editable({
pk: '1',
name: 'laborCostIndex',
url: '../BTool/edit_laborCostIndex.do',
validate: function( value ) {
if($.trim( value ) == '') {
return 'This field is required';
} else if( isNaN( value ) ) {
return 'Only accepts numbers';
}
},
params: function(params) {
var basicDailyWage = $(this).closest('td').next('td').find('a').text();
var pagIbig = $(this).closest('tr').find(':nth-child(11)').find('a').text();
var emp = $(this).closest('tr').find(':nth-child(13)').find('a').text();
var datas = new Array(basicDailyWage, pagIbig, emp);
params.pk = datas;
return params;
},
success: function(response, newValue) {
console.log( response );
console.log( response.laborCostIndex );
console.log( response['laborCostIndex'] );
}
});
Both results to undefined, I don't know why.
Try this in your success function
var obj = JSON.parse(response);
console.log( obj.laborCostIndex);
I've been using javascript for years, and gotten pretty good at it since I started a web development job, but working with node on a personal project I am having issues.
I create an object
;( function( undefined ) {
'use strict';
var scrape = {
data : {},
scrapers: [],
init : function( coins, exchanges, trade_paths ) {
scrape.start_time = new Date().getMilliseconds();
// Load events
var Event = require( 'events' );
// Add an eventEmitter to process
process.event = new Event.EventEmitter()
// Now we can load any modules, now that global process is modified ;)
//require( './bter.js' );
exchanges.forEach( function( exchange ) {
console.log( exchange.name + '.js' );
require( exchange.name.toLower() + '.js' );
} );
// Trigger the preload event
process.event.emit( 'scraper::init', coins );
// Response to all modules processes
process.event.on( 'scraper::add', scrape.add );
},
add : function( module ) {
scrape.data[module.name] = module.data;
}
};
// Get list of exchanges, coins, and trade paths
var sql_data = {
sql : {},
db : {},
coins : [],
exchanges : [],
trade_paths : [],
init : function() {
sql_data.sql = require( 'mysql' );
sql_data.db = sql_data.sql.createConnection( {
host : '127.0.0.1',
user : 'root',
password : ''
} );
sql_data.db.connect();
// Get coin list
sql_data.db.query('SELECT * FROM `node`.`coins` WHERE active=true', function(err, rows, fields) {
if( typeof rows !== 'undefined' ) {
sql_data.coins = rows;
}
// Oddly, if no error, its equal to null.
if( err !== null ) {
console.log( err );
}
} );
// Get exchange list
sql_data.db.query( 'SELECT * FROM `node`.`exchanges` WHERE active=true', function( err, rows, fields ) {
if( typeof rows !== 'undefined' ) {
sql_data.exchanges = rows;
}
if( err !== null ) {
console.log( err );
}
} );
// Get trade paths
sql_data.db.query( 'SELECT * FROM `node`.`trade_paths` WHERE active=true', function( err, rows, fields ) {
if( typeof rows !== 'undefined' ) {
sql_data.trade_paths = rows;
}
if( err !== null ) {
console.log( err );
}
} );
// Close connection to the database
sql_data.db.end();
}
};
sql_data.init();
// Start scraping
scrape.init( sql_data.coins, sql_data.exchanges, sql_data.trade_paths );
} () );
object.x is not accessible. Not even within object itself. I don't know what to do or how to fix this.
Your scrape.init function is called before you received data from mysql. You need to call it inside sql_data.init callback (which you can safely call in third .query() since mysql queries are executed sequentially per connection).
I'm currently building a simple application of example for Google Map API GeoCoding and stumbled upon a problem with javascript.
The geocodeRequest method should assign its result value to the variable this.tempResult.
However the varriable is null when I try to print it in Listener.
The output to console is:
Listener: null
geoCodeRequest: Object
The order how output is printed seems to imply that the code in Listener run ahead before the geoCodeRequest method manage to assign the this.tempResult variable.
Is there a solution for this?
$JSKK.Class.create
(
{
$namespace :'application',
$name :'GeoCoder'
}
)
(
{
},
{
service :null,
main :null,
geoCodeInput: null,
geoCodeButton: null,
reverseGeoCodeButton: null,
reverseGeoCodeActive: null,
callback :null,
reverseCallback:null,
tempResult: null,
init: function(main, callback, reverseCallback)
{
this.main = main;
this.callback = callback;
this.reverseCallback = reverseCallback;
this.service = new google.maps.Geocoder();
this.geoCodeInput = $('#toolPannel div[data-resource=locator] input[data-action=input]');
this.geoCodeButton = $('#toolPannel div[data-resource=locator] input[data-action=geocode]');
this.reverseGeoCodeButton = $('#toolPannel div[data-resource=locator] input[data-action=reversegeocode]');
this.reverseGeoCodeActive = false;
this.createListener();
},
geoCodeRequest: function(request)
{
this.service.geocode
(
request,
function (result,status)
{
//console.debug(arguments);
if (status== google.maps.GeocoderStatus.OK)
{
this.tempResult = result[0];
console.debug(this.tempResult);
}
else
{
alert ('GeoCoder request failed!');
}
}.bind(this)
);
},
createListener: function()
{
this.geoCodeButton.click(function()
{
this.geoCodeRequest
(
{
address: this.geoCodeInput.val()
}
);
this.callback(this.tempResult);
}.bind(this) //Bind here
);
this.reverseGeoCodeButton.click(function()
{
if (!this.reverseGeoCodeActive)
{
this.main.map.setOptions({draggableCursor:'crosshair'});
this.reverseGeoCodeActive=true;
}
else if(this.reverseGeoCodeActive)
{
this.main.map.setOptions({draggableCursor:'hand'});
this.reverseGeoCodeActive=false;
}
}.bind(this)
);
google.maps.event.addListener
(
this.main.map,
'click',
function (event)
{
if (this.reverseGeoCodeActive)
{
this.geoCodeRequest
(
{
location: event.latLng
}
);
console.debug(this.tempResult);
this.reverseCallback(this.tempResult);
}
}.bind(this)
);
}
}
);
The problem is this code:
geoCodeRequest: function( request ) {
this.service.geocode( request, function( result, status ) {
if (status == google.maps.GeocoderStatus.OK) {
this.tempResult = result[0];
console.debug( this.tempResult );
} else {
alert( 'GeoCoder request failed!' );
}
}.bind(this) );
},
this.geoCodeButton.click( function() {
this.geoCodeRequest({
address: this.geoCodeInput.val()
});
this.callback( this.tempResult );
}.bind(this) );
(Sorry, but I took the liberty of reformatting so I could follow the logic better. Feel free to convert back to your own style.)
You are trying to call this.callback() before the geocoded result comes back from the server. That won't work. You need to handle the geocoding result in the callback that the geocoder uses.
You're already providing the geocoder a callback in the geoCodeRequest() method, so what you can do is add a callback to your click handler and call that from the callback in geoCodeRequest(). You could do that like this:
geoCodeRequest: function( request ) {
this.service.geocode( request, function( result, status ) {
if (status == google.maps.GeocoderStatus.OK) {
request.success( result[0] );
} else {
request.failure( status );
}
}.bind(this) );
},
this.geoCodeButton.click( function() {
this.geoCodeRequest({
address: this.geoCodeInput.val(),
success: function( result ) {
console.debug( result );
this.callback( result );
},
failure: function( status ) {
alert( 'GeoCoder request failed!' );
}
});
}.bind(this) );
Note that I also added a failure callback to handle the error condition.
The reverseGeoCodeButton.click() handler has the same problem and can be fixed with the same solution.
This is close to your original approach, but I have to wonder if the code could be simplified. Do you need these multiple levels of code? In any case, wherever you're dealing with an asynchronous result like what the geocoder gives you, you do have to handle that inside the appropriate callback instead of after the geocoder (or any asynchronous function) returns.