Mootools Request.JSON dynamic callback - javascript

Developing an app where all tabular data is returned as an object.
Some cells will have onclick events on them.
The JSON object is coming back fine and there is a key in the object call 'cb'.
This is set by the logic on the server.
My question is the object key cb will contain a string, how can I run that as a valid function without using eval()
Example:
var archive = function() {
console.log('archiving');
}
new Request.JSON ({
...
onSuccess: function(r){
//r.cb: 'archive'
docuemnt.id(td).addEvent('click', r.cb);
}
...
});
docuemnt.id(td).addEvent('click', eval(r.cb)); // works looking for alternative
I know i am over thinking this and it should not be that difficult.
Must not have had enough coffee yet today.

Use square bracket notation. If your function is in the global scope, use window[r.cb]:
new Request.JSON ({
...
onSuccess: function(r) {
//r.cb: 'archive'
document.id(td).addEvent('click', window[r.cb]);
}
...
});
If your function is not in the global scope, move your functions into an object:
var callbacks = {
archive: function () { ... },
foo: function () { ... },
...
}
Then use callbacks[r.cb].

Related

Accessing Svelte component properties in a callback?

Imagine that you have a lot of properties in a component:
let a = 'foo';
let b = 'bar';
// ...
let z = 'baz';
You then want to do something like update all of them from an external callback, like in another library (i.e. something that isn't and can't be a Svelte component itself).
A simple use case is just an AJAX method to load in a bunch of data (assume this ajax function works and you can pass it a callback):
onMount(async function() {
ajax('/data', function(data) {
a = data.a;
b = data.b;
// ...
z = data.z;
});
});
This works, but it's incredibly boilerplaty. What I'd really like is a way to loop through all the properties so they can be assigned to programmatically, especially without prior knowledge on the outside library/callback's part.
Is there no way to get access to a Svelte component and its properties so you can loop through them and assign them from an outside function?
Vue has a simple solution to this, because you can pass the component around, and still check and assign to its properties:
var vm = this;
ajax('/data', function(data) {
for (var key in data) {
if (vm.hasOwnProperty(key)) {
vm[key] = data[key];
}
});
});
I have seen some solutions to this, but they're all outdated - none of them work with Svelte 3.
Apologies if this has been asked before. I've spent days trying to figure this out to avoid all that extra boilerplate and the closest I could find is Access Component Object in External Callback? which does not have an answer right now.
If possible, you could put the ajax call in the parent component and have the data returned from it stored in a temporary object, that you then pass on to the component using the spread operator.
<Component { ...dataObject }></Component>
let dataObject = {};
onMount(async function() {
ajax('/data', function(data) {
dataObject = data;
});
});
You can reduce the boilerplate by using destructuring:
onMount(async function() {
ajax('/data', data => {
({ a, b, ..., z } = data);
});
});
But if you have a very large number of variables, you might be better off just putting them in an object in the first place:
let stuff;
onMount(async function() {
ajax('/data', data => {
stuff = data;
});
});

Confusion on how to work with module pattern

