How do i change "this" object - redux

I know the rule that this object cant be changed but need an alternative method .
var that= this
axios.get('http://ec2-54-165-240-14.compute-1.amazonaws.com:3000/api/foodItem').then(function(data){
console.log("inside axios ",data)
that.setState({
items : data,
});
var curGroupId = that.props.cartReducer.val;
var items = that.state.items ;
var curItems= [];
for(var i in items){
if(items[i].food_group_id==curGroupId){
curItems.push(items[i]);
}
}
that.setState({
curItems : curItems
})
}).catch(function(err){
console.log(err)
})
I want to update the state in this object which is not accessible inside the then function and therefore i have stored this object in that before the function but i want to apply changes in the this object.

You can try using an arrow function, that way you will have access to this inside the inner functions.
axios.get('http://ec2-54-165-240-14.compute-1.amazonaws.com:3000/api/foodItem')
.then((data) => {
console.log("inside axios ",data)
this.setState({ // <---- references to the parent scope
items : data,
});
var curGroupId = that.props.cartReducer.val;
var items = that.state.items ;
var curItems= [];
for(var i in items){
if(items[i].food_group_id==curGroupId){
curItems.push(items[i]);
}
}
this.setState({ // this too ;)
curItems : curItems
})
}).catch((err) => {
console.log(err)
})
An arrow function will use the same scope as the parent function, pretty handy for these situations.
One more thing, I don't recommend calling setState multiple times. You should call it only once at the end of the callback, when all your data is ready to use.

Related

How to get object by id in the JSON format using angularfire2?

I am looking for simply fetching an object as a JSON, not as an observable.
So far I could:
fbGetCarById(car_id: string){
var car_json;
var car_obs: FirebaseObjectObservable<any>;
car_obs = this.db.object('/cars/'+car_id, { preserveSnapshot: true });
car_obs.subscribe(snapshot => {
console.log(snapshot.val())
car_json = snapshot.val();
});
return car_json;
}
However, .subscribe is an async function which does not return the snapshot linearly, so everything ends a mess =/.
How can I simply look for an object and have a simple JSON object as a response?
You could return a Promise.
fbGetCarById(car_id: string):Promise<string>{
const promise = new Promise<string>((resolve,reject)=>{
var car_obs: FirebaseObjectObservable<any>;
car_obs = this.db.object('/cars/'+car_id, { preserveSnapshot: true });
car_obs.subscribe(snapshot => {
resolve(snapshot.value())
});
}
return promise;
}

Meteor template autorun is not a function when using es6

Works
Template.Hello.onRendered(function() {
this.autorun(() => {
console.log('sup');
});
});
Doesn't work.
Template.Hello.onRendered(() => {
this.autorun(() => {
console.log('sup');
});
});
The error is TypeError: _this.autorun is not a function.
Any ideas why using arrow notation gives us this error?
Arrow functions use lexical binding of this which means that this will be whatever it was when the function was created. This means that you unfortunately can't use it when creating functions on objects that use object properties such as the template.
A small example is something like:
o = {};
o.fn = () => console.log(this);
o.fn(); // not 'o'
o.fn = function () { console.log(this); }
o.fn(); // 'o'
.autorun is a method of the template so the functional binding of this is required.
There are times when the lexical binding of arrow functions are useful such as in the callback to autorun. In that case, you want this to remain the same as the outer scope. Otherwise you would have to bind it:
Template.Hello.onRendered(() => {
this.autorun(() => {
console.log(this); // the template
});
this.autorun(function () {
console.log(this); // the template
}.bind(this));
this.autorun(function () {
console.log(this); // the callback function
});
});

How to show documents from multiple remote publication in the template?

I wish to use Meteor to subscribe a few remote publication via DDP. Then show the documents in one template. Here is what I did:
Posts = {};
var lists = [
{server: "localhost:4000"},
{server: "localhost:5000"}
];
var startup = function () {
_.each(lists, function (list) {
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection});
connection.subscribe("allPosts");
});
}
startup();
This file is at client folder. Every startup, in this example, at browser I have two client collections Posts["localhost:4000"] and Posts["localhost:5000"], both are same schema. I know this format (Collection[server]) is ugly, please tell me if there is a better way.
Is there a way to show these client collections in the same template with reactive. Like this:
Template.registerHelper("posts", function () {
return Posts.find({}, {sort: {createdAt: -1}});
});
I think Connected Client is a big part of the Meteor. There should be a best practice to solve this problem, right?
Solved.
Connect to multiple servers via DDP, then observe their collections reactive via cursor.observeChanges.
Posts = {};
PostsHandle = {};
// LocalPosts is a local collection lived at browser.
LocalPosts = new Mongo.Collection(null); // null means local
// userId is generated by another Meteor app.
var lists = [
{server: "localhost:4000", userId: [
"hocm8Cd3SjztwtiBr",
"492WZqeqCxrDqfG5u"
]},
{server: "localhost:5000", userId: [
"X3oicwXho45xzmyc6",
"iZY4CdELFN9eQv5sa"
]}
];
var connect = function () {
_.each(lists, function (list) {
console.log("connect:", list.server, list.userId);
var connection = DDP.connect(`http://${list.server}`);
Posts[`${list.server}`] = new Mongo.Collection('posts', {connection: connection}); // 'posts' should be same with remote collection name.
PostsHandle[`${list.server}`] = connection.subscribe("posts", list.userId);
});
};
var observe = function () {
_.each(PostsHandle, function (handle, server) {
Tracker.autorun(function () {
if (handle.ready()) {
console.log(server, handle.ready());
// learn from http://docs.meteor.com/#/full/observe_changes
// thank you cursor.observeChanges
var cursor = Posts[server].find();
var cursorHandle = cursor.observeChanges({
added: function (id, post) {
console.log("added:", id, post);
piece._id = id; // sync post's _id
LocalPosts.insert(post);
},
removed: function (id) {
console.log("removed:", id);
LocalPosts.remove(id);
}
});
}
})
});
}
Template.posts.onCreated(function () {
connect(); // template level subscriptions
});
Template.posts.helpers({
posts: function () {
observe();
return LocalPosts.find({}, {sort: {createdAt: -1}}); // sort reactive
}
});

