I'm working on a node.js single-page web application where I wanna use MVC. On my back-end, i'm using postgres database to store data that will be provided by the user throught forms interaction. For this, i'm also using Sequelize.
When 'npm start' runs, the application verifies if the database already exists locally, if not, then it is created with its tables and relations, just as expected.
On my front-end, the application loads a file called index.html (where all forms will be located). In this file, I'm trying to use my controllers to call 'create' methods using Sequelize, but i can't exacly call those functions on html script tag.
The first errors i was getting was 'required is not defined' when I was trying to do things like 'var PropController = require('../controllers/PropriedadeController.js')'. After some research, just figured out that it has somethings to do with browser/client-side in JavaScript.
I'm now trying to deal with this using Browserify, but still can't find out how it fits in my case. I did some research, but couldn't find a similar situation.
Basically, my idea is to reference all my controllers in a script.js file, and then browserify this file, in order to use it in my html file in the script tag.
My controller file:
const models = require("../models")
exports.findCreate = obj => {
return new Promise((resolve, reject) => {
models.Produtores.findOrCreate({
attributes: ["propt_nomeProdutor"],
where: { propt_nomeProdutor: obj.propt_nomeProdutor },
defaults: {
propt_nomeProdutor: obj.propt_nomeProdutor
}
})
.then(resp => {
resolve(resp);
})
.catch(e => {
reject(e);
});
});
};
My script.js file:
const ProdController = require("./ProdutorController");
module.exports = ProdController
And in my index.html file, after running 'browserify script.js -o bundle.js', i'm calling this generated script. So now, Inside a script tag, i'm trying to:
`(html form here)
<script src="../controllers/bundle.js"></script>
</body>
</html>
<script>
// ProdController.findCreate(obj) ...
</script>`
After trying this, i'm getting an error 'ProdController is not defined'. I even tried to browserify my controller file and my script.js into the bundle.js file, but then i'm getting an error witch I couldn't solve either:
Uncaught Error: Cannot find module './dialects/postgres/data-types'
I would like to know if there is any solution for this. I'm afraid that it's not possible to do this using these dependencies all together. I did a similar application using Electron, but now i'm trying to deal with a browser, and can't figure out how to do it.
This project is in my github repo: https://github.com/gabrielftwgarcia/TCC2
Any help will be greatly appreciated.
I think you are not familiar with front-end and back-end concepts. Controller is used at back-end side and it cannot use at front-end side. If you want to call a controller method you should write a new router to execute that method.
And another important thing is require method belongs to Node scripts and cannot be used at front-side.
Related
I've been trying to figure out a better way to push a variable from the backend to the frontend. Right now I do something like this.
I have a MVC-pattern, so when hitting the route
app.get('/fil', middleWare.isLoggedIn, user.fil)
... trough node does some querying the DB, and pass on the data.
exports.fil = async (req, res) => {
try {
faktura = await Lan.find().populate('client', 'namn kundnr')
res.status(200).render('pages/createInvoice', {
faktura: faktura
});
} catch (err) {
return res.status(500).send({
message: err.message || "Some error occurred while retrieving datas."
});
};
};
... it generates the page, with the help of EJS (i love EJS) and then pass it on to the client/user.
And in the .ejs-file that is served to the client/user I add the following
<script>
var fakturor = <%- JSON.stringify(faktura) %>;
</script>
which then means that I use up the variable and work with it with JS.
And this is where my question pops up. Is this a good way to do it or is there any other way to handle it?
I guess one idea is to let the user to query the DB straight from the page, but in my case I believe it wouldn't actually be better for the user to do so (the user will reieve like 100 different rows that they will be able to filter and then download a file of)
But is there any other ways I could do this without the script-tag? Like i said, I guess a ajax-call from JS/the client could be used but could you do it any other way? Can EJS do it any other way?
ejs is used for static pages mainly, if you want to build a dynamic page I would look for a single page application framework like angular and react.
if you still want to use ejs you can use ajax call to the server to load a variable from the DB.
I would never query directly from Front end to DB because then you are not controlling the security of the server, always go through the BE.
also try to think if you really need a variable in the front end, can you solve your problem using rendering only?
I am creating a simple app using angular and I am trying to use mongodb to save my data. So far I managed to create my SPA with angular and deploy it to heroku adding the server.js file. My problem starts when I tried to connect mongodb.
Currently I was serving my page using
app.get('/*', function(req,res) {
res.sendFile(path.join(__dirname,'/dist/showoff/index.html'));
});
Inside index.html I am calling <app-root> and my application has two routes: /display and /control
I then realized I have to add some more routes to save and read from my database through a service, so I had to add things like:
router.route('/players').get((req, res) => {
Player.find((err, player) => {
if (err)
console.log(err);
else
res.json(player);
});
});
Problem is that I cant reach those routes since I have already one with /*. I tried writing this other routes on top as I figured it might take the first it finds but its not working and I am always redirected to my index.html
My question is:
Is there a way to deploy my SPA like this and still use mongo? or do I need to somehow restructure everything since my approach isn't right?
You can find my whole code here if needed GitHub code
In case it helps anyone I did the following. Not sure if its the best way to go but it worked.
I divided my /* route as follows
app.get('/display', function(req,res) {
res.sendFile(path.join(__dirname,'/dist/showoff/index.html'));
});
app.get('/control', function(req,res) {
res.sendFile(path.join(__dirname,'/dist/showoff/index.html'));
});
And continue to add handlers for other routes there, which manage the requests to write and read from the database.
I am currently making a project using vue-cli which generates a Node project with webpack. I can build all scripts and use the pug language within .vue files. When building, the project uses HTML-Webpack-Plugin, though, which outputs the code as static HTML with client-side scripts.
How do I pass variables from the server-side to these client-side scripts? I previously was using pug, which made this process easy, but since it now gets built into .html files, this can no longer be done.
I have two attempts, both suboptimal:
1. Send variables into clientside scripts
script.
const clinetSideVar = `!{serverSideVar}`;
The problem with this approach is that I cannot pass this variable into the vue instance, since it gets obfuscated when built and I have no way of accessing it (or do I? I haven't found a way).
2. Using AJAX requests
I could also make a restful API for server-side site data and retrieve it using AJAX, but this seems like a real hack and this would lose quite a bit of performance over just sending the data plainly through a pug template (with no. 1 I'd too, since client-side JS would have to insert the data into the DOM).
I'd recommend using JSONP (since your index.html is built ahead of time in Vue-cli).
In your public/index.html (which is a template)
<head>
...
<script>function getServerData(data) { window.__SERVER_DATA__ = data; }</script>
<script src="/api/server-data.json?callback=getServerData"></script>
</head>
In your Node Express routes definition
const app = express();
// ...
app.get('/api/server-data.json', (req, res) => {
res.jsonp({
foo: 'foo',
bar: 'bar'
});
});
Then just access window.__SERVER_DATA__ from within any Vue component.
I want to extend this open topic: Add Routes at Runtime (ExpressJs) which sadly didn't help me enough.
I'm working on an application that allows the creation of different API's that runs on NodeJs. The UI looks like this:
As you can see, this piece of code contains two endpoints (GET, POST) and as soon as I press "Save", it creates a .js file located in a path where the Nodejs application is looking for its endpoints (e.g: myProject\dynamicRoutes\rule_test.js).
The problem that I have is that being that the Nodejs server is running while I'm developing the code, I'm not able to invoke these new endpoints unless I restart the server once again (and ExpressJs detects the file).
Is there a way to register new routes while the
NodeJs (ExpressJs) is running?
I tried to do the following things with no luck:
app.js
This works if the server is restarted. I tried to include this library (express-dynamic-router, but not working at runtime.)
//this is dynamic routing function
function handleDynamicRoutes(req,res,next) {
var path = req.path; //http://localhost:8080/api/rule_test
//LoadModules(path)
var controllerPath = path.replace("/api/", "./dynamicRoutes/");
var dynamicController = require(controllerPath);
dynamicRouter.index(dynamicController[req.method]).register(app);
dynamicController[req.method] = function(req, res) {
//invocation
}
next();
}
app.all('*', handleDynamicRoutes);
Finally, I readed this article (#NodeJS / #ExpressJS: Adding routes dynamically at runtime), but I couldn't figure out how this can help me.
I believe that this could be possible somehow, but I feel a bit lost. Anyone knows how can I achieve this? I'm getting a CANNOT GET error, after each file creation.
Disclaimer: please know that it is considered as bad design in terms of stability and security to allow the user or even administrator to inject executable code via web forms. Treat this thread as academic discussion and don't use this code in production!
Look at this simple example which adds new route in runtime:
app.get('/subpage', (req, res) => res.send('Hello subpage'))
So basically new route is being registered when app.get is called, no need to walk through routes directory.
All you need to do is simply load your newly created module and pass your app to module.exports function to register new routes. I guess this one-liner should work just fine (not tested):
require('path/to/new/module')(app)
Is req.params enough for you?
app.get('/basebath/:path, (req,res) => {
const content = require('content/' + req.params.path);
res.send(content);
});
So the user can enter whatever after /basepath, for example
http://www.mywebsite.com/basepath/bergur
The router would then try to get the file content/bergur.js
and send it's contents.
I'm trying to get google calendar events into my React Redux app.
I've tried using googleapis and google-auth-library but webpack is throwing errors because googleapis was built to run server side and bundle.js is referenced from client.
So I've read a few forums about these errors and they all point to using Google's js client library instead.
I understand how to implement this in a java or php app (I'm old... 35 ;) but I'm new to React Redux and I'm looking for the best way to implement this.
I'm trying to fetch the events from my calendar in my actions.js. I tried including <script src="https://apis.google.com/js/api.js"></script> in my html header and then using gapi.load() from actions.js. I also tried creating a api.js file and referencing that with require('./api'). I also tried to use the cli commands from the Node.js Quickstart guide to get an access_token and then just use axios to call Google API directly but I'm getting a 403. I'm thinking I'm just not providing the proper headers but that wouldn't be best practice anyway.
My question is basically how do I reference Google's js client library from my actions.js file while adhering to Redux standards?
You're on the right track by including the official Google client API in the HTML header. It's less than ideal -- it would be nice if Google provided the (browser) client API as an npm module that you could import. But they don't (that I see), so I think what you're doing is fine.
Then there's the question of "how do I use it in a way that's React/Redux friendly?" Redux is a mechanism for managing the state of your application. The Google API is not part of your application (though what you do with it may inform the state of your application).
It's easy to verify that you have access to the Google API: you can just make a call from the componentDidMount method of one of your components, and do a console log:
class MyComp extends React.Component {
componentDidMount() {
// this is taken directly from Google documentation:
// https://developers.google.com/api-client-library/javascript/start/start-js
function start() {
// 2. Initialize the JavaScript client library.
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
// clientId and scope are optional if auth is not required.
'clientId': 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
'scope': 'profile',
}).then(function() {
// 3. Initialize and make the API request.
return gapi.client.request({
'path': 'https://people.googleapis.com/v1/people/me',
})
}).then(function(response) {
console.log(response.result);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// 1. Load the JavaScript client library.
gapi.load('client', start);
},
}
If you don't see what you expect on the console, somehow gapi isn't getting loaded as you expect. If that happens, you'll have a more specific question you can ask!
If you do get a response, you now know how to call GAPI...but then how to make use of it in a Redux-friendly way?
When you make a GAPI call, you probably want to modify your application's state in some way (otherwise why would you be doing it?) For example, you might invoke the auth flow, and when GAPI returns success, your application state now has loggedIn: true or similar (possibly with lots of other state changes). Where you make the GAPI call is up to you. If you want to do it when the component loads, you should do it in componentDidMount. You also may commonly be making the GAPI call in response to a user action, such as clicking on a button.
So the typical flow would be something like this:
// either in componentDidMount, or a control handler, usually:
someGapiCall()
.then(result => {
this.props.onGapiThing(result.whatever)
})
Where this.props.onGapiThing is a function that dispatches an appropriate action, which modifies your application state.
I hope this overview helps...feel free to follow up with more specific questions.
Can you try this library which I used to load external libraries and modules in my React app when I couldn't find a NPM module for it:
https://github.com/ded/script.js/
So your code will be like this:
import $script from 'scriptjs';
$script('https://apis.google.com/js/api.js', function () {
//Put your google api functions here as callback
});
I'm going to answer my own question despite some very good correct answers.
#MattYao answered my actual question of how to get a js script available for reference in my actions.js file.
#Ethan Brown gave a very detailed answer that showed some excellent flow possibilities.
#realseanp changed the scope but a valid answer.
I tried all of the above and they worked.
So I'm not sure what I was doing wrong but I was finally able to access the gapi object from actions.js by just adding <script src="https://apis.google.com/js/api.js"></script> to my index head.
I'm using pug so it looks like this:
doctype
html
head
title MyTitle
link(rel='stylesheet' href='/static/css/main.css')
link(rel='stylesheet' href='/static/css/react-big-calendar.css')
script(src='https://apis.google.com/js/api.js' type='text/javascript')
body
div(id='app')
script(src='/static/bundle.js' type='text/javascript')
Here is my component file:
import React from 'react'
import BigCalendar from 'react-big-calendar';
import moment from 'moment';
import { connect } from 'react-redux'
import { fetchEvents } from '../actions/actions'
BigCalendar.momentLocalizer(moment);
#connect((store) => {
return {
events: store.events.events
}
})
export default class Calendar extends React.Component
{
componentWillMount()
{
this.props.dispatch(fetchEvents())
}
render()
{
return (
<div>
<BigCalendar
events={this.props.events}
startAccessor='startDate'
endAccessor='endDate'
style={{height: 800}}
/>
</div>
)
}
}
And then I was able to get this working in my actions.js file
export function fetchEvents()
{
return (dispatch) =>
{
function start()
{
// 2. Initialize the JavaScript client library.
gapi.client.init({
'apiKey': API_KEY,
// clientId and scope are optional if auth is not required.
'clientId': CLIENT_ID,
'scope': 'profile',
}).then(function() {
// 3. Initialize and make the API request.
return gapi.client.request({
'path': 'https://www.googleapis.com/calendar/v3/calendars/MY_EMAIL#gmail.com/events?timeMax=2017-06-03T23:00:00Z&timeMin=2017-04-30T00:00:00Z',
})
}).then( (response) => {
let events = response.result.items
dispatch({
type: 'FETCH_EVENTS_FULFILLED',
payload: events
})
}, function(reason) {
console.log(reason);
});
};
// 1. Load the JavaScript client library.
gapi.load('client', start)
}}
I had to make my calendar public to access it this way. So now I'm going to work on the oauth2 stuff :/
I would load all the google stuff in my index file before i loaded my webpack bundle (Option 1) . Then I would use redux sagas to call the google apis. Loading the google code before your webpack bundle will ensure everything is ready to go when you call the api from the saga
Try this package.
It looks like it is updated.
https://github.com/QuodAI/tutorial-react-google-api-login