I am confused on how to work with module pattern (and design patterns in general) in JavaScript.
I already wrote some functioning code in my application using module pattern that does what I want to, but it doesn't seem to be very modular to me, and I keep having this feeling that I am doing it wrong. I didn't manage to find any concrete and complete application example with any design pattern.
Here is how I work with it :
Let's say I have forms in my application that I'll use for different modules (post a thread, reply to a thread, comment the guests book), with some JavaScript I'll give users some functionalities, as such as popping a smiley bubble and handling insertion of them in my forms, sending data posts to my server code to return the HTML code in order to add the message without reloading the page, I'll do something like that:
let Form = function (selector_form, selector_textarea, selector_emoticonsButton, selector_postButton) {
let form, textarea, emoticonsButton, postButton;
let emoticonsBubble = new EmoticonsBubble()
return {
selectors: function () {
return {
form: function () { return selector_form },
sendButton: function () { return selector_sendButton }
}
}
setElements: function (obj) {
form = $(obj).get(0);
textarea = $(form).find(selector_textarea).get(0);
emoticonsButton = $(form).find(emoticonsButton).get(0);
postButton = $(form).find(selector_postButton).get(0);
emoticonsBubble.setElements(form, emoticonsButton);
},
get: function () {
return {
form: function () { return form },
//...
emoticonsBubble: function () { return emoticonsBubble }
}
},
post: function (moduleId, callback) {
$.ajax({
//parameters
}).done(function (data) {
callback(data);
});
}
}
}
let EmoticonsBubble = function () {
let thisContainerToAppendTo, thisTextarea;
return {
setElements: function (container, textarea) {
thisContainerToAppendTo = container;
thisTextarea = textarea;
},
pop: function () {
this.ajax().pop(function (data) {
$(thisContainerToAppendTo).append(data);
});
}
insert: function (emoticon) {
$(thisTextarea).append(emoticon);
},
ajax: function () {
return {
pop: function (callback) {
$.ajax({
//parameters
}).done(function (data) {
callback(data);
});
}
}
}
}
}
// Events part
let form = new Form('#threadForm', '.textarea', 'button[name="emoticons"]', 'button[name="send"]');
let emoticonsBubble = form.get().emoticonsBubble();
$(form.selectors().form()).on('click', function (e) {
form.setElements(this);
});
$(form.selectors().sendButton()).on('click', function (e) {
let moduleId = // retrieve module id, if it belongs to guests book, thread creation module or reply module
form.post(moduleId, function (data) {
// append data to something
});
});
// etc for emoticons handling
The fact that I have to rewrite the event part for every different form I have in my application while keeping everything the same but variables name, annoys me a lot.
Could you guys tell me how you would handle those functionalities and what may be wrong with my way of coding?
The Module Pattern is about keeping units of code from colliding with other scopes (usually the Global scope).
As we know, in JavaScript, variables defined with:
let and const are scoped to their parent block
var are scoped to their containing function (or Global if not in a
function)
So, if you were to take your Form function:
let Form = function (x,y,z) {
let form, textarea, emoticonsButton, postButton;
let emoticonsBubble = new EmoticonsBubble()
return {
. . .
}
setElements: function (obj) {
. . .
},
get: function () {
. . .
},
post: function (moduleId, callback) {
. . .
}
}
}
The variable Form is Global because there is no containing block. This is a problem because what if there is already another Global called Form (which there very well could be because of the generic nature of the word "Form"). So, this code doesn't cut off your code from being exposed. To use the Module Pattern on it, we'd wrap it with an IIFE (Immediately Invoked Function Expression) and within that IIFE, we'd create a custom namespace in the Global scope that we're sure doesn't exist (thereby avoiding name collisions):
(function(){
// This is going to be exposed as publicly available via the module namespace
function Form(x,y,z) {
. . .
}
// This will remain private within the module
function helper(){
}
// **********************************************************************
let temp = {}; // Create a temporary object to bind only the public API
temp.Form = Form; // Bind the public members to the object
// Expose the module to the Global scope by creating a custom namespace
// and mapping the temp object to it
window.myCustomAPI = temp;
})();
// Now, outside of the module (in some higher scope), your public portions
// of the Module are accessible:
let myForm = new myCustomAPI.Form(arg, arg, arg);
The repetition in your code basically comes from the selection of elements and their helpers, and that can easily be abstracted into a function:
function Elements(selectors, children, options) {
let elements = { ...children };
return {
selectors,
elements,
setElements(obj) {
for(const [name, selector] of Object.entries(selectors))
elements[name] = $(obj).find(selector).get(0);
for(const child of Object.values(child))
child.parent && child.parent(this, obj);
},
...options
}
}
That can then be used as:
function Form(form, textarea, emoticonsButton, postButton) {
const emoticonsBubble = EmoticonsBubble();
return Elements({ form, textarea, emoticonButtons }, { emoticonsBubble }, {
post() {
//...
}
});
}
function EmoticonsBubble() {
return Elements({ /*...*/ }, {}, {
parent(parent, obj) {
this.setElements(parent);
}
});
}
But you are basically reinventing a lot of wheels here, have you thought about using one of the MVCs that are out there (React, Vue, ...) ?
Ok the boilerplate for some common tasks that you have in the event part is driving you crazy right ?
So checking your code you can fix them in many ways.
A. Encapsulate your code in real modules I mean this.
const Form = (function(/*receive here dependencies as arguments */){
// your code module goes here
})(/*inject dependencies here to module*/);
B. You can create a event pattern module, to drive your internal and externals events for module.
C. You know what are the listener that the module needs , so apply them into your module.
That way should be more reusable than now

angularjs this is undefined while using $.proxy

