I have a PHP page that loads two JS files at the end. In the first file I have this...
// global variables
var refineSearchStorage = {};
// function calls
window.addEventListener("load", function() {
refineSearchStorage.get();
});
refineSearchStorage = {
data : null, // empty storage
get : function() {
refineSearchStorage.data = localStorage.getItem("refineSearchStorage");
if(refineSearchStorage.data === null) {
refineSearchStorage.data = { refineKeywords: '' };
refineSearchStorage.save();
}
else {
refineSearchStorage.data = JSON.parse(refineSearchStorage.data);
}
},
add : function(x) {
refineSearchStorage.data.refineKeywords = x;
refineSearchStorage.save();
},
save : function() {
localStorage.setItem("refineSearchStorage", JSON.stringify(refineSearchStorage.data));
}
};
Inline javascript calls Function 1 from the middle of the page. It is created by PHP after a search result...
<script>
window.addEventListener('load', function () {
searchActions('{$keywords_human}');
});
</script>
Function 1 appears in the 2nd JS page and the result is Uncaught TypeError: Cannot set property 'refineKeywords' of null... instead of adding to the localStorage.
function searchActions(x) {
refineSearchStorage.add(x);
}
Function 2 below is called with the click of a button and adds to a localStorage variable without issue. It is also located on the 2nd JS page...
function keywordAdd(y) {
var existingParams = refineSearchStorage.data.refineKeywords;
var param = y.toLowerCase();
var newParams;
newParams = (existingParams + ' ' + param).trim();
refineSearchStorage.add(newParams);
}
Function 1 used to work, but I did something to break it when I split the functions on to different pages. What did I do?
It's because
window.addEventListener('load', function () {
searchActions('{$keywords_human}');
});
gets called before
window.addEventListener("load", function() {
refineSearchStorage.get();
});
That error means that your data attribute is null
(screenshot) See what I mean by that
convert add to
add : function(x) {
refineSearchStorage.data = refineSearchStorage.data || {}; // this might help
refineSearchStorage.data.refineKeywords = x;
refineSearchStorage.save();
},
Related
I Made a factory that keeps the information in my scopes in a series of 6 pages. Now when the user completes the 6th page and pushes the object I want the factory to reset to empty arrays again.
I already tried a lot with the timeout and apply elements, also tried a lot of combinations to set the array to empty (null, "", {}). but it still loads the old information when I load the page again(page 1/6).
The submit function (That also needs to reset the scopes) is
$scope.send = function(){
if(ArrayInfo.Checkmark == true){
firebase.database().ref('lorem/' + ArrayInfo.Ordernumber).set({
info: ArrayInfo,
Dates: Dates,
gasmeter: gasmeter,
gasmeter1: gasmeter1
}).then(function(){
firebase.database().ref('lorem2/' + ArrayInfo.currentYear).set({
last_number: ArrayInfo.Ordervalue
});
}).then(function(){
//ArrayInfo = {};
setTimeout(function(){
ArrayInfo = "";
$scope.info = "";
$scope.$apply();
$scope.$digest();
}, 50);
});
//close newrental
setTimeout(function(){
if (window.confirm('Information saved! You are ready to leave this screen? no changes possible after this point'))
{
//disable back button in home
$ionicHistory.nextViewOptions({
disableBack: true
});
//go home
$state.go("app.auth");
}
//error close newrental
else
{
alert("Take your time");
}
}, 50);
}
//error send array
else {
alert("Please accept the terms and conditions.");
}
}
My factory looks like this
mainapp.factory("infoFactory", function(){
ArrayInfo = {};
placeholders = {
"licenseone" : "img/placeholder.png",
"licensetwo" : "img/placeholder.png",
"licensethree" : "img/placeholder.png",
"licensefour" : "img/placeholder.png",
"imageone" : "img/front.png",
"imagetwo" : "img/sideleft.png",
"imagethree" : "img/back.png",
"imagefour" : "img/sideright.png",
"imagefive" : "img/roof.png",
"imagesix" : "img/placeholder.png",
"imageseven" : "img/placeholder.png",
"imageeight" : "img/placeholder.png"
};
gasmeter = {
"url" : "img/gas/gas1.png",
"gasvalue" : "1"
}
gasmeter1 = {
"url" : "img/gas/gas1.png",
"gasvalue" : "1"
}
ArrayInfo.returned = false;
RawDate = {};
Dates = {};
console.log(ArrayInfo);
return ArrayInfo;
return gasmeter;
return gasmeter1;
return placeholders;
return RawDate;
return Dates;
})
and I load the information in my controller like this
$scope.info = infoFactory;
$scope.Dates = Dates;
$scope.RawDate = RawDate;
$scope.gasmeter = gasmeter;
$scope.gasmeter1 = gasmeter1;
The angular version I am using is "3.6.6"
First of all, when you put return in your code, there's no use to include additional code after that, because it will never run. You need to return an Object instead.
mainapp.factory("infoFactory", function(){
ArrayInfo = {};
placeholders = {
"licenseone" : "img/placeholder.png",
// Rest of the images
};
gasmeter = {
"url" : "img/gas/gas1.png",
"gasvalue" : "1"
}
gasmeter1 = {
"url" : "img/gas/gas1.png",
"gasvalue" : "1"
}
ArrayInfo.returned = false;
RawDate = {};
Dates = {};
console.log(ArrayInfo);
return {
ArrayInfo: ArrayInfo,
gasmeter: gasmeter,
gasmeter1: gasmeter1,
placeholders: placeholders,
RawDate: RawDate,
Dates: Dates
};
})
Now you can inject infoFactory to the controller and use it like this: infoFactory.RawDate.
Now, if you want to reset the factory, you can add a function that reset all the data:
mainapp.factory("infoFactory", function(){
// Save a reference to the current pointer of the factory, so you won't loose it inside other scopes
var self = this;
self.params = {
ArrayInfo: {},
placeholders: {},
gasmeter: {},
gasmeter1: {},
ArrayInfo: false,
RawDate: {},
Dates: {}
};
self.reset = function() {
self.params.ArrayInfo = {};
self.params.placeholders.licenseone = "img/placeholder.png";
self.params.gasmeter.url = "img/gas/gas1.png";
self.params.gasmeter.gasvalue = "1";
self.params.gasmeter1.url = "img/gas/gas1.png";
self.params.gasmeter1.gasvalue = "1";
self.params.ArrayInfo.returned = false;
self.params.RawDate = {};
self.params.Dates = {};
}
self.reset(); // Call this function by default in order to initially set the factory properties
return {
reset: self.reset, // You can export the reset function and use it outside the factory too
ArrayInfo: self.params.ArrayInfo,
gasmeter: self.params.gasmeter,
gasmeter1: self.params.gasmeter1,
placeholders: self.params.placeholders,
RawDate: self.params.RawDate,
Dates: self.params.Dates
};
})
Now when you have a reset function, you can use it like this outside the factory: infoFactory.reset() whenever you want to reset the data to the initial state. I created inside the factory a base object (this.params = { .. }) and saved inside it all the details properties, inside the reset function I have updated those properties without breaking the original references (Working example).
The above is just an example, but you can (or perhaps should) encapsulate the params of the factory, and only allow the user to control and change the values via helper functions. Example of how to do it:
mainapp.factory("infoFactory", function(){
var self = this;
self.params = {
returned: false,
};
return {
setReturned: function(val) { self.params.returned = val === true; },
returned: function() { return self.params.returned; }
}
});
The above example will hide the actual params.returned from the user outside the factory, and only allow it to set the returned flag via helper function, i.e infoFactory.setReturned( true ); or infoFactory.setReturned( false );, and inside that setReturned function you can implement complex logic to validate the value sent to the function. Note that infoFactory.setReturned( 'invalid value!!!' ); will set the returned flag to false since i'm validating the value using the strict === operator - val === true.
Then, to get the value from the factory you call the infoFactory.returned() function - By using a function you're blocking outside access to the properties of the factory.
As a side note - Don't use setTimeout(function(){ ... }); Use $timeout and $interval and then you won't need $scope.$apply(); + $scope.$digest(); in order to manually run a digest cycle because it is being handeled nativaly by Angularjs for you
I'm using the below CasperJS script to recursively parse the (multi-page) search results provided by google for the query site:https://www.launchgood.com/project/.
var links = [];
var casper = require('casper').create();
function getLinks() {
var currentLinks = document.querySelectorAll('h3.r a');
return Array.prototype.map.call(currentLinks, function(e) {
rawHref = e.getAttribute('href');
urlPattern = /.*(https?[:/]+[^&]+).*/g;
cleanHref = urlPattern.exec(rawHref);
return cleanHref[1];
});
Array.prototype.push.apply(links, currentLinks);
this.echo(' - ' + currentLinks.join('\n - '));
}
function parseAndContinue() {
links = this.evaluate(getLinks);
// now click 'Next'
if(this.exists('a.fl')) {
this.thenClick('a.fl');
this.then(parseAndContinue);
} else {
this.exit();
}
}
casper.start('http://google.com/ncr', function() {
// search from google form
this.fill('form[action="/search"]',
{ q: 'site:https://www.launchgood.com/project/' }, true);
});
casper.then(parseAndContinue);
casper.run();
This seems to continually search the 2nd page, over and over again in a never-ending loop -- instead of advancing to the next page.
What am I doing wrong?
Your looks fine apart from not printing anything. getLinks is a function that is evaluated in the page context. this refers to the global object, which is window, inside of the page context. You have no access to casper inside of the page context, because it is sandboxed and only primitive objects can be passed in or out. It has no access to variables defined outside of it (no access to links).
function getLinks() {
var currentLinks = document.querySelectorAll('h3.r a');
return Array.prototype.map.call(currentLinks, function(e) {
var rawHref = e.getAttribute('href');
var urlPattern = /.*(https?[:/]+[^&]+).*/g;
var cleanHref = urlPattern.exec(rawHref);
return cleanHref[1];
});
}
function parseAndContinue() {
var links = this.evaluate(getLinks);
console.log(JSON.stringify(links, undefined, 4));
// now click 'Next'
if(this.exists('a.fl')) {
this.thenClick('a.fl');
this.then(parseAndContinue);
} else {
this.exit();
}
}
Additionally, no code after the return statement will be executed.
Please be more careful and don't create global variables left and right.
A couple times now I have wanted to check element sizes as the page loads. I've been doing that using $(document).ready();, but find that often the properties are null. The same is true if I use $(window).load();.
To get around this I have been using a bit of a hack, where I recursively recall the function if the element is not set.
Question: Is there a better approach in terms of professionalism?
var makeMusic = {
init: function() {
if ($('#bloc-1').height() == null) {
setTimeout(function() {
makeMusic.init() ########## THIS IS THE HACK ##########
}, 10)
} else {
makeMusic.height = $('#bloc-1').height();
makeMusic.width = $('#bloc-1').width();
}
makeMusic.watchExperience();
},
watchExperience: function() {
//Some stuff
}
}
var Main = {
run: function() {
makeMusic.init();
}
}
$(document).ready(Main.run());
You do not need a hack at all. The issue here is that Main.run function is invoked before document.ready() is fired. You should:
$(document).ready(Main.run);
Instead of
$(document).ready(Main.run());
When you add () to the function name interpeter invokes it as soon as the line is reached.
When passing a callback, you should only pass a reference to the function.
In terms of proffessionalism i think its better to put your code in a namespace like this:
var app = window.app || {};
app.set = {};
app.set.makeMusic = (function(){
// private members
this.height = "";
this.width = "";
var init = function() {
height = $('#bloc-1').height();
width = $('#bloc-1').width();
alert(height + " " + width);
};
//public interface
return {
init: init
};
})(); // self invoked
$(function(){
app.set.makeMusic.init();
});
fiddle
I cannot find an proper example for the love of my life on how to do this or even if this is possible. Based on my pieced together understanding from fragments of exmaples, I have come up with the following structure
var t = function()
{
this.nestedOne = function()
{
this.nest = function()
{
alert("here");
}
}
}
t.nestedOne.nest();
However this is not working (obviously). I would greatly appreciate if someone could point me in the right direction!
That is simply done with:
var t = {
nestedOne: {
nest: function() {
alert('here');
}
}
};
Your code otherwise doesn't make sense. this inside function doesn't refer to the function itself, it refers to the object context that the function is invoked in. And you are not even invoking the functions in your code.
If I say obj.func() then this inside func will be obj for that call. So assigning this.asd = true will assign true to that object's "asd" property.
If you wanted to do a nested class, it looks very different:
ClassA = (function() {
function ClassA() {
}
ClassA.prototype.method1 = function() {
};
function ClassB() {
}
ClassB.prototype.method1 = function() {
};
return ClassA;
}())
only ClassA can now make instances of ClassB. This should achieve same goals as nested classes in java.
See http://jsfiddle.net/CstUH/
function t(){
function f(){
this.nest = function()
{
alert("here");
}
}
this.nestedOne = new f();
}
var myt=new t();
myt.nestedOne.nest()
Edit 1:
You can also use
new t().nestedOne.nest()
instead of
var myt=new t();
myt.nestedOne.nest()
(http://jsfiddle.net/CstUH/1/)
Edit 2:
Or even more condensed:
function t(){
this.nestedOne = new function(){
this.nest = function(){
alert("here");
}
}
}
new t().nestedOne.nest()
http://jsfiddle.net/CstUH/2/
In JS functions are prime class objects, and you can access them directly in the code [i.e. without using reflection or so].
The code you put inside t body would be performed when actually executing t:
t();
You wrote t.nestedOne,nest(), but t has no nestedOne property - you should do like this:
var t = {
nestedOne : {
nest : function()
{
alert("here");
}
}
};
t.nestedOne.nest();
I advice you to have a trip on John Resig's Learning Advanced JavaScript tutorial, it was very enlightening for me.
A simple callback handler I wrote today as an example of how I do deep nesting. I apologize if it's not the bees knees when it comes to code style, it made the concept a little clearer for me.
function test () {
this.that = this;
this.root = this;
this.jCallback = new Array(new Array()); // 2d
this.jCallbackCount = -1;
this.str = "hello";
// Callback handler...
this.command = {
that : this, // let's keep a reference to who's above us on the food chain
root : this.root, // takes us back to the main object
// add : function() { var that = this; console.log(that.that.str); },
add : function(targetFnc, newFunc) {
var that = this;
var home = that.that; // pretty much root but left in as an example of chain traversal.
var root = this.root; // useful for climbing back up the function chain
// console.log(that.that.str);
home.jCallbackCount++;
// target, addon, active
home.jCallback[home.jCallback.length] = { 'targetFunc' : targetFnc, 'newFunc' : newFunc, 'active' : true, 'id': home.jCallbackCount};
console.log('cbacklength: ' + home.jCallback.length);
console.log('added callback targetFunction:[' + targetFnc + ']');
return home.jCallbackCount; // if we want to delete this later...
},
run : function(targetFnc) {
var that = this;
var home = that.that;
console.log('running callback check for: ' + targetFnc + ' There is : ' + (home.jCallbackCount + 1) + 'in queue.');
console.log('length of callbacks is ' + home.jCallback.length);
for(i=0;i < home.jCallback.length - 1;i++)
{
console.log('checking array for a matching callback [' + targetFnc + ']...');
console.log('current item: ' + home.jCallback[i]['targetFunc'] );
if( home.jCallback[i]['targetFunc'] == targetFnc )
{
// matched!
home.jCallback[i]['newFunc']();
}
// console.log(that.that.jCallback[i].targetFunction);
}
}
};
}
test.prototype = {
say : function () {
var that = this;
console.log('inside');
// that.command('doSay');
that.command.run('doSay');
console.log(that.str);
}
} // end proto
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
// BEGIN TESTING **************************************************************************
var testing = new test();
testing.command.add('doSay', function () { console.log('213123123'); } );
testing.command.add('doSay', function () { console.log('12sad31'); } );
testing.command.add('doSay', function () { console.log('asdascccc'); } );
testing.say();
live:
http://jsfiddle.net/Ps5Uf/
note: to view console output, just open inspector in chrome and click on the "console" tab.
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); });
});