node.js ejs - Don't render file if it does not exist - javascript

I have a views folder structure and a ejs file profile_60113.ejs like this
views
docs
profile_60113.ejs
I can dynamically render the file like this (where data.groupID == 60113):
<%- include("docs/profile_" + data.groupID); %>
But how can I first check if the file exists? I tried this:
<% if (fs.existsSync("views/docs/profile_" + data.groupID)) { %>
<%- include("docs/profile_" + data.groupID); %>
<% } %>
Or ...
<% if (fs.existsSync("docs/profile_" + data.groupID)) { %>
<%- include("docs/profile_" + data.groupID); %>
<% } %>
But didn't work ...
const fs = require('fs') is included in the controller and fs:fs is rendered

What works is e.g.:
Option 1 (Synchronously): Template
Serverside:
const fs = require('fs');
var getGroupID = 60113;
res.render('docs', {
page: setPage,
data: {groupID: getGroupID},
fs: fs
});
Template:
<% if (fs.existsSync("views/docs/profile_" + data.groupID + ".ejs")) { %>
<%- include("docs/profile_" + data.groupID); %>
<% } %>
Option 2 (Synchronously): Serverside & Template
Serverside:
const fs = require('fs');
var getGroupID = 60113;
var getProfile;
if (fs.existsSync("views/docs/profile_" + getGroupID + ".ejs")) {
getProfile = true;
} else {
getProfile = false;
}
res.render('docs', {
page: setPage,
data: {groupID: getGroupID},
profile: getProfile
});
Template:
<% if (profile) { %>
<%- include("docs/profile_" + data.groupID); %>
<% } %>
Option 3 (Asynchronous I/O): Serverside & Template
Serverside:
...
var getProfile;
try {
await fs.promises.access("views/docs/profile_" + getGroupID + ".ejs");
getProfile = true;
} catch (error) {
console.log(error);
}
...

Related

errorMessage is not defined

How do I use errorMessage object from routes in a partial.I tried this
Route:-
const express = require("express");
const router = express.Router();
const Character = require("../models/character");
// All Character
router.get("/", (req, res) => {
res.render("characters/index");
});
// New Character
router.get("/new", (req, res) => {
res.render("characters/new", { character: new Character() });
});
// Creat
router.post("/", (req, res) => {
const character = new Character({
name: req.body.name,
});
character.save((err, newCharacter) => {
if (err) {
res.render("characters/new", {
character: character,
errorMessage: "Error Creating",
});
} else {
// res.redirect(`characters/${newCharacter.id}`)
res.redirect("characters");
}
});
});
module.exports = router;
layout:-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Test</title>
</head>
<body>
<%- include("../views/partials/header.ejs") %>
<%- include("../views/partials/errorMessage.ejs") %>
<br />
<%- body %>
<br />
</body>
</html>
partial :-
<%= errorMessage %>
it gives me this error:-
ReferenceError: D:\Web_Development\LGS\layouts\layout.ejs:10
8|
9| <body>
>> 10| <%- include("../views/partials/header.ejs") %> <%-
11| include("../views/partials/errorMessage.ejs") %>
12| <br />
13| <%- body %>
D:\Web_Development\LGS\views\partials\errorMessage.ejs:1
>> 1| <%= errorMessage %>
2|
errorMessage is not defined
Maybe should you try to include specifix variables :
<%- include("../views/partials/errorMessage.ejs", {errorMessage}) %>
Or just check the avaibality of your variable as it's in the main layout...
<% if (errorMessage !== undefined) { %>
<%= errorMessage %>
<% } %>
You are passing data from sever to "characters/new.ejs" this file
and now in new.ejs file you have used layouts such as header and errorMessage by using <%- include() %> statement
and to pass data from new.ejs file to this layouts you need to provide second argument to <%- include() %> statement and object of data that you want to pass
so in your example to pass errorMessage to
"../views/partials/errorMessage.ejs" you need to provide
<%- include("../views/partials/errorMessage.ejs", {errorMessage}) %>
then you can use this passed data to your layout like <%= errorMessage %>
if you want to pass more then one data you can do this
<%- include("../views/partials/errorMessage.ejs", {data1, data2, ...}) %>
try this it might be helpful it worked for me !!!!!
<% if(locals.errorMessage != null) {%>
<%= errorMessage %>
<%}%>
I had the same problem... and I solved it by using this:
<%= locals.errorMessage %>

can't fetch data from mongodb and display that into html

