Here is the pseudo-code in question: https://jsfiddle.net/yzps2gef/40/
I'm trying to understand why I cannot access an object's properties directly in one scenario (see ISSUE #1 in comments) but I can in another scenario (see ISSUE #2 in comments). I'm failing to see the difference between the two. Thanks!
Here's the fiddle code:
window.DataStore = function () {
var url = new Url(),
filters = new Filters(),
orderBy,
orderByDir,
setOrderBy = function (x, y) {
orderBy = x;
orderByDir = y;
},
getOrderBy = function () {
return orderBy;
},
getOrderByDir = function () {
return orderByDir;
};
return {
url: url,
filters: filters,
orderBy: orderBy,
orderByDir: orderByDir,
setOrderBy: setOrderBy,
getOrderBy: getOrderBy,
getOrderByDir: getOrderByDir
};
};
window.Url = function () {
var get = function (ds) {
var url = 'xyz.php';
console.log(ds);
// ISSUE #1: These do not work. It results in: xyz.php?orderby=undefined&orderbydir=undefined.
// Why can't I access them directly like I do below with the dataStore.filters.someFilterOption?
url = url + '?orderby=' + ds.orderBy;
url = url + '&orderbydir=' + ds.orderByDir;
// These work when I use the "get" functions.
// url = url + '?orderby=' + ds.getOrderBy();
// url = url + '&orderbydir=' + ds.getOrderByDir();
return url;
}
return {
get: get
};
};
window.Filters = function () {
var someFilterOption = 0;
return {
someFilterOption: someFilterOption
};
};
window.Grid = function () {
var dataStore = new DataStore(),
doSearch = function () {
console.log(dataStore.url.get(dataStore));
},
render = function () {
doSearch();
// ISSUE #2: Why can I access this one directly but not the order bys?
if (dataStore.filters.someFilterOption) {
console.log('Why was I able to read this one (dataStore.filters.someFilterOption) directly and not have to have a getSomeFilterOption() function to read it? But when it comes to the orderBy and orderByDir above I cannot read them directly.');
}
}
return {
dataStore: dataStore,
render: render
};
};
window.MyReUsableGrid = function () {
var grid = new Grid(),
showSomeFilterOption = function () {
grid.dataStore.filters.someFilterOption = 1;
},
render = function () {
grid.render();
};
grid.dataStore.setOrderBy(4, 'asc');
return {
showSomeFilterOption: showSomeFilterOption,
render: render
};
};
// The Screen
var myGridScreen = new MyReUsableGrid();
myGridScreen.showSomeFilterOption();
myGridScreen.render();
Because when your object gets returned from the function this line gets evaluated:
orderBy: orderBy,
And as the variable orderBy isnt set yet it is actually:
orderBy: undefined
Now later you call setOrderBy and set the internal variable orderBy to a value which you can expose through the getter, but that doesnt get reflected to the objects property.
IMO the whole thing should be restructured so that the methods work with their context:
window.DataStore = () => ({
url: new Url(),
filters: new Filters(),
applyOrder(order, dir) {
this.orderBy = order;
this.orderByDir = dir;
},
});
That way you dont need getters at all.
Related
So I'm trying to refactor a function that goes something like this:
if (typeof init_common_load_more != "function") {
init_common_load_more = function (id) {
fdxm.js_manager.on("fdx_loadmore_js", function () {
component_wrapper = document.getElementById(id);
const loader = new fdxLoadMore({
posts_counter:
component_wrapper.querySelector(".posts_counter"),
load_more_posts_url: function (instance) {
let post_list_obj = instance.getObject("posts_list"),
load_options = {
controller:
post_list_obj.dataset["load_more_controller"],
//...other options
};
let url = "/api/posts/load_more_posts";
for (option in load_options) {
url += "/" + option + "/" + load_options[option];
}
return url;
},
});
// do things wiht the loader
});
};
}
This somehow works. The problem arises when I try to make the "load_more_posts_url" function a separate function:
if (typeof init_common_load_more != "function") {
init_common_load_more = function (id) {
fdxm.js_manager.on("fdx_loadmore_js", function () {
component_wrapper = document.getElementById(id);
const loader = new fdxLoadMore({
posts_counter:
component_wrapper.querySelector(".posts_counter"),
load_more_posts_url: get_load_more_url(this),
});
});
};
// do things wiht the loader
function get_load_more_url(instance) {
let post_list_obj = instance.getObject("posts_list"),
load_options = {
controller: post_list_obj.dataset["load_more_controller"],
//...other options
};
let url = "/api/posts/load_more_posts";
for (option in load_options) {
url += "/" + option + "/" + load_options[option];
}
return url;
}
}
I passed "this" as a parameter, because sometimes writing random words makes the code work. But not this time: when I console log "instance" I'm getting some kind of default callback object instead of the new fdxLoadMore object.
How can I reference the new fdxLoadMore, in the same way it is referenced on the first version of the code, but using a separate, named function?
I have an array of objects. Each object has a method that should update a boolean property in the same object called 'found'.
When I call the function, the property does not update. I am not sure why.
I thought that the 'found' property would be accessible but it isn't??
I have created a minimal version of the problem here:
https://codepen.io/sspboyd/pen/XWYKMrv?editors=0011
const gen_s = function () { // generate and return the object
let found = false;
const change_found = function () {
found = true;
};
const update = function () {
change_found();
};
return {
change_found,
found,
update
};
};
const s_arr = []; // initialize an array
s_arr.push(gen_s()); // add a new s object to the array
console.log(s_arr[0].found); // returns 'false'
s_arr.forEach((s) => {
s.update();
});
console.log(s_arr[0].found);
When your change_found function changes the value of found, it's changing the value pointed to by your let found variable, but the object returned by your gen_s function still points to the old value.
You can fix your code using the 'holder' pattern, like this:
const gen_s = function () { // generate and return the object
let foundHolder = {value: false};
const change_found = function () {
foundHolder.value = true;
};
const update = function () {
change_found();
};
return {
change_found,
foundHolder,
update
};
};
const s_arr = []; // initialize an array
s_arr.push(gen_s()); // add a new s object to the array
console.log(s_arr[0].foundHolder.value); // returns 'false'
s_arr.forEach((s) => {
s.update();
});
console.log(s_arr[0].foundHolder.value);
Or even better, use a class:
class S {
constructor() { this.found = false; }
change_found() { this.found = true; }
update() { this.change_found(); }
}
const s_arr = [];
s_arr.push(new S());
console.log(s_arr[0].found);
s_arr.forEach(s => s.update());
console.log(s_arr[0].found);
var Rules = Rules || (function () {
saverule = function () {
var level = document.getElementById("level-selection");
var metrics = document.getElementById("metric-selection");
var operator = document.getElementById("operator-selection");
var value = document.getElementById("value123");
var saveAction = $("#hidden-save").val();
$.post(saveAction, { level_id: level, product_id: metrics, opp: operator, value: value }, function () {
},
'json');
};
wireLinkActions = function () {
$("a.save-ok").on("click", function(event) {
saverule();
return false;
});
};
return {
Initialize: function () {
wireLinkActions();
}
}
})();
$(document).ready(Rules.Initialize);
illegal invocation error it wont even cal the the save rule function while debugging also
Make sure your return the values and not the DOM element itself.
For example change this:
var level = document.getElementById("level-selection");
into this:
var level = document.getElementById("level-selection").value;
or, simply use jQuery such as this:
var level = $("#level-selection").val();
In the following code, I want to be able to call bindClickEvents() like so:
App.Utils.Modal.bindClickEvents();
However, I don't understand the syntax necessary to do this.
Current code:
var App = new Object;
App.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Modal.bindClickEvents();
});
You can do it in one go:
var App = {
Modal : {
bindClickEvents : function () {/* ... */}
}
}
or if you want to break that up to separate steps:
var App = {};
App.Modal = {};
Modal.bindClickEvents = function () {/* ... */};
BTW, in reference to your original question title, this is not object chaining. This is object composition. Object chaining is being able to call methods in an object multiple times in a single statement.
Is this what you're trying to do?
var App = {};
App.Utils = {};
App.Utils.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Utils.Modal.bindClickEvents();
});
Prefer the object literal syntax to the Object constructor; some authors go so far as to call the latter an anti-pattern
Here's the simplest way to set up App.Utils.Modal.bindClickEvents();
var App = {
Utils: {
Modal: {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
}
}
};
Or you can piece it together one step at a time:
var App = {};
App.Utils = {};
App.Utils.Modal = {};
App.Utils.Modal.bindClickEvents = function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
};
I have a json object retrieved from server in my $(document).ready(...); that has an string that I would like to resolve to a function also defined within $(document).ready(...); so, for example:
$(document).ready(function{
$.getJSON(/*blah*/,function(data){/*more blah*/});
function doAdd(left,right) {
return left+right;
}
function doSub(left,right) {
return left-right;
}
});
with json string:
{"doAdd":{"left":10,"right":20}}
One way I thought about was creating an associative array of the function before loading the json:
var assocArray=...;
assocArray['doAdd'] = doAdd;
assocArray['doSub'] = doSub;
Using eval or window[](); are no good as the function may not be called for some time, basically I want to link/resolve but not execute yet.
Change your JSON to
{method: "doAdd", parameters : {"left":10,"right":20}}
Then do
var method = eval(json.method);
// This doesn't call it. Just gets the pointer
Or (haven't tried this)
var method = this[json.method]
How about something like this?
$(function(){
// Function to be called at later date
var ressolvedFunc = null;
// Ajax call
$.getJSON(/*blah*/,function(data){
// Generate one function from another
ressolvedFunc = (function(data) {
var innerFunc;
var left = data.left;
var right = data.right;
// Detect action
for (action in data) {
if (action == "doAdd")
innerFunc = function() {
return left + right;
};
else
innerFunc = function() {
return left - right;
};
}
return innerFunc;
})(data);
});
});
The anonymous function returns fresh function, with the new values stored within the enclosure. This should allow you to call the function at later date with the data previously retrieved from the GET request.
Rich
try this:
var doX = (function() {
var
data = [],
getDo = function(action) {
for(var d in data) {
if (data[d][action]) {
return data[d];
}
}
return null;
};
return {
set: function(sdata) {
data.push(sdata);
},
doAdd: function() {
var add = getDo("doAdd");
if (!add)
return 0;
return add.doAdd.left + add.doAdd.right;
},
doSub: function() {
var sub = getDo("doSub");
if (!sub)
return 0;
return sub.doAdd.left + sub.doAdd.right;
}
};
})();
$(document).ready(function{
$.getJSON(/*blah*/,function(data){ doX.set(data); });
});