Objects members become null when I call a member function - javascript

I'm having trouble with how member variables of objects work in javascript.
I have this function which finds a specific table in HTML and it seems to be working just fine. But it for some reason the member variables of the object associated with the table becomes null...
I tried debugging and here is what I got.
let tables;
function find_table(s) {
for(const table of tables){
if(table.prefix === s){
return table;
}
}
return null;
}
function search_table(table){
const tmp = find_table(table);
console.log(tmp.prefix); // debug0
tmp.search();
}
Inside the object called "table".
search(){
const search_text = $("#" + this.prefix + "_search_text").val().toLowerCase();
let item_count = 0;
$("#" + this.prefix +"_table tr").each(function () {
if (!$(this).hasClass("search_ignore")) {
let count = 0;
let show = false;
console.log(this.prefix); // debug1
for(const type of this.types){ // error here. this.types is undefined
const temp = $(this).find("td").eq(count).text().toLowerCase();
if(type === 0){
// 文字列検索
show = temp.includes(search_text)
}else if(type === 2){
...(the rest is probably irrelevant)...
So, I call the search_table() function so I can search for a table in tables with a string. The function find_table() seems to be working fine. The debug0 line returns "admin_account_list" properly. But after that when search() of table is called, the variable this.types has for some reason, turned into null. And the debug1 line also returns null.
I expected it to have the value I have assigned when the constructor was called.
I would greatly appreciate it if someone helped me understand what I did wrong here.

Related

How can I correctly add an argument to a local function?

I'm trying to filter an array by a given ID in a function, but I don't know how to add the ID to the filter-method
function checkIdFilter(mediaObject, subCollectionId){
//mediaObject is allMedia[index??]
console.log("FILTERING ON " + subCollectionId);
return mediaObject.id == subCollectionId//check if mediaObject has certain ID here;
}
function searchMediaByCollection(subCollectionId) {
let filteredMedia = allMedia.filter(checkIdFilter);
// let filteredMedia = allMedia.filter(checkIdFilter(this.mo??, subCollectionId));
}
As you can see, I don't know how to correctly pass the Id to the checkIdFilter method, as it (I assume) iterates invisibly over the array.
A filter function takes only one parameter: the current object, so you have to wrap your function in another function, and use the closure to reach subCollectionId.
function checkIdFilter(mediaObject, subCollectionId){
//mediaObject is allMedia[index??]
console.log("FILTERING ON " + subCollectionId);
return mediaObject.id == subCollectionId//check if mediaObject has certain ID here;
}
function searchMediaByCollection(subCollectionId) {
let filteredMedia = allMedia.filter(media => checkIdFilter(media, subCollectionId);
}
or
function searchMediaByCollection(subCollectionId) {
let filteredMedia = allMedia.filter(function(media) {
return checkIdFilter(media, subCollectionId);
});
}
Don't hesitate to ask details if needed ;)

.map() unable to access Object's this.function

Thanks in advance for any responses:
I don't think this is a duplicate: I reviewed that article in the first comment, that is just a general breakdown of objects and using "this" within javascript.
My other this.function's perform just fine, so I at least have the basics of JS Obj's figured out.
This issue is related to using .map() with a this.function within a constructed object.
The following Google Appscript code uses .map() to update a string in a 2d array. [[string, int],[string, int]]
For some reason, when using .map() it is am unable to access the function "this.removeLeadingZero". If that same function is placed outside of the OBJ it can be called and everything works just fine. For some reason the system claims row[0] is an [object, Object] but when I typeof(row[0]) it returns "string" as it should.
Error: TypeError: Cannot find function removeLeadingZero in object [object Object]. (line 106, file "DEEP UPC MATCH")
Is there any issue using this.function's with .map() inside an object or am I using an incorrect syntax?
function test2DMapping(){
var tool = new WorkingMappingExample()
var boot = tool.arrayBuild();
Logger.log(boot)
}
function WorkingMappingExample(){
this.arr= [["01234", 100],["401234", 101],["012340", 13],["01234", 0422141],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3],["01234", 1],["01234", 2],["12340",3]];
//mapping appears faster that normal iterations
this.arrayBuild = function(){
var newArray1 =
this.arr.map( function( row ) {
**var mUPC = removeLeadingZero2(row[0])** //working
**var mUPC = this.removeLeadingZero(row[0])** // not working
var index = row[1]
Logger.log(mUPC + " " + index)
row = [mUPC, index]
return row
} )
return newArray1;
};
}; //end of OBJ
//THE NEXT 2 FUNCTIONS ARE WORKING OUTSIDE OF THE OBJECT
function removeLeadingZero2(upc){
try {
if (typeof(upc[0]) == "string"){
return upc.replace(/^0+/, '')
} else {
var stringer = upc.toString();
return stringer.replace(/^0+/, '')
}
} catch (err) {
Logger.log(err);
return upc;
}
}
function trimFirstTwoLastOne (upc) {
try {
return upc.substring(2, upc.length - 1); //takes off the first 2 #'s off and the last 1 #'s
} catch (err) {
Logger.log(err);
return upc;
}
}
Inside the function that you pass to map, this doesn't refer to what you think it does. The mapping function has its own this, which refers to window, normally:
var newArray1 = this.arr.map(function(row) {
// this === window
var mUPC = this.removeLeadingZero(row[0]);
var index = row[1];
Logger.log(mUPC + " " + index);
return [mUPC, index];
});
You have four options:
Array#map takes a thisArg which you can use to tell map what the this object in the function should be:
var newArray1 = this.arr.map(function(row) {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
}, this); // pass a thisArg
Manually bind the function:
var newArray1 = this.arr.map(function(row) {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
}.bind(this)); // bind the function to this
Store a reference to the outer this:
var self = this;
var newArray1 = this.arr.map(function(row) {
// self === (outer this)
var mUPC = self.removeLeadingZero(row[0]);
// ...
});
Use an arrow function:
var newArray1 = this.arr.map(row => {
// this === (outer this)
var mUPC = this.removeLeadingZero(row[0]);
// ...
});
Additionally, you could stop using this and new.
I have solved this issue and below is the answer in case anyone else runs into this:
this needs to be placed into a variable:
var _this = this;
and then you can call it within the object:
var mUPC = _this.removeLeadingZero(row[0])
Javascript scope strikes again!

Pass Arguments from One Function to Another Without Calling It

I'm trying to get either options or, ideally, dynamicTable passed from initializeTable to the applyTableFilters function and I'm having problems getting the expected values. I'm using List.js to make a table dynamic and I need to pass or recreate the dynamicTable object so I can go ahead and use it to filter the table.
Here is the function that creates the List.js object from the HTML table:
function initializeTable(options) { // initializes table to be dynamic using List.js functions
var dynamicTable = new List("table-content", options);
dynamicTable.on("updated", function (list) { // writes a message to the user if no results are found
if (list.matchingItems.length == 0) {
document.getElementById("no-results").style.display = "block";
}
else {
document.getElementById("no-results").style.display = "none";
}
});
console.log(dynamicTable);
console.log(options);
console.log(arguments.length);
applyTableFilters.bind();
}
I've tried different methods to pass the variables to the function below. I tried .call, applyTableFilters(args), and .apply, but the problem is that I do not want the function to execute from inside here, only when the click event from the button goes off (not shown in these functions).
This is the function I want to pass the object to and proceed to make the filter functions using it:
function applyTableFilters(dynamicTable) {
var form = document.getElementById("filter-form");
//console.log(options);
//var dynamicTable = new List("table-content", options);
console.log(dynamicTable);
var filters = form.querySelectorAll('input[type="checkbox"]:checked');
dynamicTable.filter(function (item) {
console.log(item);
console.log(item._values);
if (item.values().id == 2) {
return true;
}
else {
return false;
}
//var filterStrings = [];
//console.log(filters);
//for (var i = 0; i < filters.length; i++) {
// var filterVal = filters[i].value;
// var filterString = "(" + item.values().column == filterVal + ")"; // filterVal.contains(item.values().column) ||
// filterStrings.push(filterString);
// console.log(filterVal);
// console.log(filterString);
//}
//console.log(filterStrings);
//var filterString = filterStrings.join(" && ");
//console.log(filterString);
//return filterString;
});
}
I've used:
applyTableFilters.bind(this, dynamicTable/options);
applyTableFilters.bind(null, dynamicTable/options);
applyTableFilters.bind(dynamicTable/options);
Switching between the two since I don't need both passed if one ends up working, etc. I always get a mouse event passed in and that's not even the right type of object I'm looking for. How can I get the right object passed? Also all the values in the first function are not empty and are populated as expected so it's not the original variables being undefined or null. Thanks in advance.
From your initializeTable function return a function that wraps the applyTableFilters function with the arguments you want.
Then assign the returned function to a var to be executed later.
function initializeTable(options) {
var dynamicTable = new List("table-content", options);
// other stuff
return function () {
applyTableFilters(dynamicTable)
}
}
// other stuff
var applyTableFiltersPrep = initializeTable(options)
// later, when you want to execute...
applyTableFiltersPrep()
JSFiddle example

How to declare instance of a class within a instance of class?

I am making a simple hmtl/js game. I'd like to have all the data of the Game in DataofGame. It is like tennis, it is simpler than tennis: there is only set and match. changeinSet is called on click.
But I think i have a problem with private variable so it doesn't work.
Uncaught TypeError: Cannot read property 'WordsoftheGame' of undefined
//Added
document.getElementById('playboutton').addEventListener('click', newGame);
function newGame() {
var DataofGame = new newGameData();
}
// New game
function newGameData() {
this.pointTeam1 = 0;
this.pointTeam2 = 0;
this.WordsoftheGame = ShuffleListe();
this.ASet = new aSet();
}
//How the set is manage ********************
function aSet() {
var oneWord = DataofGame.ListeMot;
// display the word and delete it from the list
document.getElementById('jouer').innerHTML = oneWord[0];
DataofGame.WordsoftheGame.shift();
this.turn = true;
this.score = 0;
}
function changeinSet() {
DataofGame.ASet.score += 1;
//This is the other team's turn:
DataofGame.ASet.turn = !DataofGame.ASet.turn;
};
//shuffle liste
ListOfWords = ['Artiste', 'Appeler', 'Cheval', 'Choisir', 'Ciel', 'Croire', 'Dormir'];
function ShuffleListe() {
data = shuffle(ListOfWords);
return data;
}
function newGameData(){
this.pointTeam1=0;
this.pointTeam2=0;
this.WordsoftheGame= ShuffleListe();
this.ASet=new aSet();
}
//How the set is manage ********************
function aSet(){
var oneWord=DataofGame.ListeMot;
// display the word and delete it from the list
document.getElementById('jouer').innerHTML=oneWord[0];
DataofGame.WordsoftheGame.shift(); // << DataofGame not assigned yet
this.turn=true;
this.score=0;
}
Here when you're accessing DataofGame, it's not yet assigned because you're inside the constructor when calling aSet().
What you want to achieve is not completely clear, but if it's adding an ASet method to your object, you could write something like this:
function newGameData(){
this.pointTeam1=0;
this.pointTeam2=0;
this.WordsoftheGame= ShuffleListe();
this.ASet = function() {
// your code
};
}
NB your coding style for names is a bit messy, you should use uppercases consistently. The usage is to start constructor names with uppercases, the rest in lower cases.
You can let the function return an object with the data or just set the object.
function newGameData(){
return {
pointTeam1 : 0,
pointTeam2 : 0,
WordsoftheGame : ShuffleListe(),
ASet : new aSet()
}
}
But I would recommend to search for how to work with objects in javascript. Maybe this helps:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

How do I use an array instead of the variables in this repetitive function?

So, I have this little code in my js file:
window.onload = function Equal() {
var a = 'b1'
var b = 'box1'
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
var a = 'b2'
var b = 'box2'
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
The function itself is not important (it equals checkboxvalues set in the localstorage), but I execute it 2 times. First time with var a & b set to 'b1' & 'box1'. Then I run the script again (same script), but with var a & b set to 'b2' & 'box2'. Now, this code works, but my question is if there is a shorter way to write this? I can imagine some sort of array with a loop, but I could not get it to work for some reason. The 2 variables are pairs, and I know this might be a dumb question, but I can't find the answer anywhere.
You can use a second function which will accept the local storage key and the checkbox id like
window.onload = function Equal() {
setCheckboxState('box1', 'b1');
setCheckboxState('box2', 'b2');
}
function setCheckboxState(id, key) {
document.getElementById(id).checked = 1 == localStorage.getItem(key);
}
You might separate common logic into another function
window.onload = function Equal() {
function extractFromStorage(a, b) {
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
extractFromStorage('b1', 'box1');
extractFromStorage('b2', 'box2');
}
function doTheStuff(a, b) {
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
window.onload = function Equal() {
doTheStuff('b1', 'box1');
doTheStuff('b2', 'box2');
}
?
This is how I would do it.
There are several problems with your code.
You do not check that the element you are stetting an attribute to
exists. You do not check if the localStorage item you get is
defined.
You pollute the global name space with the function name Equal.
That function should not be named with a capital as it is not a Object generator.
There is no need to use setAttribute and removeAttribute, in
fact removeAttribute makes no sense in this case as you can not
remove the checked attribute from the element. BTW why use setAttribute here and not for window.onload?
The checked attribute is either true or false, it does not use the
string "checked"
Binding the load event via the onload attribute is not safe as you may
block 3rd party code, or worse 3rd party code may block you.
There is no error checking. DOM pages are dynamic environments, pages
have adverts and content from many places that can interfer with your
code. Always code with this in mind. Check for possible errors and deal with them in a friendly way for the end user. In this case I used an alert, not friendly for a normal user but for you the coder.
My solution.
// add an event listener rather than replace the event listener
window.addEventListener(
"load", // for the load event
function(){
// the update function that is called for each item;
var update = function(item){
// the right hand side equates to true if the localstorage
// is equal to "1". LocalStorage allways returns a string or
// undefined if the key is not defined.
item.element.checked = localStorage[item.storageName] === "1";
}
// safe element getter
var getElement = function(eId){
var e = document.getElementById(eId); // try and get the element
if(e === null){ // does it exist?
throw "Missing element:"+eId; // no then we can not continue
// the program stops here unless
// you catch the error and deal with
// it gracefully.
}
return e; //ok return the element.
}
// Item creator. This creates a new item.
// sName is the local storage name
// eId id the element ID
var item = function(sName, eId){
return {
storageName: sName, // set the loaclStorage name
element:getElement(eId); // get the element and check its safe
};
}
// make it all safe
try{
// create an array of items.
var items = [
item("b1","box1"),
item("b2","box2")
];
// for each item update the element status
items.forEach(update);
}catch(e){
alert("Could not update page?");
}
}
);

Categories