Is it possible to send a redirect from server side to client in Meteor framework.
I can't find out this functionality.
Is so, could you please show an example.
There are two ways to communicate between your client side and your server side:
Collections
Meteor.methods
Let's assume you have:
- FlowRouter
- Blaze
- FlowRouter Blaze
- autopublish
Using collections
You can do the following:
Collection file /collections/Events.js:
// Create a global collection
Events = new Meteor.Collection("Events");
Somewhere on the server side:
Events.insert({
type: 'redirect',
route: 'home'
// require user_id if you want this to work with multi users
params: {}
});
Client side route:
FlowRouter.router('/some/url', {
action: function () {
BlazeLayout.render('Layout', {main: 'displayedTemplate'});
},
name: 'home'
});
Client side layout:
Template.Layout.onCreated(function () {
// suscriptions to Events if autopublish have been removed...?
this.autorun(function() {
const event = Events.findOne();
switch (event.type) {
case 'redirect':
FlowRouter.go(event.route, event.param);
Events.delete(event);
...
}
});
});
Using methods
When the user does something and you want to redirect him:
Template.Layout.events({
'click h2': function (event, template) {
Meteor.call('getCoffee', function (err, event) {
if (err) {
//handle error
} else {
if (event.type === 'redirect') {
FlowRouter.go(event.route, event.param);
}
}
});
}
})
Related
I want to submit a request from my Meteor client to the server, that has the server make an HTTP request to a website, and then return the response to the client.
On a REST web server, I would make an HTTP GET from the client to the server, which would then make its own request and respond to the client.
I haven't added a REST interface to my Meteor app and don't want to add this overhead for just this one need. However, using collections to get this done is unweildy and not the right tool.
Is there any way for the Meteor client to securely ask the server to do something and get a response without using collections? I'm messing with meteor Methods such as:
Meteor.methods({
'/http/get'(name, cbk) {
cbk = cbk || function() {};
HTTP.get('http://www.google.com', {}, (err, data) => {
cbk(err, data);
});
},
});
However this isn't seeming to work. The call is being made on the Client side.
This is exactly what Meteor methods are for.
Meteor methods docs
Server
First define your method on your server:
Meteor.methods({
// Namespace for clarity
'make.rest_call'(callback) {
HTTP.get('http://www.google.com', {}, (err, data) => {
callback(err, data);
});
}
});
OR
If you need the client to do something with the data then return a promise here (promise docs)
Meteor.methods({
// Namespace for clarity
'make.rest_call'(callback) {
return new Promise((resolve, reject) => {
HTTP.get('http://www.google.com', {}, (err, data) => {
if (err) { reject(err); }
resolve(data);
});
}
}
});
Client
Then call it from your client:
// Simple call (just makes the call, does nothing on the client)
Meteor.call('make.rest_call');
OR
// Promise based call
Meteor.call('make.rest_call', (error, result) => {
if (error) { /* do something with error */ }
// result contains your promise.
result.then((data) => {
// do something with returned data
});
});
Loopback has the concept of non-database connectors, including a REST connector.
What is the right way of caching get requests to such data source?
Interesting thought... I think you'd have to do this yourself by creating a new custom remote method and check a local hash of values:
// in /common/models/myModel.js
var cache = {};
MyModel.lookup = function loopkup(someParam, next) {
if (cache[someParam]) {
// first see if the value is already in the cache
return next(null, cache[someParam]);
} else {
// otherwise do the REST remote method call...
MyModel.restLoopkup(someParam, function lookupCallback(err, data) {
if (err) { return next(err); }
cache[someParam] = data; // ...and then set the new cache value.
next(null, data);
});
};
MyModel.remoteMethod(
'lookup',
{
accepts: { arg: 'param', type: 'object', http: { source: 'query' } },
returns: { arg: 'results', type: 'object' },
http: { verb: 'get', path: '/lookup' }
}
);
This code would set up an endpoint at .../api/MyModels/lookup?param=foobar for the calling code to hit. Note that you would probably want to also set an expiration time for the data and properly manage the "cache". You could also use something like a redis store for the values instead of in-memory like I've done above.
Good luck!
Should it be implemented in the action creator, or in a service class or component? Does the recommendation change if it's an isomorphic web app?
I've seen two different examples:
Action creator dispatches an action login_success/login_failure after making the rest call
Component calls an api service first and that service creates a login_success or failure action directly
example 1
https://github.com/schempy/react-flux-api-calls
/actions/LoginActions.js
The action itself triggers a call to the api then dispatches success or failure
var LoginActions = {
authenticate: function () {
RESTApi
.get('/api/login')
.then(function (user) {
AppDispatcher.dispatch({
actionType: "login_success",
user: user
});
})
.catch(function(err) {
AppDispatcher.dispatch({actionType:"login_failure"});
});
};
};
example 2
https://github.com/auth0/react-flux-jwt-authentication-sample
The component onclick calls an authservice function which then creates an action after it gets back the authentication results
/services/AuthService.js
class AuthService {
login(username, password) {
return this.handleAuth(when(request({
url: LOGIN_URL,
method: 'POST',
crossOrigin: true,
type: 'json',
data: {
username, password
}
})));
}
logout() {
LoginActions.logoutUser();
}
signup(username, password, extra) {
return this.handleAuth(when(request({
url: SIGNUP_URL,
method: 'POST',
crossOrigin: true,
type: 'json',
data: {
username, password, extra
}
})));
}
handleAuth(loginPromise) {
return loginPromise
.then(function(response) {
var jwt = response.id_token;
LoginActions.loginUser(jwt);
return true;
});
}
}
What's the better/standard place for this call to live in a Flux architecture?
I use an api.store with an api utility. From https://github.com/calitek/ReactPatterns React.14/ReFluxSuperAgent.
import Reflux from 'reflux';
import Actions from './Actions';
import ApiFct from './../utils/api.js';
let ApiStoreObject = {
newData: {
"React version": "0.14",
"Project": "ReFluxSuperAgent",
"currentDateTime": new Date().toLocaleString()
},
listenables: Actions,
apiInit() { ApiFct.setData(this.newData); },
apiInitDone() { ApiFct.getData(); },
apiSetData(data) { ApiFct.setData(data); }
}
const ApiStore = Reflux.createStore(ApiStoreObject);
export default ApiStore;
import request from 'superagent';
import Actions from '../flux/Actions';
let uri = 'http://localhost:3500';
module.exports = {
getData() { request.get(uri + '/routes/getData').end((err, res) => { this.gotData(res.body); }); },
gotData(data) { Actions.gotData1(data); Actions.gotData2(data); Actions.gotData3(data); },
setData(data) { request.post('/routes/setData').send(data).end((err, res) => { Actions.apiInitDone(); }) },
};
In my experience it is better to use option 1:
Putting API calls in an action creator instead of component lets you better separate concerns: your component(-tree) only calls a "log me in" action, and can remain ignorant about where the response comes from. Could in theory come from the store if login details are already known.
Calls to the API are more centralized in the action, and therefore more easily debugged.
Option 2 looks like it still fits with the flux design principles.
There are also advocates of a third alternative: call the webAPI from the store. This makes close coupling of data structures on server and client side easier/ more compartmental. And may work better if syncing independent data structures between client and server is a key concern. My experiences have not been positive with third option: having stores (indirectly) create actions breaks the unidirectional flux pattern. Benefits for me never outweighed the extra troubles in debugging. But your results may vary.
I have collections that are published on the server side, and use iron router's waitOn to subscribe to these collections. However, on the client side I can never see any reference to the collections defined on the server side. The only way I can access them is by defining the collection on the client side (devices = new Meteor.Collection('devices')), but I don't see anyone doing this in their examples online. Here is the code:
Client Code:
Router.route('/devices', {
waitOn: function() {
return [
Meteor.subscribe('devices', Meteor.user()._id),
];
},
action: function() {
this.render();
}
});
Server Side:
Devices = new Mongo.Collection("devices");
Devices.allow({
'insert': function (userId, doc) {
if (userId === doc.accountId) {
return true;
}
return false;
},
'update': function (userId, doc) {
if (userId === doc.accountId) {
return true;
}
return false;
},
});
Meteor.publish('devices', function(id) {
return Devices.find({accountId: id});
});
I have removed autopublish, and from the examples online, I should just be able to reference Devices.find({}). Instead, I have to use devices = new Meteor.Collection('devices') which then causes issues since if I call this again, it will say I already have a collection called devices. Does anyone know why I can't reference Devices?
The reason you can't reference it on the client side is that you haven't made the collection available to the client.
Leave the .allow and .publish methods on the server side, but move your collection creation to the lib folder to make it available on both the client and server.
/lib/collections.js:
Devices = new Mongo.Collection("devices");
my sails.js app is embedded in php project where php generate some date for sails model. Can't find the way to fetch this date in beforeCreate callback or some custom method. I don't want use db to sync 2 models (model from sails & model from php). And i need to send some date to remote php app.
sails.js v.0.9.x
here is my controller:
module.exports = {
index: function (req, res) {},
create: function (req, res) {
if ( !req.param('login') )
throw new Error('Login is undefined');
if ( !req.param('message') )
throw new Error('Initial message is undefined');
var user, thread;
User.create({
id: 1,
name: req.param('login'),
ip: req.ip
}).done( function (err, model) {
user = model;
if (err) {
return res.redirect('/500', err);
}
user.fetch(); // my custom method
});
return res.view({ thread: thread });
}
};
and model:
module.exports = {
attributes: {
id: 'integer',
name: 'string',
ip: 'string',
fetch: function (url) {
var app = sails.express.app;
// suggest this but unlucky :)
app.get('/path/to/other/loc', function (req, res, next) {
console.log(req, res)
return next()
});
}
}
};
UPD My solution
Model:
beforeCreate: function (values, next) {
var options = 'http://localhost:1337/test',
get = require('http').get;
get(options, function (res) {
res.on('data', function (data) {
_.extend( values, JSON.parse( data.toString() ) );
next();
});
}).on('error', function () {
throw new Error('Unable to fetch remote data');
next();
});
}
Yikes--app.get is nowhere near what you need. That's binding a route handler inside of your app and has nothing to do with requesting remote data. Don't do this.
The easiest way to fetch remote data in Node is using the Request library. First install it in your project directory using npm install request. Then at the top of your model file:
var request = require('request');
and in your fetch method:
fetch: function(url, callback) {
request.get(url, function(error, response, body) {
return callback(error, body);
});
}
Note the added callback parameter for fetch; this is needed because the request is an asynchronous operation. That is, the call to fetch() will return immediately, but the request will take some time, and when it's done it will send the result back via the callback function. Seeing as fetch is at this point just a wrapper around request.get, I'm not sure why it's necessary to have it as a model method at all, but if the URL was based on something within the model instance then it would make sense.