model named Field.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/SuperchainV1', {
useNewUrlParser: true });
mongoose.set('useNewUrlParser', true);
mongoose.set('useFindAndModify', false);
mongoose.set('useCreateIndex', true);
const db = mongoose.connection;
const FieldSchema = mongoose.Schema({
productID: {
type: String
},
productName:{
type: String
},
fieldLocation: {
type: String
},
farmerName: {
type: String
},
farmerMobile: {
type: String
},
farmerNid: {
type: String
},
date: {
type: Date,
default: Date.now
}
});
const Field = mongoose.model('Field', FieldSchema);
module.exports = Field;
routes index.js
router.get('/dashboard', ensureAuthenticated, (req, res) => {
let field = Field.find({})
.sort({date:'desc'}).exec( (err, field) => {
res.render('dashboard', field);
});
})
dashboard.ejs where i want to display data after fetching
<div class="jumbotron">
<p class="lead">
<% field.productID %>
<% field.productName %>
<% field.fieldLocation %>
<% field.farmerName %>
<% field.farmerNumber %>
<% field.farmerNid %>
</p>
</div>
errors i get "field is not defined"
I want to fetch data from collections fields and display all the data into a ejs page named dashboard i tried this but always get the error field is not defined.
You need to use for loop in ejs template
<% for(var i=0; i < field.length; i++) { %>
<div class="jumbotron">
<p class="lead">
<%= field[i].productID %>
<%= field[i].productName %>
<%= field[i].fieldLocation %>
<%= field[i].farmerName %>
<%= field[i].farmerNumber %>
<%= field[i].farmerNid %>
</p>
</div>
<% } %>

Render partial after ajax call without rails helpers (using webpack)

In the show.html.erb page I have a list of items:
<div id="shopOffersPartial">
<%= render "shops/shop_offers", offers: #offers %>
</div>
In the partial, there is simply a loop. #offers come from the backend
<% offers.each do |offer| %>
<%= render "shared/mini_offer_card/content", offer: offer, shop: #shop %>
<% end %>
I want to filter the element son every key up event. For that, I listen to an input. I have the JS logic in Webpack.
const shopFilterProductsInput = document.getElementById("shopFilterProducts");
const shopId = shopFilterProductsInput.dataset.shopid;
const shopOffersPartial = document.getElementById("shopOffersPartial");
const filterOfferes = (e) => {
let inputValue = shopFilterProductsInput.value;
const url = `/shops/${shopId}?query=${inputValue}`;
fetch(url)
.then(function() {
shopOffersPartial.innerHTML = "<%= render 'shops/shop_offers', offers: #offers %>";
})
.catch(function() {
// This is where you run code if the server returns any errors
});
}
if (shopFilterProductsInput) {
shopFilterProductsInput.addEventListener("keyup", filterOffers)
}
My question is in this part of the code:
fetch(url)
.then(function() {
shopOffersPartial.innerHTML = "<%= render 'shops/shop_offers', offers: #offers %>";
})
Once I get the response, I want to re-render the partial which has the list of items.
In rails, with .js.erb you can do things like that:
// app/views/reviews/create.js.erb
// Here you generate *JavaScript* that would be executed in the browser
function refreshForm(innerHTML) {
const newReviewForm = document.getElementById('new_review');
newReviewForm.innerHTML = innerHTML;
}
function addReview(reviewHTML) {
const reviews = document.getElementById('reviews');
reviews.insertAdjacentHTML('beforeend', reviewHTML);
}
<% if #review.errors.any? %>
refreshForm('<%= j render "reviews/form", restaurant: #restaurant, review: #review %>');
<% else %>
addReview('<%= j render "reviews/show", review: #review %>');
refreshForm('<%= j render "reviews/form", restaurant: #restaurant, review: Review.new %>');
<% end %>
But I am in a Webpack file. I can't use the Rails helpers.
How can I render then a Rails helper using Webpack?
This code
fetch(url)
.then(function() {
shopOffersPartial.innerHTML = "<%= render 'shops/shop_offers', offers: #offers %>";
})
should be replaced with this:
fetch(url)
.then(function(res) {
return res.text();
}).then(function(html) {
shopOffersPartial.innerHTML = html;
});
You don't have to use render in the JS file. The controller accessed by /shops/${shopId}?query=${inputValue} should return the needed html. Something like this:
def show
# offers = ???
# ...
respond_to do |format|
format.html { render 'shops/show' }
format.js { return plain: render_to_string("shops/shop_offers", offers: offers, layout: false) }
end
end

Trying to implement ajax drop down on a rails app

