I am needing to have 1 page of a website submit data to MongoDB then pull that same data and display it on a different page. I am using node.js, express, Mongoose, and MongoDB.
Currently, I have it so It gets submitted properly to the database, and I can see it with the correct layout, But I cannot seem to get it posted on the page.
How exactly do I go about doing this?
Can someone give a example of code of this?
I am really new to this stuff and still learning.
Thanks!
In the route of the page you want to load, use the Mongoose .find()
method.
You can use {} in the find() method to return all the data, or access
individual data based on the object key find({id:'value'}). Then when you
render the page, just pass in an object to the render, where the key is
what you access in the url page, in my example you would use
(mongs) to access the values within the url page (.ejs, etc). So in
your route definition file:
app.get('/', (req, res) => {
MongModel.find({}, (err, foundMongModel) => {
err ? console.log(err) : res.render('url/route', { mongs: foundMongModel });
});
});
Then if you're using .ejs file, you would need to use <%= %> to access
individual data, and <% %> to use a loop or something.
and use the mongs value. So if you imported all the data from the
database, you could loop through it using
<% mongs.forEach(mong =>{ %>
<div>mong.key<div>
<% }) %>
You can access the keys for each database object like above using
mong.key
Related
I'm trying to create a function in a controller and use it in an ejs file. My purpose is to send this function to the ejs file and call it with other data I'm sending and show elements in the list by using for loop. In this situation, I have a Category and Course model. The course model has name and category(references to category collection by using ID) fields. In controller.js I created a function getCoursesByCategory(), I want to use it in the ejs file and get the releated courses.
ERROR: I'm able to access the values via console.log() in the ejs but when I return the values, [object promise] text appears in the list; thus I can't show data on the website.
Notes: I tried using getCoursesByCategory(categories[i]._id).then((courses)=>courses) in the ejs file it didn't work but I can log it as getCoursesByCategory(categories[i]._id).then((courses)=>console.log(courses)) and it shows data in the console. Also I tried self-executing async function in the ejs file to get data it didn't work either.
I'm using express/mongoose.
Thanks for any help.
[Edited]
I found topics about using {async:true} option: ejs async true with node Express
When I added this option, page stays in the loading position all the time and not responding.
CONTROLLER.JS
const Category = require('../models/Category');
const Course = require('../models/Course');
.
.
.
exports.getIndexPage = async (req, res) => {
const categories = await Category.find();
async function getCoursesByCategory(categoryid){
return await Course.find({category: categoryid});
}
res.status(200).render("index", {
categories,
getCoursesByCategory
})
}
INDEX.EJS
<% for(let i=0; i<categories.length;i++) { %>
<tr>
<td><%=categories[i].name%></td>
<td><%=getCoursesByCategory(categories[i]._id)%></td>
</tr>
<% } %>
I've got a variable, say data containing data in the form of an Array with each item having a unique ID.
app.get('/products/:id', function (req, res) {
res.send(data.map(data => "" + data.id + "")) //basically gets the data of the element in the Array whos Id has been given to the server.
})
I have sent the data from the server to the front-end on a GET request. But how do I create a seperate webpage for each element dynamically in the data array? where do i have to write the html and the css? I want a way with which I can create a page for each element like domain.com/products/id which displays information about the data entry which matches the Id . Do need to use pug? hbs?ejs? I' so confused.
So I found I had to use Javascript Templates to send data to a view. I used ejs, which went pretty good!
Here's how it went:
1. fetch my data form my DB, which in this case is MongoDB using db.findOne().
2. We get an array, let's say data. send the variable to my view using the same res.render syntax, just in a cooler way.
app.get('/blogs/:id',(req,res)=>{
const data = //find function
res.render('page.ejs', {body:data});
})
:id creates a page for every element in the DB.
and now the view that is, the public/page.ejs file has a global body variable, which
we can now use to show our blogs.
3. the front end markup in pages.ejs:
<div class="blogs">
<%=body.forEach (item)=>{%>
<p><%=item.blog%></p><br>
<%=}%>
</div>
We call a forEach function on the array, and create a paragraph element for each item in the array, that is for each blog.
Please not that <%, <%= and %> are EJS' tags. Read about them more in the official docs.
Thanks Mohammad for letting me know about this. (From comments)
I've created a simple blog site, with posts, users and comments.
I'm using - MongoDB, NodeJS and Express with an EJS view.
I've encounterd a problem when I tried to create the comments sections.
I wanted to use JQuery with Ajax requests in order to make the comments section
so the page won't refresh when a user is posting a comment or edting one.
In order to get the comments for each post, I built an api route that look like this - www.domain.com/api/messages/:post_id
this url returns JSON that contains the comments for that post.
I didn't want to expose 'risky' data about the author of the comment so I fillterd the results with the Mongo query.
Now for the problem -
I want to add edit and delete buttons for each comment, that would show up only for the admin and the author of course.
When I use EJS its simple - I wrote something like this -
<% if (user !== null&& (JSON.stringify(user._id) === JSON.stringify(comments[i].author._id)
|| user.is_admin === true)) { %>
put the buttons here...
The thing is, when I'm using JQuery on my main scirpt file, I can't access the user (that's an object that was sent from Nodejs on the backend).
I'm not sure if I should 'send' this object to my js file from the EJS, I think it's not secure and not the right way.
And I can't also keep that EJS code I quoted a few lines ago because when the EJS file loads there are no comments on that page (the XHR request gets them and JQuery puts them on the page).
So I'm really not sure how to move on.
Edit - I think i have a solution: Maybe I should create an array of IDs on the EJS file that would contain the comments that need to have these buttons, and then I would send that array to the JS file somehow?
Thank you!
So I think I managed to overcome this problem, I'll post what I did so hopefully it will help someone in the future.
In the EJS file, I assigned an array that will hold the ID's of the comments that were published by the current user (The user was sent to the EJS file by the nodejd backend).
Then, I copied this array to a script tag variable in the EJS file, By doing that, I can now access it from the main.js file, and add the delete/edit buttons only to comments that have an ID that's in the array.
<% var arr = []; %>
<% for(var i = 0; i < comments.length ; i++){ %>
<% if (user !== null && (JSON.stringify(user._id) === JSON.stringify(comments[i].author._id) || user.is_admin === true)) { %>
<% arr.push(comments[i].comment_id); %> <% } %>
<% } %>
<script>
var exported_array = <%- JSON.stringify(arr) %>
</script>
Send the user and all comments to the main EJS file.
app.get('/', function (req, res) {
res.render('index', {
comments: [{ ... }, { ... }, { ... }],
user: { ... }
});
});
The comments can be rendered by including a EJS partial comment.ejs from the folder partials in the main EJS file.
<% comments.forEach(function(comment){ %>
<% include partials/comment %>
<% }) %>
In that partial you render the comment and buttons. The comment is past through to the include. The user already lives on the main EJS template.
<div class="comment-container" data-commentid="comment._id">
<p><% comment.content %></p>
<% if (user !== null&& (JSON.stringify(user._id) === JSON.stringify(comment.author._id) || user.is_admin === true)) { %>
<button class="button_delete" data-commentid="<% comments_id %>">delete</button>
<% } %>
</div>
Then create a route in your API like www.domain.com/api/messages/delete/:post_id.
app.get('/messages/delete/:post_id', function(req, res){
// logged on user lives on the server
// check if user may delete comments
// delete comment
// send result
res.send(true);
});
When you click the button use jQuery to send that id from the data attribute of the button to the server/api and from there I would Identify the user that's logged on. If the logged on user is authorized delete it and send back the result. If everything is okay use jQuery to delete the div containing the whole message.
$('.button_delete').click(function(){
// retrieve id from data attribute here
var commentId = $(this).data("commentid");
$.get('www.domain.com/api/messages/delete/' + commentId, {}, function(result){
if(result) {
// delete comment container from html using commentId or refresh
}
});
});
I have some problems with my api.
I use NodeJS with ExpressJS Routing and AngularJS.
What I want to do is to render a template (ejs) and at the same time send a json object.
In my routes folder I have the index.js file with this (a little part) :
router.get('/data', function(req, res, next){
Planning.getPlanningM(function(resultat){
res.json(resultat);
res.render('index');
});
});
About the variable resultat I'm sure that it contains that I want. But I can't do the res.json and the res.render. Because of the two invoke of send function.
And in my angular I have this in a function :
var resultat = []
$http.get('/data')
.success(function(res){
angular.extend(resultat, res.data);
})
.error(function(res){
console.log('err');
})
return resultat;
The goal is to render my index.ejs and to show my planning in this page. But I find no solution to do this.
This is my first ask on stackoverflow, english is not my native language. Please don't be rude with me :)
I'm not familiar with EJS, I use handlebars, but you should be able to pass data in the render function like so-
...
res.render("index", { data:resultat });
...
Then access it in the template in whatever format EJS uses. For hbs it would look something like
...
<div>My data looks like this: {{data}}</div>
...
Again, EJS is sure to do it differently, refer to the doc to ensure you have the correct format.
Thanks for your answer MPawlak !It helped me !
That I want is to send the data with the render like you do yes.
But I want to grab/take this data in my angular Factory (my factory fills the controller, this part works) that I show before :
var resultat = []
$http.get('/data')
.success(function(res){
angular.extend(resultat, res.data);
})
.error(function(res){
console.log('err');
})
return resultat;
With your method, I can take this data into my view directly your right and it's works ! Thanks !
<pre> <%= data %> </pre>
So I was thinking about a dirty temporaly solution to do this:
<textarea ng-model="planning"> <%= data %> </textarea>
But when I want to show this planning it don't work and stay empty... I don't understand why.
But to get a good and clean solution I think this is not a good idea, so my ask is the same... how to take this data in my angular factory directly ?
I am using the FileStack API and the file picker gem (https://github.com/Ink/filepicker-rails). I have an Attachment model that has a :title as a string. When a file is uploaded, the URL from the FilePicker API is stored as the :title. But the gem has a onchange method that returns an event variable as a JSON object that contains attributes of the file. I use JavaScript to access those attributes but I want to find a way in Rails to store those attributes, accessed via JavaScript, in a Model so that I can access it through the rest of the Rails app.
<%= filepicker_js_include_tag %>
<%= simple_form_for(#attachment) do |f| %>
<%= f.filepicker_field :title, onchange: 'onUpload(event)' %>
<%= f.submit %>
<% end %>
<script>
function onUpload(event) {
console.log(event);
var name = event.fpfile.filename;
console.log(name);
}
</script>
Update:
So after looking into your solution and googling around I am using ajax to send the data via routes to the controller. Below is my updated Javascript as well as the route and controller. When I render and inspect the #foo instance variable it is nil. So my data isn't getting passed properly. Furthermore, this whole process from the firing of the Javascript function to displaying the index view is now very very slow. I think I have the right idea after viewing your solution and doing more digging but I'm missing something and/or overcomplicating this. Any advice would be much appreciated.
<script>
function onUpload(event) {
var name = event.fpfile.filename;
jQuery.ajax({
data : { data_value: event },
type: 'post',
url: "/attachment/index"
});
}
</script>
Route
post 'attachments/' => 'attachment#index'
Controller
def index
#attachments = Attachment.all
#foo = params[:data_value]
end
View (returns nil)
<%= raise #foo.inspect %>
If you're using Postgres 9.3 or above you should consider using the hstore module and creating a JSON column. In a migration you can do:
add_column :your_model, :your_attribute, :json
And then you can just update YourModel.your_attribute => {'your': 'JSON here'}
Docs here: http://edgeguides.rubyonrails.org/active_record_postgresql.html#json
If you're using MySQL it's tricky, but doable. You have to create a text column and save the JSON as a string, and parse it every time you interact with it. Postgres is definitely better at handling JSON. I realize that this answer relies on an assumption, so if you're not using one of the two data stores mentioned, let me know and I'll pull it down.