Angular Factory Object Persisting Over Storing in Local Storage - javascript

I've got a problem that i need help with in Angular 1.4.0.
I have a factory in angular which has a complex structure and method within so i am able to restore the factory back to its original state by calling the method.
The factory looks like this.
angular.module('myApp').factory('itemFcty', function(){
var currentValues = {};
var default values {
a = 1,
b = 2,
resetData : function(){
currentValues = angular.extend(currentValues, defaultValues);
return currentValues
};
};
defaultValues.resetData();
return currentValues;
});
In order to add values to 'a' i call itemFcty.a = 2;
So this method works well when i want to overwrite all the values as and when required.
However i have been asked could i persist the data over a refresh. So i stringify the object into JSON. Like this:
localStorage.setItem('itemFcty', JSON.parse(itemFcty);
However i have hit a snag. The only data to be stored in the local storage is the
{a = 1,b = 2,}
I can add the method back in by doing this:-
itemFcty.resetData = function(){return currentValues = angular.extend(currentValues, defaultValues);}
This is the issue that now the factory does function the same way as before as i am not able to call the function as the call and return outside the default values object is not there any more i can cannot for the life of me work out how to add it back into as everything goes directly into the object as a whole.
Your help would be greatly appreciated!
/*************************EDIT *****************************/
Ok, so i think that i havent explained the point very well.
My factory looks exactly like the above. The user hits refresh. The factory is stored in local storage. I get it back from local storage. But heres the issue.
It looks like this before local storage
angular.module('myApp').factory('itemFcty', function(){
var currentValues = {};
var defaultValues = {
a : 1,
b : 2,
resetData : function(){
angular.extend(currentValues, defaultValues);
// you don't have to return the values
} // <------you can't use ; in the object properties
};
defaultValues.resetData();
return currentValues;
});
Now when i get the data out f local storage and into the factory the factory then looks like this.
angular.module('myApp').factory('itemFcty', function(){
a : 1,
b : 2,
});
I can add the reset data function back in, however as the factory does not contain current or default values, the reset data function will not work.
So basically i am asking how to make my factory, look the same as it does originally after i have reloaded data from the local storage.

Did you load the variable back from localStorage? Also, there is a typo, I'd say var default values. In general stringifying does not account for methods, because that wouldn't make sense for other languages importing them.

Problems:
var default values {, space separated variable and missing assignment operator =.
In the object you can assign values with = but :, so it will produce error { a = 1, b = 2 }.
what you can do is:
angular.module('myApp').factory('itemFcty', function(){
var currentValues = {};
var defaultValues = { // <-------should have to be declared like this
a : 1, // = should be replaced with :
b : 2, // and here too.
resetData : function(){
angular.extend(currentValues, defaultValues);
// you don't have to return the values
} // <------you can't use ; in the object properties
};
defaultValues.resetData();
return currentValues;
});

Related

Accessing Svelte component properties in a callback?

Imagine that you have a lot of properties in a component:
let a = 'foo';
let b = 'bar';
// ...
let z = 'baz';
You then want to do something like update all of them from an external callback, like in another library (i.e. something that isn't and can't be a Svelte component itself).
A simple use case is just an AJAX method to load in a bunch of data (assume this ajax function works and you can pass it a callback):
onMount(async function() {
ajax('/data', function(data) {
a = data.a;
b = data.b;
// ...
z = data.z;
});
});
This works, but it's incredibly boilerplaty. What I'd really like is a way to loop through all the properties so they can be assigned to programmatically, especially without prior knowledge on the outside library/callback's part.
Is there no way to get access to a Svelte component and its properties so you can loop through them and assign them from an outside function?
Vue has a simple solution to this, because you can pass the component around, and still check and assign to its properties:
var vm = this;
ajax('/data', function(data) {
for (var key in data) {
if (vm.hasOwnProperty(key)) {
vm[key] = data[key];
}
});
});
I have seen some solutions to this, but they're all outdated - none of them work with Svelte 3.
Apologies if this has been asked before. I've spent days trying to figure this out to avoid all that extra boilerplate and the closest I could find is Access Component Object in External Callback? which does not have an answer right now.
If possible, you could put the ajax call in the parent component and have the data returned from it stored in a temporary object, that you then pass on to the component using the spread operator.
<Component { ...dataObject }></Component>
let dataObject = {};
onMount(async function() {
ajax('/data', function(data) {
dataObject = data;
});
});
You can reduce the boilerplate by using destructuring:
onMount(async function() {
ajax('/data', data => {
({ a, b, ..., z } = data);
});
});
But if you have a very large number of variables, you might be better off just putting them in an object in the first place:
let stuff;
onMount(async function() {
ajax('/data', data => {
stuff = data;
});
});

Object not updating after localstorage get

In the following I try to test whether an object has been stored in localstorage, and if not to fill it with initial variables.
var TimerData = $localstorage.getObject("TimerData", "{}");
if(!TimerData.hasOwnProperty("timerState")) {
TimerData["timerState"] = "run";
TimerData["timeOutMode"] = false;
TimerData["timeOutStartDate"] = null;
console.log("test line", TimerData)
};
However, running the console at line "test line" returns {} despite that I filled TimerData with variables lines before.
$localstorage.getObject looks as follows:
getObject: function(key, fallBack) {
return JSON.parse($window.localStorage[key] || fallBack);
},
My guess is that the operation is dealing with async problems (taking data from localstorage takes longer).
How can this be overcome?
Jakee1 has the right idea but but you asked about angular...
Instead of
var TimerData = $localstorage.getObject("TimerData", "{}");
I would create local storage first, then assign a var to it.
$localStorage.$default({TimerData: {}});
var TimerData = $localStorage.TimerData;
This will only setup localStorage to {} if it doesn't exist ety
It looks like you are setting the value of "TimerData" to an empty object.
I think you can simplify this using standard js notation (you definitely dont need a special $localstorage adapter because you are using ionic). Ionic should respect standard js notation (although purely speculation on my part)
var TimerData = localStorage.get("TimerData");
if (!TimerData.timerState) {
TimerData["timerState"] = "run";
TimerData["timeOutMode"] = false;
TimerData["timeOutStartDate"] = null;
console.log("test line", TimerData)
}

Modifying javascript class attributes

What i am trying to do here is, I have the following class Session
function Session(){
this.accounts = {};
this.setupAccounts = function(res){
this.accounts = res;
log(res);
log(this.accounts);
};
this.test = function(){
log(this.accounts);
};
}
The class Session has an attribute accounts, which will keep store certain data. But in order to initialize it, i initialize it as an empty object.
Next I call the method setupAccounts to modify the value of accounts. For example, I read a file, load it's data and then store that data inside accounts.
But I am having scope problems.
For example the following code :
var session = new Session();
var user_account_path = '/adata/user_accounts.json';
loadJsonFile(user_account_path)
.then(session.setupAccounts);
session.test();
So what i am doing in the code above is fetching the contents of a file as a Json Object and then I am passing that data to the method setupAccounts in order to store that data in the variable accounts. But my output looks like the following:
Object {arjrule3: Object} // printing the json object read from file
Object {arjrule3: Object} // locally changed value of accounts
console.log(session.accounts) // printing global value of accounts
{} // value has not changed.
What am i doing wrong? Why isn't the value for accounts changing for the object session ?
Something Funny just happened, if i write the code as the following:
var session = new Session();
var user_account_path = '/adata/user_accounts.json';
loadJsonFile(user_account_path)
.then(function(res){
session.setupAccounts(res); // Change Here
});
Output:
Object {arjrule3: Object}
Object {arjrule3: Object}
session.accounts
Object {arjrule3: Object} // works! Why ?
It works, why is it so ?
var session = new Session();
var user_account_path = '/adata/user_accounts.json';
loadJsonFile(user_account_path)
.then(session.setupAccounts);
session.test();
In the above example you're just passing the function "setupAccounts" as the callback. You would need to bind it first e.g.
var session = new Session();
var user_account_path = '/adata/user_accounts.json';
loadJsonFile(user_account_path)
.then(session.setupAccounts.bind(session));
session.test();
The other example you've added works because you're calling the "setupAccounts" function on the session object, not just passing a reference to it.

Meteor Storing and Retrieving Custom Objects in Session

I have a custom shopping cart object that I created and put it in the lib folder.
ShoppingCart = function ShoppingCart() {
this.Items = new Array();
this.grandTotal = 0.00;
}
ShoppingCart.prototype.addItem = function(Item){
this.Items.push(Item);
this.Items.sort();
this.calculateTotal();
}
I initialized the shopping cart and store it as Session.set('shoppingCart') during the page created phase.
Template.loginStatus.created = function() {
Session.set('loginShown',false);
if(!Session.get('shoppingCart')){ //set default if session shopping cart not exist
var cart = new ShoppingCart();
Session.setDefault('shoppingCart',cart);
}
Then when user click add item to cart, it will trigger this logic:
var cart = Session.get('shoppingCart');
cart.addItem(item);
Session.set('shoppingCart',cart);
Somehow, it does not work. When I take a look ad the chrome console it says undefined is not a function, pointing at cart.addItem(item) line. If I change it to this, it will work , but of course since everytime new shopping cart is created, I cannot accumulate items in the cart.
var cart = new ShoppingCart();
cart.addItem(item);
Session.set('shoppingCart',cart);
How should I store and retrieve the object from session properly? It looks like the returned object from the Session.get() somehow not considered as ShoppingCart. Did I miss any type cast?
As #Peppe L-G mentioned, you can only store EJSONs in Session. To store your custom object, you need to be able to manually transform it to and from EJSONs. Example:
_.extend(ShoppingCart, {
fromJSON: function(json) {
var obj = new ShoppingCart();
obj.grandTotal = json.grandTotal;
obj.Items = json.Items;
return obj;
},
});
_.extend(ShoppingCart.prototype, {
toJSON: function() {
return {
grandTotal: this.grandTotal,
Items: this.Items,
};
},
});
Then you can save it to Session:
Session.set('shoppingCart', cart.toJSON());
and restore:
ShoppingCart.fromJSON(Session.get('shoppingCart'));
I ran into the same problem. Essentially what is happening Meteor Sessions (and Collections) can only store EJSON types, so your ShoppingCart custom type is retrieved from the Session as a normal Object.
While you can manually transform to and from EJSONs, you may end up needing to do this repeatedly in a lot of different places. If your ShoppingCart is a member of another object, you'll have to also manually transform the member. It's better to use EJSON.addType to tell Meteor how to handle it automatically anywhere you store or retrieve an object of that type.
There's a great demo of this here: https://www.eventedmind.com/feed/meteor-create-a-custom-ejson-type. Full docs are also here: http://docs.meteor.com/#/full/ejson. But a short version is this:
Add a method to your custom type called typeName:
ShoppingCart.prototoype.typeName = function(){
return "ShoppingCart";
};
Add another method called toJSONValue:
ShoppingCart.prototype.toJSONValue = function(){
/* return a JSON compatible version of your object */
};
And finally, add the custom type to EJSON with:
EJSON.addType("ShoppingCart", function fromJSONValue(value){
/* return an object of your custom type from the JSON object 'value' */
};
NOTE: the "Type Name" in steps 1 and 3 must match exactly.

Change var in object literal function

Hi guys I am writing some code using the object literal pattern, I have function that returns a value:
'currentLocation': function() {
var cL = 0;
return cL;
},
I then need to update the variable 'cL' from another function like this:
teamStatus.currentLocation() = teamStatus.currentLocation() + teamStatus.scrollDistance();
This part is part of another function - however I get an error back stating: invalid assignment left-hand side
I am guessing I can not update the variable in this way, could anyone suggest a better method or point me in the right direction.
Any help would be greatly appreciated.
Going to add more code to highlight what I am trying to do:
'currentLocation': function() {
var cL = 0;
return cL;
},
'increaseTable': function() {
if (teamStatus.currentLocation() <= teamStatus.teamStatusTableHeight() ) {
teamStatus.currentLocation = teamStatus.currentLocation() + teamStatus.scrollDistance();
$("#tableTrackActual").animate({scrollTop: (teamStatus.currentLocation)});
$("#tableMembers").animate({scrollTop: (teamStatus.currentLocation) });
//console.log(teamStatus.currentLocation());
teamStatus.buttonRevealer();
}
}
As you can see increaseTable should update the value of currentLocation - help this sheds more light on what I am trying to achieve.
You're writing teamStatus.currentLocation() =, which calls the function teamStatus.currentLocation and tries to assign to the return value. That isn't valid. You want just teamStatus.currentLocation = — no function call.
The variable inside your function is completely private to that function (and any functions defined within it). If you need to create a number of functions that share a set of private variables, you can do that with a closure. For instance:
var Thing = (function() {
var thingWideData;
function getData() {
return thingWideData;
}
function setData(newData) {
thingWideData = newData;
}
return {
getData: getData,
setData: setData
};
})();
What that does is create a Thing object which has getData and setData functions available for it, which get and set the completely private thingWideData variable contained by the anonymous closure. More about this pattern here and here, although the latter of those is more about private methods than private data.
What your code produces is:
0 = 0 + <some number>
Which variable do you want to update? cL? You are declaring it in the function, you cannot assign a value to it from outside. Depending on the rest of your code, you might be better off with getters and setters:
var object = {
_cL = 0,
get currentLocation() {
return this._cL;
},
set currentLocation(value) {
this._cL = value;
}
}
then you can do:
teamStatus.currentLocation = teamStatus.currentLocation + teamStatus.scrollDistance();
Update:
Regarding IE: If currentLocation should actually be just a number, it might be sufficient to just declare it as property:
var obj = {
currentLocation: 0
}

Categories