I am trying to implement a ajax dropdown on a rails app. When I select the category, the sub category drop down should populate the sub categories according to the category selected. Currently the sub category drop down is getting populated only when I select the category and enter "filter" and goes to the next page. I have the controller method as follows:
controller
def index
#categories = Category.roots.active.all
if (params.keys & ['category_id', 'sub_category_id']).present?
if params[:category_id].present?
#category = Category.active.find params[:category_id]
else
#category = Category.active.find params[:sub_category_id] if params[:sub_category_id].present?
end
end
#root_categories = Category.active.roots
#sub_categories = #category.children.active if params[:category_id].present?
#sub_categories ||= {}
#wanted_equipments = WantedEquipment.Approved.filter(params.slice(:category_id, :sub_category_id)).order("created_at desc").page(params[:page]).per(per_page_items)
end
def fetch_sub_categories
category = Category.active.where(id: params[:category_id].to_i).first
sub_categories = category.present? ? category.children.active : []
render json: sub_categories, status: 200
end
This is the js file
equipment.js
$(document).ready(function($) {
// Fetch sub-categories as per category selected
$("select#category_id, select#wanted_equipment_category_id").selectric().change(function(e){
$.getJSON("/fetch_sub_categories",{category_id: $(this).val(), ajax: 'true'}, function(response){
var options = '';
for (var i = 0; i < response.length; i++) {
options += '<option value="' + response[i].id + '">' + response[i].name + '</option>';
}
if (e.target.id=="category_id"){
$("select#sub_category").html('<option value="">Sub-Category</option>'+options);
var Selectric = $('select#sub_category').data('selectric');
Selectric.init();
}
if (e.target.id=="wanted_equipment_category_id"){
$("select#wanted_equipment_sub_category_id").html('<option value="">Select Sub-Category</option>'+options);
var Selectric = $('select#wanted_equipment_sub_category_id').data('selectric');
Selectric.init();
}
})
})
$(document).ajaxStop(function () {
$('.loader').hide();
});
$(document).ajaxStart(function () {
$('.loader').show();
});
});
This is the index.html.erb file
<%= form_tag filter_wanted_equipments_path, :method => 'get' do %>
<%= select_tag "category_id", options_from_collection_for_select(#categories, "id", "name", params[:category_id]), :prompt=>"Select Category", id: "search_category" %>
<%= select_tag "sub_category_id", options_from_collection_for_select(#sub_categories, "id", "name", params[:sub_category_id]), :prompt=>"Sub-Category" %>
<%= hidden_field_tag( 'category_id', params[:category_id]) if params[:category_id].present? %>
<%= hidden_field_tag( 'sub_category_id', params[:sub_category_id]) if params[:sub_category_id].present? %>
<%= submit_tag "filter-", :name => nil, style: "display:none;", id: 'filter-submit' %>
<% end %>
The routes.rb file
resources :wanted_equipments do
get "/fetch_sub_categories" => 'wanted_equipments#fetch_sub_categories'
collection do
get 'search'
get 'filter'
end
end
http://blog.ashwani.co.in/new/2017/03/06/How-to-use-create-ajax-dropdown-from-database-models-in-Rails.html
Code:
index.html.erb
<div>
<%= f.collection_select :vertical, #verticals, :vertical, :vertical, {:prompt => "Select a Vertical"}, {:class => "dropdown_vertical btn btn-default dropdown-toggle"} %>
<%= f.collection_select :subVertical, #subVerticals, :subVertical, :subVertical, {:prompt => "Select a Sub Vertical"}, {:class => "dropdown_subVertical btn btn-default dropdown-toggle"} %>
</div>
custom.js
$("#search_vertical").on('change', function(){ var listitems = []; $.ajax({ url: "populate_subVerticals", type: "GET", data: {vertical_name: $(this).val()}, success: function(data) { $("#search_subVertical").children().remove(); $("#search_subVertical").append('<option value=>' + "Select a Sub Vertical" + '</option>'); $.each(data,function(key, value) { listitems += '<option value="' + value.subVertical + '">' + value.subVertical + '</option>'; }); $("#search_subVertical").append(listitems); } }) });
Controller
def populate_subVerticals vertical_name = params[:vertical_name] #verticals = Vertical.where(:vertical => vertical_name).select("id","subVertical").all respond_to do |format| format.json { render json: #verticals } end end
routes.rb file
'populate_subVerticals', to: 'verticals#populate_subVerticals'
Here is a piece of code what I have used for populating dynamic drop-down.
index.html.erb --> dd1_id is the static drop-down, once dd1_id is selected, dd2_id drop-down will be populated.
<tr>
<td class="td1"></td>
<td class="td2"><span class="">Drop Down 1:</span></td>
<td><%=f.select :dd1_id, options_for_select(#abc.collect { |abc| [abc.type_name.upcase, abc.type_name.upcase] }, 0), {prompt: "-- Select --"}, { id: 'dd_1_select',:style => "width:300px"} %></td>
</tr>
<tr>
<td class="td1"></td>
<td class="td2"><span class="">Drop Down 2:</span></td>
<td><%=f.select :dd2_id, options_for_select([""]), {}, {:id => "dd_2_select",:style => "width:300px;"} %></td>
</tr>
device.js --> It has a onchange function that checks if the dd1_id drop-down is selected with a value and then triggers a function called update_dd2, that captures the dd1_id value and hits the url specified below in the ajax request.
$('#dd_1_select').on('change', function(){
if ($("#dd_1_select option:selected").val() != ""){
update_dd2();
$('#dd_2_select').val('');
}
});
function update_dd2() {
$.ajax({
url: '/update_dd_2',
data: {
dd1_value: $("#dd_1_select option:selected").val()
},
method: 'GET',
dataType: 'script',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
success: function(){
console.log("Dynamic dd_2 select OK!");
$('#dd_2_select').prop('disabled',false);
},
failure: function(error){
console.log("AJAX FAILURE: "+error);
alert("Something went wrong, Please try again later.");
},
error: function(error){
console.log("AJAX ERROR: "+error);
alert("Oops! there was an error, Please reload the page and try again.");
}
});
}
}
routes.rb --> The url specified in the ajax request would be redirected to an action
get "update_dd_2" => "abc#update_dd_2", :as => "update_dd_2"
Controller/Action --> Based on the value selected from the first drop-down, It fetches the data required to populate the second drop-down
def update_dd_2
#values = SomeTable.where("id in (?)", params[:dd1_value])
respond_to do |type|
type.js
end
end
dd1_value.js --> The above action would expect a .js file in views
This would render a partial response to the html file and the drop-down would be populated with the appropriate values
$("#dd_2_select").empty().append("<%= escape_javascript(render(:partial => #values )) %>");

Using remote: true from .js file

This is almost an identical question as was asked here: https://stackoverflow.com/questions/28571985/rails-remote-true-equivalent, but no one answered. I am hoping this doesn't mean that the question is impossible, though.
I have a page where a list of people can be dragged and dropped into bins. An assignment in a join table is automatically generated when they are dropped. I want the page to not re-load, though. In ruby, I would do this using remote: true, but the re-direct here happens in my javascript file. I believe everything is set up correctly, if only I could put remote: true at the end of my window.location.pathname line in the .js file.
z_game_sessions.js
app.gameSession = {
dragDrop: function () {
$('.groups-students').sortable({
tolerance:"pointer",
revert: true
});
$('.draggables').draggable({
connectToSortable: '.groups-students',
revert: 'invalid',
stop: function (e) {
var id = e.target.id;
var groupId = $(this).closest('ul').attr('id');
if (groupId) {
window.location.pathname = "game_sessions/add_player/" + groupId + "/" + id;
}
}
});
}
}
game_sessions_controller.rb
def add_player
#game_session = Group.find(params[:group_id]).game_session
GroupAssignment.assign(params[:group_id], params[:student_id], #game_session.id)
respond_to do |format|
format.js
end
end
add_player.js.erb
$("#new-group-form-container").html("<%= j(render partial: 'new_group_form', locals: {f: #game_session}) %>");
_new_group_form.html.erb
<%= f.simple_fields_for :groups do |ff| %>
<div class="mdl-cell mdl-cell--4-col mdl-shadow--4dp group-assignment-forms" id="group<%= "#{ff.object.id}" %>">
<h3>Group</h3>
Password: <%= ff.object.password %>
<%= ff.input :name, label: "Group Name", wrapper_html: { class: "mdl-textfield mdl-js-textfield mdl-textfield--floating-label" }, input_html: { class: "mdl-textfield__input" }, label_html: { class: "mdl-textfield__label" } %>
<%= ff.input :_destroy, as: :boolean, label: "Remove Group" %>
<h4>Students</h4>
<ul class="groups-students student-drag-area" id="<%= "#{ff.object.id}" %>">
<% ff.object.students.each do |student| %>
<li class="draggables" id="<%= student.id %>"><%= student.full_name %></li>
<% end %>
</ul>
</div>
<% end %>
First, it should be a POST request. Change your routes.
Second, make your controller return html and then just do an ajax request&assign that retuned html on success.
So instead of:
window.location.pathname = "game_sessions/add_player/" + groupId + "/" + id;
it should be something like:
$.ajax({
url: "game_sessions/add_player/" + groupId + "/" + id,
type: "POST",
success: function(data) {
$("#new-group-form-container").html(data)
}
});

Categories

Resources