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.
Related
I have a really simple JS code:
var worldRef = firebase.database().ref('something/something1');
var something = new Object();
worldRef.on('child_added', function(snap) {
something.id = snap.key;
something.value = snap.val().value;
});
console.log(something);
It works almost fine - just one key&value from database, but it's something. If I modify it:
console.log(something.id);
Undefined. I've tried with map instead of object, but same. I see everything in map, but when I try to call the getmap(key), it's undefined.
Array-based solution:
var worldRef = firebase.database().ref('something/something1');
var something = [];
worldRef.on('child_added', function(snap) {
something.push ({
id: snap.key,
value: snap.val().value
})
});
console.log(something);
It works.
This is because each time a child is added, you re-assign the id and value properties of your something object.
Since, the child_added event is triggered once for each initial child at this location (see the doc), at the end your object just contains the last children element.
If you do as follows (creating a new property for each child), you will see the same effect than with the Array:
var something = new Object();
worldRef.on('child_added', function (snap) {
something[snap.key] = snap.val().value;
});
I do the following:
const blockOrField = new Block();
ev.dataTransfer.setData("data", blockOrField);
When I get data in another place:
cosnt data = e.dataTransfer.getData("data");
I get data as [object Object] instead real instrance.
Before passing to data I see that it is instance:
if (blockOrField instanceof FieldDefinition) {
alert("works");
}
ev.dataTransfer.setData("data", blockOrField);
I know it should be serialized to string JSON, but I have complicated instance with composition.
If you look at the documentation for setData() it specifically says "A DOMString representing the data to add to the drag object.". So you are out of luck trying to store an object reference there.
What I would do here is create a another object somewhere and store the needed reference there with an id.
const dataTransferCache = {};
function onDragStart(ev) {
const block = new Block();
const id = GetRandomId(); // Just get an id somehow
dataTransferCache[id] = block;
ev.dataTransfer.setData("data", id);
}
function onDragEnd(ev) {
const id = ev.dataTransfer.getData("data");
const block = dataTransferCache[id];
delete dataTransferCache[id]; // Remove the value again
}
This would even support multi touch dragging if that is somehow needed. If this needs to be shared between components you could simply put the dataTransferCache in a separate file and include a reference to in in both components.
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;
});
I want to pass object in data context in route.
I am trying to read JavaScript object(which holds the business logic) . If i send the object as such , then i can read it's properties in template. But if i try to send a custom object by reading some of it's properties . Then I am getting error that it can not read properties of same object.
this.route('updateBook/' ,
{
path:'/updateBook',
loadingTemplate: 'loading',
data : function(){
var Book = Books.findOne({hasFinished:true});
var UpdateBookObject ={};
UpdateBookObject.currentPage = Book.currentPage;
UpdateBookObject.name = Book.name;
UpdateBookObject.author = Book.author;
UpdateBookObject.img = Book.img;
UpdateBookObject.numOfPages = Book.numOfPages;
UpdateBookObject.dateStarted = Book.dateStarted;
UpdateBookObject.dateToFinish = Book.dateToFinish;
UpdateBookObject.percentage = (UpdateBookObject.currentPage/UpdateBookObject.numOfPages).toFixed(2);
UpdateBookObject.pagesRemaining = UpdateBookObject.numOfPages - UpdateBookObject.currentPage;
var finishMoment = moment(UpdateBookObject.dateToFinish,"DD-MM-YYYY");
var startMoment = moment(UpdateBookObject.dateStarted,"DD-MM-YYYY");
UpdateBookObject.perDayToRead = finishMoment.diff(startMoment,'days');
return UpdateBookObject;
}
});
Pretty sure it's because UpdateBookObject is returning before all of those properties are added to it. Why don't you return the Book variable you declared (which you said works) and then use a template helper to make the calculations?
Templates.updateBook.helpers({
updatedBook: function() {
//return data here
}
});
And register global template helpers to do your date parsing for you using moment, this is how I do it for timestamps:
Global Template Helper:
Template.registerHelper("dateAndTime", function(date) {
if(date)
return moment(date).format('l LT');
}); //returns formatted Date and Localized Time, e.g. 2/22/2015 12:30PM.
Spacebars in Template:
{{dateAndTime dateStarted}} //where createdAt is the field in the document
You can also use global template helpers instead of standard ones to do percentage calculations, and just pass the arguments from the Book object in with the Spacebars syntax in your templates.
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.