Re-initialize template with new id on selectbox change

I have a selectbox in template A which selects an item "name and id".
I also have an "item" template that needs an ID as parameter to load its data from its database. I am using a session variable for the id and I pass the id to the "item" template using return Session.get . This only works for on load. When the session variable change the "item" template is not updated. How do I get the "item" template to re-initialize
Some code:
Template.selectBox.helpers({
selectList: function () {
return Templates.find({}, {fields: {'_id': 1, 'name': 1}});
},
selectedId: function() {
return Session.get("selectedId");
}
});
Template.selectBox.events({
'change #item-chooser': function (event) {
var selectedId = $(event.currentTarget).find(":selected").val();
if (typeof(selectedId) === 'undefined'
|| selectedId === "new") {
Session.set("selectedId", "new");
}
else {
Session.set("selectedId", selectedId);
}
}
});
The items template is called using
{{> item selectedId}}
Template.item.onCreated(function() {
var selectedId = this.data.selectedId;
this.selectedItem = new ReactiveVar;
if (typeof(selectedId) === 'undefined'
|| selectedId === "new") {
this.selectedItem.set(emptyItem);
}
else {
var selectedItemData = Templates.findOne({_id: selectedId});
this.selectedItem.set(selectedItemData );
}
});
It's important to note that the Template.onCreated method is not reactive so if you have reactive variables, this method does not automatically re-run when a reactive data source changes unlike Template.helpers
The easiest way to fix your problem would be to use autorun
Template.item.onCreated(function(){
var self = this;
self.autorun(function(){
// some code that has a reactive data source (e.g. Session Var, Collection, or Reactive Var
// NOTE: you can access template instance data using self.data
});
});
However, based on your description, I think there could be a better way to handle your problem using Template.helpers
Template.item.helpers({
selectedItem: function(){
return Templates.findOne({_id: Session.get("selectedId")});
}
});

Async call generates " Error: Can't wait without a "fiber", even with _wrapAsync

I've been having a problem using an RSS parser in meteor. It's an async call, so it needs ot be wrapped, however it still doesn't seem to work. I presume this is because the anonymous on('readable' function is outside the fiber, but I can't see how to resolve it.
var FeedParser = Meteor.require('feedparser');
var request = Meteor.require('request');
function getBlog(url, parameter, id){
request(parameter)
.on('error', function (error) {
console.error(error);
})
.pipe(new FeedParser())
.on('error', function (error) {
console.error(error);
})
.on('readable', function () {
var stream = this,
item;
while (item = stream.read()) {
Items.insert(new_item);
}
});
}
var wrappedGetBlog = Meteor._wrapAsync(getBlog);
Meteor.methods({
blog: function (url, parameter, id) {
console.log('parsing blog');
var items = wrappedGetBlog(url, parameter, id);
}
});
Meteor._wrapAsync() expects the wrapped function to return error and result to a callback. Your function, getBlog(), does not do that so _wrapAsync is not the right approach.
I have wrapped that function before but used a Future.
That approach allowed me to call feedparser from a Meteor.method(), which doesn't allow async functions, but you are also trying to do an insert inside the readable event. I think that insert will complain if it is not in a fiber. Maybe like this would also be necessary:
var r = request( parameter );
r.on( 'response' , function(){
var fp = r.pipe( new FeedParser() ); //need feedparser object as variable to pass to bindEnvironment
fp.on('readable', Meteor.bindEnvironment(
function () {
var stream = this,
item;
while (item = stream.read()) {
Items.insert(new_item);
}
}
, function( error ){ console.log( error );}
, fp // variable applied as `this` inside call of first function
));
});
Fibers is another option...
var Fiber = Npm.require( "fibers" );
.on('readable', function () {
var stream = this,
item;
while (item = stream.read()) {
Fiber( function(){
Items.insert(new_item);
Fiber.yield();
}).run();
}
});

Resources