Dynamic variable and content ejs - javascript

i have a list of projects, each have one tag :
project 1 : tag : Home
project 2 : tag : Appartement
...
And i want to be able to display only projects who includes one selected tag
If i put a tag it's working :
<% if (project.tags.includes('Appartements')) { %>
<h2 class="project-title"><%= project.title %></h2>
<%}%>
But i want to use button to change this tag :
<button onclick="changeSelectedTag('Appartements')">Appartements </button>
<button onclick="changeSelectedTag('Maisons')">Maisons </button>
<script type="text/javascript">
changeSelectedTag = function(tag) {
selectedTag = tag;
console.log(selectedTag)
}
</script>
The console log is working, i can see the selectedTag updated each time i press a button.
but i i put this code, i have 0 projects and selectedTag is empty :
<% if (project.tags.includes(selectedTag)) { %>
FullCode :
<div class="body-content">
<div class="body-tags">
<%selectedTag = 'Appartements'%>
<button onclick="changeSelectedTag('Appartements')">Appartements</button>
<button onclick="changeSelectedTag('Maisons')">Maisons</button>
</div>
<div class="projects-container">
<% projects.forEach((project) => { %>
<% if (project.tags.includes(selectedTag)) { %>
<div class="projects-content">
<h2 class="project-title"><%= project.title %></h2>
</div>
<%}%>
<% }); %>
</div>
</div>
<script type="text/javascript">
changeSelectedTag = function(tag) {
selectedTag = tag;
console.log(selectedTag)
}
</script>
RENDER in my controller
exports.list = (req, res) => Project.find((err, projects) => {
if (err) return next(err);
let selectedTag;
return res.render('../templates/projects.ejs', { projects: projects, selectedTag: selectedTag });
});
CONTENT i want to display :
<div id="here" class="items-container">
<% projects.forEach((project) => { %>
<%selectedTag = 'Appartements'%>
<% if (project.tags.includes(selectedTag)) { %>
<div class="items-content">
<img onmouseover="onProjectHover('<%=project.id%>-img', '<%=project.id%>-titles')" class="item-img" src="/<%=project.images.main.url%>" alt="<%=project.images.main.alt%>">
<div onmouseleave="onProjectLeave('<%=project.id%>-img', '<%=project.id%>-titles')" id="<%=project.id%>-img" class="item-hover-content">
<div class="item-hover-content-title">Surface : <span class="item-hover-content-value"><%=project.surface%> m2</span></div>
<div class="item-hover-content-title">Budget : <span class="item-hover-content-value"><%=project.budgect%></span></div>
<div class="item-hover-content-title">Durée : <span class="item-hover-content-value"><%=project.duration%> mois</span></div>
<button class="item-hover-content-btn">Voir le projet <i class="fas fa-arrow-right"></i></button>
</div>
<div id="<%=project.id%>-titles" class="item-titles">
<h2 class="item-title"><%= project.title %></h2>
<p class="item-city"><%= project.cities %></p>
</div>
</div>
<%}%>
<% }); %>
</div>

This ejs code works. I use pure javascript. You can also use jquery.
<div class="body-content">
<div class="body-tags">
<button id='Appartements' onclick="changeSelectedTag('Appartements',<%=JSON.stringify(projects)%>)">Appartements</button>
<button onclick="changeSelectedTag('Maisons',<%=JSON.stringify(projects)%>)">Maisons</button>
</div>
<div id='here' class="projects-container"></div>
</div>
<script type="text/javascript">
changeSelectedTag = function(tag, projects) {
selectedTag = tag;
console.log(selectedTag);
var main = document.getElementById('here');
main.innerHTML = '';
console.log(projects);
projects.forEach(el => {
if (el.tags.includes(selectedTag)) {
var div = document.createElement('div');
div.className = 'projects-content';
var h2 = document.createElement('h2');
h2.className = 'project-title';
h2.innerHTML = el.title;
div.appendChild(h2);
main.appendChild(div);
}
});
}
document.getElementById('Appartements').click();
//changeSelectedTag('Appartements',<%=JSON.stringify(projects)%>);
</script>

Related

Rails: Image preview inside a cocoon gem field

I have a simple form(product model) and I’m also using the cocoon gem for the nested fields(attachments model) that I have inside that form. When I create a product I use the cocoon gem for the nested fields in order to create attachments(images) and associate them to that product. The user can add a cocoon field and add an image to that field. I also have a preview inside each field so the user can view the image they attach to that field. But the only problem is that the preview only shows in the first cocoon field. If I try to add an image to another cocoon field the image preview doesn’t appear inside that field. I would like to be able to add a cocoon field, then add the image to that field and view the preview of that image inside that field. Any ideas on how I can change around my code in order to achieve the above?
This is the setup I have:
<%= f.simple_fields_for :attachments do |attachment| %>
<%= render 'products/attachment_fields', form: attachment %>
<% end %>
<div class="links" id="add_attachment">
<%= link_to_add_association raw('<i class="fas fa-plus"></i>'), f, :attachments, form_name: 'form' %>
</div>
attachment_fields partial :
<div class="nested-fields">
<div class="col-md-12">
<div class="image-border">
<div class="image-border-content">
<div class="parent">
<div class="image-content" id="divImageMediaPreview">
<div class="font-awesome-image"><i class="far fa-image"></i></div>
</div>
</div>
<label class="btn btn-light btn-sm" style="width: 100%;">
Add a file!
<span style="display:none;">
<%= form.input :image, label: false, as: :file, input_html: { class:"custom-file-input", id: "ImageMedias" } %>
</span>
</label>
</div>
</div>
<div class="links" style="margin-bottom: 10px;">
<%= link_to_remove_association raw('<i class="far fa-trash-alt"></i> Remove'), form, class: "btn btn-light btn-sm", :style => "width: 100%; margin-top: -15px;" %>
</div>
</div>
</div>
Image preview:
$("#ImageMedias").change(function () {
if (typeof (FileReader) != "undefined") {
let dvPreview = $("#divImageMediaPreview");
dvPreview.html("");
$($(this)[0].files).each(function () {
let file = $(this);
let reader = new FileReader();
reader.onload = function (e) {
let img = $("<img />");
img.attr("style", "width: 100%; height:auto;");
img.attr("src", e.target.result);
dvPreview.append(img);
}
reader.readAsDataURL(file[0]);
});
} else {
alert("This browser does not support HTML5 FileReader.");
}
});
Update 1
I got it to work with a minor adjustment to #nathanvda answer. But I have a small glitch. If I add one field at a time, the preview appears in each field but if I add more then one field I get the issue that is showing in the image below. Any idea how I can fix this issue ?
This is the exact code I'm using:
<div class="nested-fields">
<div class="col-md-12">
<div class="image-border">
<div class="image-border-content">
<div class="parent">
<div class="image-content image-media-preview">
<div class="font-awesome-image"><i class="far fa-image"></i></div>
</div>
</div>
<label class="btn btn-light btn-sm" style="width: 100%;">
Add a file!
<span style="display:none;">
<%= form.input :image, label: false, as: :file, input_html: { class:"custom-file-input" } %>
</span>
</label>
</div>
</div>
<div class="links" id="remove_attachment" style="margin-bottom: 10px;">
<%= link_to_remove_association raw('<i class="far fa-trash-alt"></i> Remove'), form, class: "btn btn-light btn-sm", :style => "width: 100%; margin-top: -15px;" %>
</div>
</div>
</div>
$(function() {
$('input[type=file]').change(function(){
if (typeof (FileReader) != "undefined") {
let dvPreview = $(this).parents(".image-border").find(".image-media-preview");
dvPreview.html("");
$($(this)[0].files).each(function () {
let file = $(this);
let reader = new FileReader();
reader.onload = function (e) {
let img = $("<img />");
img.attr("style", "width: 100%; height:auto;");
img.attr("src", e.target.result);
dvPreview.append(img);
}
reader.readAsDataURL(file[0]);
});
} else {
alert("This browser does not support HTML5 FileReader.");
}
})
});
You use an element with an id of #divImageMediaPreview: this means that html expect this element to be present only once on the page. You need to give the element a class, and then search the closest instance.
For instance, in your view write
<div class="nested-fields">
<div class="col-md-12">
<div class="image-border">
<div class="image-border-content">
<div class="parent">
<div class="image-content image-media-preview">
<div class="font-awesome-image"><i class="far fa-image"></i></div>
</div>
</div>
<label class="btn btn-light btn-sm" style="width: 100%;">
Add a file!
<span style="display:none;">
<%= form.input :image, label: false, as: :file, input_html: { class:"custom-file-input", id: "ImageMedias" } %>
</span>
</label>
</div>
</div>
<div class="links" style="margin-bottom: 10px;">
<%= link_to_remove_association raw('<i class="far fa-trash-alt"></i> Remove'), form, class: "btn btn-light btn-sm", :style => "width: 100%; margin-top: -15px;" %>
</div>
</div>
</div>
And then in your code do the following:
$("#ImageMedias").change(function () {
if (typeof (FileReader) != "undefined") {
let dvPreview = $(this).parents(".image-border").find(".image-media-preview");
dvPreview.html("");
$($(this)[0].files).each(function () {
let file = $(this);
let reader = new FileReader();
reader.onload = function (e) {
let img = $("<img />");
img.attr("style", "width: 100%; height:auto;");
img.attr("src", e.target.result);
dvPreview.append(img);
}
reader.readAsDataURL(file[0]);
});
} else {
alert("This browser does not support HTML5 FileReader.");
}
});
so the main change is
let dvPreview = $(this).parents(".image-border").find(".image-media-preview");
this will search a parent element up in the DOM tree with class image-border and then we descend the DOM to find your preview element. It is possible that the simpler $(this).closest('.image-media-preview') also works, but I was not sure.
Facing same problem solved by below code, where I created a div with class="upload_preview" containing image tag given id="preview_image" then on change event class="upload_image" to form field of image :-
NOTE :- I used the same form for create and edit page that's why I used if else condition for preview image
<%= f.fields_for :images, f.object.images.present? ? f.object.images : f.object.images.build do |image_form| %>
<div class="field-group">
<div class="form-group">
<label class="font-weight-bold">Image</label>
<div class="upload_preview preview_style">
<% if image_form.try(:object).try(:image).present? %>
<img id="preview_image" class="preview_img" src="<%= image_form.try(:object).try(:image_url) %>" alt="" width="40px" height="40px" /> ## image preview
<%else%>
<img id="preview_image" class="preview_img" src="/assets/default-image.png" alt="" width="40px" height="40px" /> ## image preview
<% end %>
</div>
<%= image_form.file_field :image, class: "form-control upload_image"%> ## on change event
</div>
<div class="form-group">
<%= image_form.link_to_remove "Delete image", class: "btn btn-danger btn-sm" %>
</div>
</div>
<% end %>
<div class="form-group">
<%= f.link_to_add "Add more image", :images, class: "btn btn-info btn-sm" %>
</div>
java script code
<script type="text/javascript">
$(document).ready(function(){
$(document).on('change', '.upload_image', function() {
readURL(this);
});
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$(input).siblings('.upload_preview').children('#preview_image').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
</script>

Why this is saying me 'Could not find matching close tag for "<%" '?

I have the following code.
<body>
<div class="container">
<div class="row">
<% for(let i=0;i<seats.length;i++) { %>
<div class="col-lg-6">
<h3> <%= seats[i].name %> </h3>
<% if(<%= seats[i].isBook %> == false) { %>
<% document.querySelector("h3").disabled = true; %>
<% } %>
</div>
<% } %>
</div>
</div>
I get the following error:
Error: Could not find matching close tag for "<%".
at d:\seat booking\node_modules\ejs\lib\ejs.js:710:19
at Array.forEach()
...
The Problem is your if sentence. You are opening the tag <% and before closing it you are opening another one, which doesn't work and is not needed.
You can try the following:
<div class="container">
<div class="row">
<% for(let i=0;i<seats.length;i++) { %>
<div class="col-lg-6">
<h3> <%= seats[i].name %> </h3>
<% if(seats[i].isBook == false) {
document.querySelector("h3").disabled = true;
} %>
</div>
<% } %>
</div>
</div>
As you can see, I did not just remove the tags within the if(..). The other tags I removed are also unnecessary, because you don't have any HTML code in between your if sentence.

How to show a div in JS underscore template

I am using $.getJSON in a jquery script to get data from a php file and display it in a template(underscore).
My template:
<script type="text/template" id="user-template">
<% _.each(users, function(user){%>
<div class="id"><%=user.id%> </div>
<div class="name"><%= user.name %></div>
<div class="city"><%= user.city %></div><br />
<% }); %>
</script>
My script:
$.getJSON(url, function(data){
var results = userTemplate({ users: data.users}),
$("#theresults").html(results);}
On each page im listing 10 users (or 10 results). The code works fine. I want to be able to show another div after every 4 results. Like an ad or a promotional content.
<div id="mycustomdiv">Custom DIV</div>
How do i do that?
Thanks.
Just use the second argument of the callback function to determine the current index
<% _.each(users, function(user, index){%>
<div class="id"><%=user.id%> </div>
<div class="name"><%= user.name %></div>
<div class="city"><%= user.city %></div><br />
<% if(index !== 0 && (index % 4) === 0) { %>
<div id="mycustomdiv">Custom DIV</div>
<% } %>
<% }); %>

Html is not showing correctly in Node Js

I insert data using summernote text editor. When i render this in my page than it is showing like <p>somthing</p>. And i want this as plain text.
JS Code
exports.Blog = function (req, res) {
if (req.session.Id != null) {
connection.init();
connection.query("select * from blog_posts where user_id=? order by date_created desc", [req.session.Id], function (reqest, row, fields) {
res.setHeader("Content-type", "text/html");
res.render('Blog', { rows: row });
});
}
else {
res.render('login');
}
};
ejs code
<%var result=rows%>
<%
for(var i=0;i<result.length;i++)
{
%>
<div class="well">
<div id="desc-<%=result[i].Id%>"></div>
<script type="text/javascript">
$(function () {
var desc='<%=result[i].description%>';
console.log(desc);
$('#desc-<%=result[i].Id%>').html(desc);
});
</script>
</div>
<%
}
%>
<%var result=rows%>
<%
for(var i=0;i<result.length;i++)
{
%>
<div class="well">
<div id="desc-<%-result[i].Id%>"><%-result[i].description%></div>
</div>
<%
}
%>
why are you generating script to insert html you can directly put description into div.
<%var result=rows%>
<%
for(var i=0;i<result.length;i++)
{
%>
<div class="well">
<div id="desc-<%-result[i].Id%>"><%=result[i].description%></div>
</div>
<%
}
%>

Backbone load a view within a view (as partial)

Hope someone can help me with,
I have a Backbone view, that is basically a skeleton of HTML, the HTML builds a tabbed interface, each tab obviously has a link and content area. What I am wanting to is for each content render an individual Backbone template specific to that tab.
So for example in the tab #briefs I would want to render app.projectBriefsView. I cannot for the life of figure how I would go about doing this, here is my "base/master" view that I want to load everything else into.
app.ProjectsTabsView = Backbone.View.extend({
el: "header.project",
template: _.template($("#tpl-projects-tabs").html()),
events: {
'click .js-tab-link': 'showTab',
},
initialize: function() {
this.render();
var briefView = new app.projectBriefView;
briefView.render().el;
},
render: function() {
this.$el.append(this.template());
return this;
},
showTab: function(el) {
var tabRequired = $(el.currentTarget).attr("href");
console.log(tabRequired);
$(".tab-content.active").css("display", "none").removeClass("active");
$(".tab-content"+tabRequired).addClass("active").css("display", "block");
el.preventDefault();
}
});
Template
<script type="text/template" id="tpl-projects-tabs">
<div class="tabs">
<ul>
<li>Brief + Notes</li>
<li>Dates</li>
<li>Files</li>
<li>Tasks</li>
<li>Messages</li>
<li>Comments</li>
</ul>
<div class="tab-content js-tab-content active" id="brief">Briefs</div>
<div class="tab-content js-tab-content" id="dates">Dates</div>
<div class="tab-content js-tab-content" id="files">Files</div>
<div class="tab-content js-tab-content" id="tasks">Tasks</div>
<div class="tab-content js-tab-content" id="messages">Messages</div>
<div class="tab-content js-tab-content" id="comments">Comments</div>
</div>
</script>
and here is the view I am wanting to load into the .tab-content#brief area.
app.projectBriefView = Backbone.View.extend({
el: ".tab-content",
template: _.template($("#tpl-brief-notes").html()),
events: {
},
initialize: function() {
this.render();
},
render: function() {
this.$el.append(this.template({
p: app.project.toJSON()
}));
return this;
}
});
Template
<script type="text/template" id="tpl-brief-notes">
<div class="project_info_content">
<!-- New brief form -->
<div class="brief">
<h4>Brief</h4>
<% if (p.is_owner) { %>
<div class="js-brief-text">
<% if (p.brief == undefined || p.brief == '') { %>
<p class="add-text">No Brief, Click to add</p>
<% }
} else { %>
<div>
<% }
if (p.brief != undefined || p.brief != '') { %>
<% //p.brief %>
<?= nl2br(str_replace(' ', ' ', htmlspecialchars($project['brief']))); ?>
<% } %>
</div>
<div class="inline-edit">
<% if (p.is_owner) { %>
<form action="<?= base_url(); ?>projects/edit_brief/<%= p.id %>" method="post" accept-charset="utf-8" class="inline_edit edit_brief" novalidate="novalidate">
<p>
<textarea name="brief" class="brief_edit"><%= p.brief %></textarea>
<input type="submit" name="submit" value="Update"><a class="cancel" href="#">Cancel</a>
</p>
</form>
<% } %>
</div>
</div>
<!-- End of new brief form -->
<!-- New additional notes form -->
<div class="additional_notes">
<h4>Additional notes <span class="instructions">(Editable by other users)</span></h4>
<div class="js-notes-text">
<%
if (p.additional_info == undefined || p.additional_info == '') { %>
<p class="add-text">No Notes, Click to add</p>
<% } else { %>
<% // p.additional_info %>
<?= nl2br(str_replace(' ', ' ', htmlspecialchars($project['additional_info']))); ?>
<% } %>
</div>
<div class="inline-edit">
<form action="<?= base_url(); ?>projects/edit_additional_info/<%= p.id %>" method="post" accept-charset="utf-8" class="inline_edit edit_notes" novalidate="novalidate"> <p>
<textarea name="text_details" class="notes_edit"><%= p.additional_info %></textarea>
<input type="submit" name="submit" value="Update"> <a class="cancel" href="#">Cancel</a>
</p>
</form>
</div>
</div>
</div>
<!-- End of new additional notes form -->
</div>
</script>
I thought that in the app.ProjectTabsView in the render function I would be able to do something like,
var brief = new app.projectBriefView();
brief.render().el();
to throw the output to the browser but this does not seem to work. Is there a better way to work with what I would call partial views in Backbone? How can I get my partial to the browser?
Since Backbone views can populate the browser with html dynamically, you only need to have one div element for content. This div element will then serve as a placeholder for the nested view generated by the view managing the tabs.
I had to solve a very similar problem and here is what I did:
First, define a control structure to bind a tab title to a view constructor:
var Tab = function( title, viewConstructor ) {
this.title = title;
this.viewConstructor = viewConstructor;
};
_.extend( Module.prototype, {
render : function( options ) {
this.view = new this.viewConstructor( options );
return this.view.render();
},
close : function() {
if( this.view ) {
this.view.close();
}
}
});
Then, define all your tabs in an array:
var tabs = [
new Tab('brief', app.projectBriefView),
new Tab('dates', app.projectDatesView),
//...
]
You need to clean up a little bit your template:
<script type="text/template" id="tpl-projects-tabs">
<div class="tabs">
<ul>
<li>Brief + Notes</li>
<li>Dates</li>
<li>Files</li>
<li>Tasks</li>
<li>Messages</li>
<li>Comments</li>
</ul>
<div class="tab-content js-tab-content" id="tab-content">Briefs</div>
</div>
</script>
Finally, you just have to use them in your events:
//...
showTabs : function( event ) {
event.preventDefault();
var clicked = $(el.currentTarget),
title = clicked.attr("href").replace("#", ""),
tab = _.findWhere( tabs, { title : title });
// always cleanup the preceding tab.
if ( this.activeTab ) {
this.activeTab.close();
}
this.activeTab = tab;
this.$('ul > li > .active').removeClass('active');
this.$('#tab-content').html( tab.render().el );
clicked.addClass('active');
}

Categories

Resources