Sharing Server Side data to a JS file - javascript

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
}
});
});

Related

Writing EJS in JS script dynamically

i am trying to use Leaflet API and store my users saved points(markers,circles,polygons) into a MongoDB database.
Is there a more elegant and dynamic way of writing a JS script in a HTML page while getting the results from the database?
at the moment i am getting my data from Mongo and passing it through the Get request and using that on the HTML page, writing EJS inside the JS script. it works fine but im looking into a better solution.
many thanks for your time
// index.js
router.get('/index', ensureAuthenticated, (req, res) => {
var queryz = Points.find({ belongs_to: req.user._id })
queryz.exec(function (err, results) {
if (err) return handleError(err);
res.render('index', {
user: req.user,
points: results,
})
})
})
// index.ejs
// looping through the points from the database to dynamically add the points
// each time the map is called
<% if(typeof points != "undefined") { %> // making sure user is logged in
<% counter = 1 %>
<% if(points.length>0){ %>
<% points.forEach(p => {%>
<% if(p.type == "marker"){ %> // if point is a marker
marker<%=counter%> = L.marker([<%=p.coords%>], { icon: <%=p.icon%>, alt: '<%=p.popup_message%>' }).bindPopup('<%=p.popup_message%><form method="post" action="/delete_point" id="pointFORM"><input id="pointID" name="pointID" value="<%=p._id%>"><button id="submitBtn" type="submit">🚫</button></form> ').openPopup()
<% counter++%>
<% } %>
<% if(p.type == "circle"){ %> // if point is a circle
marker<%=counter%> = L.circle([<%=p.coords%>], { color: '<%=p.color%>', fillColor: '<%=p.fill_color%>', fillOpacity: <%=p.fill_opacity%>, radius: <%=p.radius%> }).bindPopup('<%=p.popup_message%>').openPopup()
<% counter++%>
<% } %>
<% if(p.type == "polygon"){ %> // if point is a polygon
marker<%=counter%> = L.polygon([<%=p.coords%>], ).bindPopup('<%=p.popup_message%>').openPopup()
<% counter++%>
<% } %>
<% });%>
<% } %>
<% } %>
I'm assuming the problem space here is that you want to dynamically fetch some data from your server and insert that into a web page using Javascript in the web page.
Given that, you have several options:
You can make an Ajax call to your server and have your server return fully formed HTML which your client-side Javascript can then insert directly into the page. That appears to be what you are illustrating already in your question.
You can make an Ajax call to your server and have your server return JSON which your client-side Javascript can then turn into HTML itself and insert that into the page. You can either manually code the conversion into HTML in Javascript or you can use client-side rendering with EJS to convert the data into HTML using a client-side EJS template.
You can reload the entire page and let the server render the whole new page, presumably including the latest data.
For incremental updates to a page, options 1 and 2 are generally preferable to option 3 and are usually more efficient for all.
The choice between 1 and 2 is really just an architectural preference. Some developers of high scale sites would rather offload as much processing to the client as possible and thus prefer letting the client do the rendering of the new HTML as in option 2, but that means you have to have both the EJS template and client-side EJS rendering in the web page which is more client-side weight. So, that's a tradeoff too.
There really aren't options that are simpler than these, so if what you have now (option 1) is working just fine you can stay with that.

How to include ejs partials with JavaScript?

I am writing some code which checks if a user has logged in. And if a user is logged in, the "my user" section should be different from when that user was not logged in.
So when the logged-in user enters the "my user" page, an if statement checks for if the user has logged in or not, and if the user is logged in, it should include a partial that corresponds to the logged-in state. If the user has not logged in, the if statement should include a different partial.
How do I include partials in a div using JS?
I have tried to use parentElement.innerHTML = "<%= include userNotLoggedIn.ejs %> without luck, and I have also tried to manually edit the html file while it runs in the browser using the inspect tool, but it doesn't fetch the template.
It seems like I can't include partials after the main html file has been rendered. Is this the case?
Here is my code:
if(document.getElementById("loginState").innerHTML == " Logg inn") {
//Append the correct html template if the user is not logged in
var includeStr = "<%= include notLoggedIn.ejs %>";
cont.innerHTML = includeStr;
} else {
//Append the desired html template if the user is logged in
var includeStr = "<%= include userLoggedInMenu.ejs %>";
cont.innerHTML = includeStr;
}
where cont is the container in which the template should be added to
I would expect the partial to be added to the container element, and that my notLoggedIn.ejs file is displayed in the html page. This is not the outcome, however.
Please check out the image below: (I can't post images directly into the post)
https://imgur.com/a/ISDFIPp
ejs gets rendered on the server side and gets sent back to the client as HTML. Id don`t think you can add in ejs at the client side.
One possible way to achieve what you are trying would be to set a variable called currentUser and the pass it on to the ejs file and in the file you would set a condition like below to conditionally display content.
// Making the logged in user available
app.use(function (req, res, next) {
res.locals.currentUser = req.user;
next();
});
<% if(!currentUser) { %>
// If the user is not logged in
<% } else { %>
// If the user is logged in
<% } %>

How to submit data then post on another page mongoDB/Node.js

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

How to consume,response with the JSON POST data in express application

I am using NodeJS and MongoDB.I have a simple app.You type your data to the textarea,when click submit,it creates a new object in a database.Then if you wanna check certain information in your database,you can type to input an id.It will search in the database for the object with the same id and then it will show you the information on the next page.It works nicely.But what i also need,is that if you will send the JSON POST request to my server,let's say,through POSTMAN,i want it to be displayed not as HTML,but as a JSON object.I guess i need to right a middleware that will check the request first,and if it should be html,it will keep running the code that renders an HTML,and if there is JSON request,then it will do another thing.
That's the code that finds the right object by it's id to show on the webpage:
app.post("/insertedids", function(req,res){
var inputid = req.body.emails;
db.collection('emails').find({"_id": ObjectId(inputid)}).toArray(function(err, foundObject){
if(err){
console.log(err)
} else {
res.render("insertedids",{emailslist: foundObject[0].emails});
};
});
});
And this is the code on the page where it shows the data:
<% include partials/header %>
<div class="centeredlist">
<h1>Emails on this ID </h1>
<ul>
<% emailslist.forEach(function(email){ %>
<li><%=email%></li><br/>
<% });%>
</ul>
</div>
<% include partials/footer %>
So i need that if there is a Json request,it will respond by showing a json objects,not an li's.
P.s inpuid that's the name of my input.
P.s.s i am sorry if i made grammar mistakes :D

Rails efficient way to store JavaScript object to Model?

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.

Categories