I have a global object variable that I pass to a function, then I call another function and assign that return value to the variable which was passed to the original function. This return value does not propagate to the global object which was passed into the function for some reason.
var DEFAULT_OPTIONS = {...}
var optionsElements = new Object();
function initializeOptions(elements, optionsObject, defaultOptions) {
elements = getOptionsElements();
optionsObject = loadOptions(elements, defaultOptions);
document.getElementById("movie_categories").addEventListener("change",
function(event) {
onCategoryChange(elements, event);
});
document.getElementById("tv_categories").addEventListener("change",
function(event) {
onCategoryChange(elements, event);
});
}
initializeOptions(optionsElements, currentOptions, DEFAULT_OPTIONS);
The elements var inside initializeOptions gets set properly, why does it not update optionsElements? My understanding is that Objects are passed by reference, so it seems to me this should work.
function getOptionsElements() {
options = {
"all_movies": document.getElementById("all_movies"),
"movie_3d": document.getElementById("movie_3d"),
"movie_480p": document.getElementById("movie_480p"),
"movie_bd-r": document.getElementById("movie_bd-r"),
"movie_bd-rip": document.getElementById("movie_bd-rip"),
"movie_cam": document.getElementById("movie_cam"),
"movie_dvd-r": document.getElementById("movie_dvd-r"),
"movie_hd-bluray": document.getElementById("movie_hd-bluray"),
"movie_kids": document.getElementById("movie_kids"),
"movie_mp4": document.getElementById("movie_mp4"),
"movie_non-english": document.getElementById("movie_non-english"),
"movie_packs": document.getElementById("movie_packs"),
"movie_web-dl": document.getElementById("movie_web-dl"),
"movie_xvid": document.getElementById("movie_xvid"),
"all_tv": document.getElementById("all_tv"),
"tv_documentaries": document.getElementById("tv_documentaries"),
"tv_sports": document.getElementById("tv_sports"),
"tv_480p": document.getElementById("tv_480p"),
"tv_bd": document.getElementById("tv_bd"),
"tv_dvd-r": document.getElementById("tv_dvd-r"),
"tv_dvd-rip": document.getElementById("tv_dvd-rip"),
"tv_mp4": document.getElementById("tv_mp4"),
"tv_non-english": document.getElementById("tv_non-english"),
"tv_packs": document.getElementById("tv_packs"),
"tv_packs-non-english": document.getElementById("tv_packs-non-english"),
"tv_sd-x264": document.getElementById("tv_sd-x264"),
"tv_web-dl": document.getElementById("tv_web-dl"),
"tv_x264": document.getElementById("tv_x264"),
"tv_xvid": document.getElementById("tv_xvid"),
"sort_options": document.getElementById("sort_options")
}
return options;
}
so after I assign elements = getOptionsElements, elements no longer
points to optionsElements, but is now a reference to the object
created inside getOptionsElements?
Yes. Try passing elements to getOptionsElements , using for..in loop within getOptionsElements to set properties of elements : optionsElements , return elements from getOptionsElements
var optionsElements = new Object();
function initializeOptions(elements, optionsObject, defaultOptions) {
elements = getOptionsElements(elements);
return elements
}
function getOptionsElements(opts) {
options = {
"a":1,
"b":2,
"c":3
};
for (var prop in options) {
opts[prop] = options[prop];
}
return opts;
};
console.log(initializeOptions(optionsElements), optionsElements)
Related
I have a method loadSet which creates elements with datas from the localstorage, and this should be run on page load i am calling it via
ReminderSet.prototype.loadSet(); // works fine
My question is, is there any other way to call a method that don't need a reference to an object instance? like person1.loadSet(); or should i abandon this and make it as a regular function?
ReminderSet.prototype.loadSet = function() {
var keys = Object.keys(localStorage),
i = 0,
key,
array;
for (; key = keys[i]; i++) {
const setId = localStorage.getItem(key);
array = JSON.parse(setId); //parse and store key values
let array_index = 0;
//Re-create the reminders and set their properties//
$reminderSection.append($('<div/>').addClass('set').attr('id', key) //Set the ID
.append($('<div/>').addClass('set-title').append($('<h1>').attr('contenteditable', 'true').text(array[array_index].set_title)), //Index is always at 0//
$('<div/>').addClass('create-reminder-control').append($('<button>').addClass('add-new-reminder').text("+ add new"), $('<input>').addClass('create-reminder-value').attr({ type: "text", placeholder: "get something done" })), $('<div/>').addClass('reminder-lists'), $('<div/>').addClass('save-control').append($('<button>').addClass('save-reminder-button').text('Save'))))
//Get our key values //
for (; array_index < array.length; array_index++) {
/*Select the element id */
$("#" + key).children('.reminder-lists').append($('<div/>').addClass('a-reminder').attr('contenteditable', 'true').text(array[array_index].description).append($('<div/>').addClass('delete-reminder').text('x'))) //Get the reminders
} //end
}
};
If loadSet doesn't need or use an instance, it doesn't make any sense for it to be on ReminderSet.prototype. Either make it a standalone function:
function loadSet() {
// ...
}
// Call it like so: loadSet();
...or a property on ReminderSet itself:
ReminderSet.loadSet = function() {
// ...
};
// Call it like so: ReminderSet.loadSet();
Only put functions on the object that a constructor's prototype property refers to if they need to use this (the instance).
You can set the function directly as a property of the other ReminderSet:
ReminderSet.loadSet = function() {//etc.}
Then you can simply call: ReminderSet.loadSet()
I am trying to re-write a function that filters out a specific property of an object to a function that can be passed a property and filter it.
This is the initial function:
function filterCategory(xmlObject, id) {
let newData = [];
xmlObject
.Sports[0]
.Sport[0]
.Category
.map(function (category) {
if (category.$.CategoryID == id) {
newData.push(category);
}
});
xmlObject
.Sports[0]
.Sport[0]
.Category = newData;
return xmlObject;
}
This is my new function:
function filterProperty(xmlObject, property, idtype, id) {
let newData = [];
if(xmlObject.hasOwnProperty(property)) {
xmlObject.property.map(function(value) {
if(value.$.idtype == id) {
newData.push(value);
}
});
xmlObject.property = newData;
}
return xmlObject;
}
For the second function my linter returns Unused idtype. Will my function be able to access the argument, or will it fail because I am trying to call it from a map() function? If so, how can I avoid this?
If you want to use idtype as a dynamic object property, then you can't use it like my.object.idtype as that will look for the property on the object that is literally called "idtype", instead you can use bracket notation to access the property
value.$[idtype];
Further illustration:
var obj = { one: 1, two: 2, three: 'foobarbaz' };
function getThingFromObject(mything) {
return obj[mything];
}
console.log(getThingFromObject('one')); // 1
console.log(getThingFromObject('three')); // 'foobarbaz'
Can someone shed some light as to why this doesn't work the way I think it should (or what I'm overlooking).
function Pane(data) {
var state = {
show: function(data) {
var pane = document.querySelector('.pane[data-content='+data.target+']');
pane.classList.add('active');
},
hide: function(data) {
var pane = document.querySelector('.pane[data-content='+data.target+']');
var paneSibling = $(pane.parentNode.childNodes);
paneSibling.each(function(sibling) {
if(check.isElement(sibling)) {
var isActive = sibling.classList.contains('active');
if(sibling != pane && isActive) {
sibling.classList.remove('active');
};
};
});
}
}
return state;
}
So I can console log Pane(arg).show/hide and it'll log it as a function, so why is it when I call Pane(arg).show it doesn't do anything? The functions in the object work (outside of the constructor function in their own functions).
The function is returning the state object, so it will never return the constructed object, even when used with new. Since state contains those methods, you can just call the function and immediately invoke one of the methods on the returned object.
Now, if you're expecting show and hide to automatically have access to data via closure, it's not working because you're shadowing the variable by declaring the method parameters. You can do this instead:
function Pane(data) {
var state = {
show: function() {
var data = data || arguments[0];
var pane = document.querySelector('.pane[data-content='+data.target+']');
pane.classList.add('active');
},
hide: function() {
var data = data || arguments[0];
var pane = document.querySelector('.pane[data-content='+data.target+']');
var paneSibling = $(pane.parentNode.childNodes);
paneSibling.each(function(sibling) {
if(check.isElement(sibling)) {
var isActive = sibling.classList.contains('active');
if(sibling != pane && isActive) {
sibling.classList.remove('active');
};
};
});
}
}
return state;
}
Then you can use it like this:
Pane({}).show();
Or like this:
var p = Pane();
p.show();
Or force a new argument when needed:
p.show({foo:'bar'});
You are overriding the original argument in each function.
So what you are doing is to find elements with the attribute data-content='undefined'
This obviously doesn't work.
So to fix this you should just remove the data argument in the show/hide function.
Here is a plnkr showing the problem and fix.
In this example I'm trying to iterate over the properties of an object that's passed to a click handler, but I am getting unexpected results.
Here's the fiddle
So with a JS script like
$(document).ready(function ()
{
Label = function (name, toDate, fromDate)
{
this.name = name;
this.toDate = toDate;
this.fromDate = fromDate;
}
lbl = new Label('John', 'Today', 'Yesterday');
$('#btnSubmit').click(function ()
{
for (var i in lbl)
{
console.log(i);
}
});
$('#btnSubmit2').click(function (Label)
{
for (var i in Label)
{
console.log(i);
}
});
});
Why can't I pass an object in the function of a click event and iterate over its properties instead of using the forin loop like I did in the btnSubmit example?
The callback is always called with the event as argument. When you write click(function(Label){ you only give that event variable the name Label (thus shadowing your outside constructor).
But you can access the variables defined in the outer scope, so what you want is probably
var lbl = new Label('John', 'Today', 'Yesterday');
$('#btnSubmit').click(function(){
for (var i in lbl) {
console.log(i, lbl[i]); // for example "name", "John"
}
});
I need to bind a table with knockout, and I would like the table cell to get a different css class if the new value is higher or lower of the previous.
I have in mind different possibilities, such as storing the previous value in the bindingContext and have a function which returns the right class, but is it possible to add a custom binding handler which receives the previous value and the new value?
Although Jeff's and SÅ‚awomir's answers would work, I found an alternative that doesn't need any change to the view model nor relies on altering the DOM element object.
function subscribeToPreviousValue(observable, fn) {
observable.subscribe(fn, this, 'beforeChange');
}
ko.bindingHandlers['bindingWithPrevValue'] = {
init: function (element, valueAccessor) {
var observable = valueAccessor();
var current = observable();
console.log('initial value is', current);
subscribeToPreviousValue(observable, function (previous) {
console.log('value changed from', previous, 'to', current);
});
}
};
Naturally, that will only work if the bound property is an observable.
I looked into knockout source and I suppose that you can't access previous value inside update method of bindingHandler but you can store it inside element
ko.bindingHandlers['bindingWithPrevValue'] = {
update: function (element, valueAccessor) {
var prevValue = $(element).data('prevValue');
var currentValue = valueAccessor();
$(element).data('prevValue', currentValue());
// compare prevValue with currentValue and do what you want
}
};
What you could do is create an extender to extend the observables that you wish to track the previous values of. You could then inspect the previous value to do as you wish.
Just pass in the name of the property that will hold the previous value.
ko.extenders.previousValue = function (target, propertyName) {
var previousValue = ko.observable(null);
target[propertyName] = ko.computed(previousValue);
target.subscribe(function (oldValue) {
previousValue(oldValue);
}, target, 'beforeChange');
return target;
};
Then to use it:
function ViewModel() {
this.value = ko.observable('foo').extend({ previousValue: 'previousValue' });
}
var vm = new ViewModel();
console.log(vm.value()); // 'foo'
console.log(vm.value.previousValue()); // null
vm.value('bar');
console.log(vm.value()); // 'bar'
console.log(vm.value.previousValue()); // 'foo'
In your case, you could probably use something like this:
function TableCell(value) {
this.value = ko.observable(value).extend({ previousValue: 'previousValue' });
this.cssClass = ko.computed(function () {
// I'm assuming numbers
var current = Number(this.value()),
previous = Number(this.value.previousValue());
if (current < previous)
return 'lower';
else if (current > previous)
return 'higher';
}, this);
}