Inside my attemptSearch function, I use jQuery $.post to get some JSON results. I'm getting the error
Undefined is not an object
on the call
this.getSearchResults.bind(this)
I have the same set up in another web app and I don't get this error. What am I doing wrong?
var app = {
init: function() {
this.cacheDom();
this.bindEvents();
},
cacheDom: function() {
this.$search = $('#search');
},
bindEvents: function() {
this.$search.keyup(this.attemptSearch)
},
searchResults : [],
getSearchResults : function(val){
var currentSearchResult = val.query.search;
for (var i = 0; i < currentSearchResult.length; i++) {
var result = {
title: currentSearchResult[i].title,
}
console.log(this.searchResults);
this.searchResults.push(result);
}
},
attemptSearch: function(event) {
var wiki = "https://crossorigin.me/https://en.wikipedia.org/w/api.php?action=query&list=search&format=json&srsearch=";
if (event.keyCode == 13) {
$.post((wiki + $('#search').val()), this.getSearchResults.bind(this))
}
},
};
app.init();
You made sure to bind getSearchResults, but you didn't bind attemptSearch. That's almost surely the issue:
this.$search.keyup(this.attemptSearch.bind(this))
Related
This is an evolution of a question I asked more than a year ago: How to create methods with a loop in jquery/javascript
I've a code that is shared with other co-workers so it's better if it changes not much. It goes like this:
var scriptList = {
components : [
'all'
],
modules : [
'one',
'two',
'three'
]
}
function core() {
var scope = this;
var promises = [];
jQuery.each(scriptList, function(key, value) {
jQuery.each(value, function (index, name) {
var hookValue = 'hook_'+name,
stringValue = 'string_'+name,
argsValue = 'args_'+name;
scope[name] = function(callback){
window[hookValue] = jQuery('.js-'+name),
window[stringValue] = 'js-'+name;
window[argsValue] = arguments;
loadAndUse(window[hookValue],key+'/'+name,callback);
}
if(key === 'modules'){
scope[name]();
}
});
});
jQuery.when.apply(jQuery, promises).then(function() {
window.executeReady = true;
});
}
ui = new core();
ui.exec = methodLoader;
ui.exec();
This code works fine, because I can use the various method I added with ui.one - ui.two and so on and is also logged in the console if I do console.log(ui).
Before this code gets fired tho, I have now another block of code inside the HTML page, which create a method (always of the ui object) called exec:
window.executeReady = false;
var ui = {},
scriptToBeLoaded = [];
var methodLoader = function(){
var scope = this;
this.exec = function(module, callback){
scriptToBeLoaded.push({
'module' : module,
'callback' : callback
});
if(module === undefined){
console.warn('This module does not exists. Please check the scriptList.');
} else {
function waitForList($context, $variable, $callback) {
if ($context[$variable]) {
$callback();
} else {
Object.defineProperty($context, $variable, {
configurable: true,
enumerable: true,
writeable: true,
get: function() {
return this['_' + $variable];
},
set: function(val) {
this['_' + $variable] = val;
$callback();
}
});
}
}
waitForList(window, 'executeReady', function(){
for (var i = 0; i < scriptToBeLoaded.length; i++) {
ui[scriptToBeLoaded[i].module](scriptToBeLoaded[i].callback);
}
scriptToBeLoaded = [];
});
}
};
};
ui = new methodLoader();
Because of this block of code, when I console.log(ui); I see only the exec method and all of the other methods are gone. Although, the method I create in the core() function are executed correctly, but not present in the ui object.
I would like to edit the code in the HTML Page to have the ui object with exec (which is create on the html side) and the other method (that are created in the js file) all inside the ui object.
How can I achieve that?
You can add new methods to existing object like this. Or you can use jQuery.extend() to merge two object.
var ui = ui || {},
scriptToBeLoaded = [];
ui.exec = function(module, callback){
scriptToBeLoaded.push({
'module' : module,
'callback' : callback
});
if(module === undefined){
console.warn('This module does not exists. Please check the scriptList.');
} else {
function waitForList($context, $variable, $callback) {
if ($context[$variable]) {
$callback();
} else {
Object.defineProperty($context, $variable, {
configurable: true,
enumerable: true,
writeable: true,
get: function() {
return this['_' + $variable];
},
set: function(val) {
this['_' + $variable] = val;
$callback();
}
});
}
}
waitForList(window, 'executeReady', function(){
for (var i = 0; i < scriptToBeLoaded.length; i++) {
ui[scriptToBeLoaded[i].module](scriptToBeLoaded[i].callback);
}
scriptToBeLoaded = [];
});
}
};
I've decided to go back and relearn vanilla js since I've always relied on jquery.
I wanted to make some simple food order form.
My issue is that on my submit method, the "this" will refer to the form being submitted, rather than my placeOrder object.
When the order is submitted, I wanted to push it to my orders array which is inside cacheDom. Obviously, it can't use a push method on a form object.
can someone offer advice on how to go about this?
(function () {
var placeOrder = {
init: function() {
this.cacheDom();
this.bindEvents();
},
cacheDom : function() {
this.orderForm = document.getElementById('place-order-form');
this.elements = this.orderForm.elements;
this.orders = [];
},
bindEvents: function() {
if(this.orderForm.addEventListener){
this.orderForm.addEventListener("submit", this.submitOrder, false);
} else if (this.orderForm.attachEvent){
this.orderForm.attachEvent('onsubmit', this.submitOrder);
}
},
submitOrder: function(e) {
// this refers to the form being submitted
e.preventDefault();
var elements = this.elements;
var order = {};
for (var i = 0; i < elements.length; i++) {
if (elements[i].tagName !== 'BUTTON') {
if (elements[i].name === 'name') {
order.name = elements[i].value;
} else if (elements[i].name === 'food') {
order.food = elements[i].value;
}
}
}
//this.orders.push(order);
console.log(this);
}
};
placeOrder.init();
}());
Following is my javaScript code.
var myObjfn = {
before : function(){
console.log("before");
},
loadA: function(){
console.log("loadA");
},
loadB: function(){
console.log("loadB");
},
loadC: function(){
console.log("loadC");
}
}
Whenever I call myObjfn.loadA(), it should call myObjfn.before() method before executing loadA method. Same for loadB() & loadC(). I don't want to explicitly call before() method in all loadA,loadB and loadC methods. Is there any option to achive this in javascript ?
You could do something like this. Which creates a wrapper function for each function in the object except the before function.
var myObjfn = { ... };
Object.keys(myObjfn).forEach(key => {
if (key === "before") return;
var oldFunc = myObjfn[key];
myObjfn[key] = function() {
myObjfn.before();
return oldFunc.apply(this, arguments);
};
});
myObjfn.loadA();
// "before"
// "loadA"
I have this code which is working with the localStorage html5 calls. However it has to be rewritten for a Chrome Desktop app and I can't figure out how to port it over.
window.fakeStorage = {
_data: {},
setItem: function (id, val) {
return this._data[id] = String(val);
},
getItem: function (id) {
return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
},
removeItem: function (id) {
return delete this._data[id];
},
clear: function () {
return this._data = {};
}
};
function LocalScoreManager() {
this.key = "bestScore";
var supported = this.localStorageSupported();
this.storage = supported ? window.localStorage : window.fakeStorage;
}
LocalScoreManager.prototype.localStorageSupported = function () {
var testKey = "test";
var storage = window.localStorage;
try {
storage.setItem(testKey, "1");
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
LocalScoreManager.prototype.get = function () {
return this.storage.getItem(this.key) || 0;
};
LocalScoreManager.prototype.set = function (score) {
this.storage.setItem(this.key, score);
};
The error I get says "window.localStorage is not available in packaged apps. Use chrome.storage.local instead."
My attempt to rewrite it was this so far.. but it is breaking somewhere along the way.
$(document).ready(function() {
$("body").bind('keyup', function() {
var number = $(".best-container").val();
if(number == 'undefined'){
var number = "0";
}
chrome.storage.local.set({'bestScore': number});
});
chrome.storage.local.get('bestScore', function (result) {
hello= result.bestScore || 0;
$(".best-container").val(hello);
});
});
Porting localStorage to chrome.storage has one important pitfall: chrome.storage methods are asynchronous whereas localStorage access is synchronous.
That means: If you try to get a value from chrome.storage before the callback of the set method has been called, the value will still be undefined
Wrong way:
chrome.storage.local.set({'key': value});
...
chrome.storage.local.get('key', function(items) {
if(items.key) // won't be able to find the key
alert(items.key);
});
Correct way:
chrome.storage.local.set({'key': value}, function() {
...
chrome.storage.local.get('key', function(items) {
if(items.key)
alert(items.key); // will be "value"
});
});
or rather:
chrome.storage.local.set({'key': value}, function() {
doFurtherStuff();
});
function doFurtherStuff() {
...
chrome.storage.local.get('key', function(items) {
if(items.key)
alert(items.key); // will be "value"
});
}
I'm trying to bind the AJAX callback function to a certain scope, what am I doing wrong?
here is my code:
var MainApp = {
files:{
"A":{
url:"files/a.json",
content:""
},
"B":{
url:"files/b.json",
content:""
}
},
init:function () {
this.loadFiles();
},
loadFiles:function () {
for (var i in this.files) {
var f = function (data) {
console.log("callback",this);
};
console.log("binding",this);
f.bind(this);
$.get(this.files[i].url, f);
}
}
};
$(function () {
MainApp.init();
});
f.bind(this);
Function#bind doesn't alter the original function, it returns a new function bound to the parameter. You probably meant:
f= f.bind(this);
Try using call:
that = this;
$.get(this.files[i].url, function() {
f.call(that)
});