I have an UserApplications object wich queries the server for a list of applications the user is registered to looking like this:
data.factory('UserApplications', function($resource){
return $resource('/users-rs/api/getapplications/:locale',{},{
query: {method: 'GET', params: {locale: 'locale'}, isArray: true}
});
});
I call it in another service and want to save the data as a JSON string using the angular-localstorageservice module (https://github.com/grevory/angular-local-storage) which is passed to the constructor of the service like this:
function UserService(UserInfoData, localStorageService, UserApplications){
this.UserInfoData = UserInfoData;
this.localStorageService = localStorageService;
this.userApplications = UserApplications;
}
When I give a callback function to the $resource.query() function and wrap it with $.proxy I keep getting this.localstorag is undefined, when I debug it this is a reference to window. So not exactly the behaviour I expected.
Is there any other way to pass 'this' or a reference to the object to the callback function?
I've allready tried with creating a variable with a reference to this but it doesn't do the trick either :/
UserService.prototype.getUserApplications = function(){
var locale = this.getUserinfoLocale();
var applications = this.localStorageService.get(Constants.key_applications+locale);
if(applications !== null){
return JSON.parse(applications);
} else {
return this.userApplications.query({locale: locale}, $.proxy(function(data, locale){
this.localStorageService.add(Constants.key_applications+locale, JSON.stringify(data));
}), this);
}
};
I think you missed the comma position and is sending "this" to userApplications' query method, not jQuery's proxy one.
Try this in your "else" block instead:
return this.userApplications.query({locale: locale}, $.proxy(function(data, locale){
this.localStorageService.add(Constants.key_applications+locale, JSON.stringify(data));
}, this));

Code Structure with ExtJS

I'm trying to organize my ExtJS javascript a little better. I've an ExtJS object like this:
Ext.define('QBase.controller.ControlModelConfigurationController', {
extend: 'Ext.app.Controller',
views: [
'ControlModelConfiguration'
],
init: function() {
console.log('Initialized ControlModelConfigurationController');
this.control({
'#testBtn': {
click: this.loadModel
}
});
},
loadModel: function() {
console.log('Load Model....');
var conn = new Ext.data.Connection;
conn.request({
url: 'partsV10.xml',
callback: function(options, success, response)
{
if (success)
{
alert("AHHH");
var dq = Ext.DomQuery;
var xml = response.responseXML;
var nodes = dq.select('part', xml,parent);
Ext.Array.forEach(nodes,handleNode);
}
}
});
},
handleNode: function(items) {
console.log(item.name);
}
});
The posted code above is not working. Ext.Array.forEach(nodes,handleNode) causes trouble. Instead of using an anonymous function like :
...
Ext.Array.forEach(nodes,function(item) {
console.log(item)});
}
...
I'd like to extract the anonymous function as a named external one. Unfortunately I'm unable to figure out the right syntax to establish a code structure as shown above.
Meanwhile, I figured out, that putting
function handleNode(item) {
{console.log(item)}
}
at the very end of the file works. Is it possible to make the handleNode method an object - "member" of the controller?
Thanks in advance
Chris
handleNode is a member of the containing object. When loadModel is called, this contains the right object, but at the time the callback is invoked, it will not point to the one we are interested in. You can save this to the local variable self, and use it instead.
loadModel: function() {
var self = this
console.log('Load Model....');
var conn = new Ext.data.Connection;
conn.request({
url: 'partsV10.xml',
callback: function(options, success, response)
{
if (success)
{
alert("AHHH");
var dq = Ext.DomQuery;
var xml = response.responseXML;
var nodes = dq.select('part', xml,parent);
Ext.Array.forEach(nodes, self.handleNode);
}
}
});
},
The solution posted by vhallac is not entirely correct. It assumes that handleNode doesn't reference the current object through this variable. Maybe just a typo, but additionally it's not really the ExtJS way...
Whenever ExtJS provides a callback parameter, there is nearly always a scope parameter to set the value of this within the callback function.
loadModel: function() {
console.log('Load Model....');
var conn = new Ext.data.Connection;
conn.request({
url: 'partsV10.xml',
callback: function(options, success, response) {
if (success) {
alert("AHHH");
var dq = Ext.DomQuery;
var xml = response.responseXML;
var nodes = dq.select('part', xml, parent);
Ext.Array.forEach(nodes, this.handleNode, this);
}
},
scope: this
});
},
handleNode: function(node) {
// From within here you might want to call some other method.
// Won't work if you leave out the scope parameter of forEach.
this.subroutine();
}
Just like forEach uses a scope parameter, the request method uses a scope config option. This is ExtJS convention for passing around the scope. You can alternatively create an extra local variable and reference the scope from there, but in the context of ExtJS this style will feel awkward, plus (I'm pretty sure) it's a lot more bug-prone.

Access class members from nested functions

i have this class in javascript
var MyGird = Class.extend({
classMemeber1 : "Some Value"
,clickEvent : function(){
this.editor.on({
afteredit: function() {
//
// HOW TO I ACCESS classMemeber1 from here? ?
//
//
}
})
})
how do i access classMemeber1 from inside of afteredit...
Thanks
You need to save a reference to the object invoking clickEvent function by storing this [1] in a variable. It will be available inside the afteredit method because of closure.
var MyGird = Class.extend({
classMemeber1: "Some Value",
clickEvent: function () {
var self = this; // save object reference
this.editor.on({
afteredit: function () {
// access classMemeber1 from here
// by using the stored reference
alert(self.classMemeber1);
}
});
},
// ...
});
[1] this operator in javascript (note: 'this' is not an operator)
If you write ES6, you can use arrow functions: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
In your example, should be something like (not tested):
var MyGird = Class.extend({
classMemeber1: "Some Value",
clickEvent: () => {
this.editor.on({
afteredit: () => () {
alert(this.classMemeber1);
}
});
},
// ...
});
I had a similar problem and solved it using a library call cron.
this library allows you to schedule tasks very easily
here is an example of code i used to implement sending a message at a specific time
import {CronJob} from "cron";
sendMessageEveryMinute(message) {
let bot = this.bot;
let chatId = this.chatId;
let job = new CronJob('1 * * * * *',
function () {
bot.telegram.sendMessage(chatId, message).then(r => console.log(r));
}, null, true, 'Europe/Rome');
}
as you can see (minus the telegram methods) what we do is just declare an object called CronJob which takes as input the so called cron schedule expressions (which is used to define the actual time when the function we want to execute will execute) and the second parameter is the function itself we want to execute at that specific time.
The true param that you see is telling that the schedule will start at instantiation time. this means that the schedule will start as the object job gets instantiated.
For the other params look at the documentation:
cron documentation

Categories