I really cannot find a way to describe this. Essentially I have a database that can have any number of tables with different column lengths and names. I have a table which defines each table and it's columns. I have a query to search for values within these tables, and I pass the results of the search and the columns to ejs using express. What I need is to echo the results. I do have:
<div class="row">
<table>
<thead>
<tr>
<% columns.forEach(function(column) { %>
<th><%= column %></th>
<% }); %>
</tr>
</thead>
...
This outputs the name of the columns in the table header correctly. I cannot for the life of me figure out how to print the actual results however. I have tried many different ways but all I keep getting is undefined or [Object object]. I have this currently:
<tbody>
<% for(var r = 0; r < results.length; r++) { %>
<tr>
... need to access column here ...
</tr>
<% } %>
</tbody>
I first tried the following (inside above)
<% for(var key in Object.keys(results[r])) { %>
<%= results[r].key %>
<% } %>
followed by so many different attempts along the lines of this. I suppose the issue is not knowing the possible key names. I don't even know what to search for either to be entirely honest. My mind is drawing a blank.
Any help is much appreciated.
Object.keys returns an array of all the keys in an object. You can use forEach to iterate over those keys, to access the value of the object via object[key]
Also, you can use forEach on your results instead of for to keep things clean.
<% results.forEach(function (result) { %>
<tr>
<% Object.keys(result).forEach(function (key) { %>
<td><%= result[key] %> </td>
<% }) %>
</tr>
<% }) %>
Modern browsers have arrows now, so this can be simplified a little:
<% results.forEach(result => { %>
<tr>
<% Object.keys(result).forEach(key => { %>
<td><%= result[key] %> </td>
<% }) %>
</tr>
<% }) %>
Related
I'm looping over an array on the front end using ejs and displaying the data in a bootstrap 5 table. The table is dynamic so rows will be added and deleted with time.
All the data is coming through without an error and the table is populating, however, I'd like to have the first column show the "count" for each row. Eg., "1, 2, 3, 4, 5, etc".
I've tried using indexOf without any success and since the code already exists in a loop, creating another loop requires me to switch my ejs syntax and I lose the ability to count the length of the array.
Below is my client side code that yields value -1 down the entire # column for each row:
<div class="col-12 d-flex flex-row-reverse">
Export to csv
</div>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Email</th>
<th scope="col">Gender</th>
<th scope="col">Age</th>
<th scope="col">Clicked IG</th>
<th scope="col">Date Added</th>
<th scope="col">Remove</th>
</tr>
</thead>
<tbody>
<% for (let fan of artist.fans){%>
<tr>
<th scope="row"><%= artist.fans.indexOf(fan.id) %></th>
<td><%= fan.email %></td>
<td><%= fan.gender %></td>
<td><%= fan.age %></td>
<td><%= fan.subscribed %></td>
<td><%= fan.dateAdded %></td>
<td style="padding-left: 2rem;">
<button onclick="removeRow()" ></button>
</td>
<td style="padding-left: 2rem;"><i class="bi bi-trash"></i></td>
<% } %>
</tr>
</tbody>
</table>
</div>
If I switch artist.fans.indexOf(fan.id) with just fan.id I get the corresponding objectId for each fan email object.
If I switch artist.fans.indexOf(fan.id) with artist.fans.length I get 7 down the # column for each row.
Here's my db model:
const artistSchema = new Schema({
image: [ImageSchema],
genre: [ String ],
fans: [{
email: String,
subscribed: String,
gender: String,
age: Number
dateAdded: {
type: Date,
default: Date.now
}
}],
});
How do I get each row to be numbered?
the problem is that you used fan.id as a search param in artist.fans.indexOf(fan.id) which is not directly accessible throw the artist.fans so instead you need to use another method that accepts comparing function so you can compare their id
try to use
<th scope="row"><%= artist.fans.findIndex(f=> f.id == fan.id) %></th>
TL;DR:
Just do this:
<% for( const fan of artist.fans.map( ( e, index ) => ({ index, ...e }) ) ) { %>
<tr>
<th scope="row"><%= fan.index %></th>
<td><%= fan.email %></td>
<!-- etc -->
</tr>
<% }%>
Explanation:
I'd like to have the first column show the "count" for each row. Eg., "1, 2, 3, 4, 5, etc".
That's not a "count". I would refer to that as "the row-number" (if 1-based) or "row-index" (if 0-based).
If you're using SQL to get your data then you can just use ROW_NUMBER() in your query and map it to some new number property in your Fan type.
Note that a ROW_NUMBER() value is kinda meaningless without a well-defined ORDER BY criteria.
Don't use indexOf in a loop: indexOf has a runtime complexity of O(n) so it's inadvisable to use that function inside a loop over the same array as that will give you O(n^2) runtime, which is very bad.
This also applies to other O(n) reduction functions like findIndex, find, includes, and lastIndexOf.
Also, indexOf (and others) only defined on Array.prototype, so it isn't available on other kinds of iterables, such as NodeList.
Within ejs, you can use an overload of Array.prototype.map which gives you the index of each element, and stick the index into each element object, like so:
const fansWithIndex = artist.fans
.map( function( element, index ) {
element.index = index;
return element;
} );
<% for( const fan of fansWithIndex ) { %>
<tr>
<th scope="row"><%= fan.index %></th>
<td><%= fan.email %></td>
<!-- etc -->
</tr>
<% }%>
...though FP purists (like myself) would argue that the above example is bad code because it mutates source data, and instead Object.assign should be used instead. Also, the long-form function can be made simpler with an arrow-function =>, like this:
const fansWithIndex = artist.fans.map( ( e, idx ) => Object.assign( { index: idx }, e ) );
<% for( const fan of fansWithIndex ) { %>
<tr>
<th scope="row"><%= fan.index %></th>
<td><%= fan.email %></td>
<!-- etc -->
</tr>
<% }%>
This can be simplified further:
As an alternative to Object.assign the object spread operator ...obj can be used:
{ index: idx, ...e } is almost semantically identical to Object.assign( { index: idx }, e ).
The difference is that Object.assign will invoke custom setter functions, whereas the ... syntax does not.
Caution: when immediately returning an object-literal from within map you will need to wrap the object-literal's braces {} in parentheses () to prevent the braces being parsed as function body delimiters, hence the => ({ foo: bar }) instead of => { foo: bar }.
The index: idx can be simplified by renaming the idx parameter to index and putting just { index, ...e }.
Like so:
const fansWithIndex = artist.fans.map( ( e, index ) => ({ index, ...e }) );
<% for( const fan of fansWithIndex ) { %>
<tr>
<th scope="row"><%= fan.index %></th>
<td><%= fan.email %></td>
<!-- etc -->
</tr>
<% }%>
Because the artist.fans.map(...) part is a single expression with a single output you can now inline it directly into your for(of) statement, like so:
<% for( const fan of artist.fans.map( ( e, index ) => ({ index, ...e }) ) ) { %>
<tr>
<th scope="row"><%= fan.index %></th>
<td><%= fan.email %></td>
<!-- etc -->
</tr>
<% }%>
I'm trying to pass an object to my view using JSON.stringify but I am not sure how to access elements in the object. Here is how I pass the object to my view:
const getTracking = (request, response) => {
pool.query('SELECT * FROM tracking', (error, results) => {
if (error) {
throw error
}
var trackingObj = JSON.stringify(results.rows);
response.render('pages/trackingInfo', {
trackingObj: trackingObj
});
})
}
Then in my index.ejs document I can access my object like so:
<p><%= trackingObj %></p>
which results in the following output in the browser (data is grabbed from postgres database):
[{"wo_num":1,"completion_date":"2021-08-04T04:00:00.000Z","material":"test","description":"test","qty":1,"total_hours":null},
{"wo_num":2,"completion_date":"2021-08-05T04:00:00.000Z","material":"test2","description":"test2","qty":2,"total_hours":2},
Is there a way to access the elements of this JSON.stringify object individually so I could do something like display them in a table?
JSON.parse('<%- JSON.stringify(trackingObj) %>')
if you want to just see the content you can also use (will be displayed on the terminal
<% console.log(trackingObj) %>
for a better view :
<% console.table(trackingObj) %>
As #Jayesh commented, the solution was to access the object using indices like so: trackingObj[0].material
To then display the elements in a table you can iterate through them in the following manner:
<% trackingObj.forEach(function(obj) { %>
<tr>
<td> <%= obj.material %> </td>
<td> <%= obj.wo_num %> </td>
<tr>
<% } %>
I am using will_paginate on the results from two instance variables, each using the same param[:query] on two different PgSearch.multisearch methods. In the views, when clicking on one of the pagination links, both the tables were being updated. Is there any way to get around this problem? By passing only one param from the form? (As my search form has only one text field). I have searched and gone through the similar questions, most of them suggesting to use two params., but none of them gave me any thoughts to solve this prob using one param. :/
Code in the form:
<%= form_tag("/search", :method => "get", :remote => true) do %>
<%= label_tag(:query, "Search for: ") %>
<%= text_field_tag(:query, params[:query]) %>
<%= submit_tag("Search", class: "btn btn-primary") %>
<% end %>
Controller code:
def index
#employee = Employee.find(session[:user_id])
#search_employees = PgSearch.multisearch(params[:query]).where(:searchable_type => "Employee").paginate(page: params[:page], :per_page => 5)
#search_customers = PgSearch.multisearch(params[:query]).where(:searchable_type => "Customer").paginate(page: params[:page], :per_page => 5)
respond_to do |f|
f.html
f.js
end
end
Code in the View:
<% if !#search_employees.empty? %>
<h2>Employees</h2>
<table class="table table-hover">
.
.
<% #search_employees.each_with_index do |doc, index| %>
<% user = doc.searchable %>
<tr id="employee-<%= user.id %>">
.
.
<% end %>
</tbody>
</table>
<% end %>
<%= will_paginate #search_employees %>
<% if !#search_customers.empty? %>
<h2>Customers</h2>
<table>
.
.
</table>
<% end %>
<%= will_paginate #search_customers %>
Can I send two different params with the same text field value from the form? if so., please let me know how. Any ideas or suggestions would be much appreciated. :)
Thanks in Advance. :)
Pagination depends on the page parameter, but not on the query parameter from the search form, therefore you don't need to change anything in your form.
To update only corresponding table you need to customize page parameter in at least one of the tables. For example change page parameter for customers:
<%= will_paginate #search_customers, :param_name => 'customers_page' %>
and in controller:
#search_customers = PgSearch.multisearch(params[:query])
.where(:searchable_type => "Customer")
.paginate(:page => params[:customers_page], :per_page => 5)
This should resolve the issue
Guys I've search for a solution for this problem with no luck, wish if someone could help.
I'm building nodejs express app, in one of .ejs I'm trying to compare mongoDB Id of the selected item with the id passed in the query string in order to change the selected item accordingly.
Below is the code:
<table class="table table-striped">
<tr>
<th>Available Models</th>
</tr>
<script>
<% var index = 0; %>
</script>
<% if(modelId) { %>
<script>
for(i =0; i <= document.getElementById("mnumake").options.length;i++) {
var optionValue = document.getElementById("mnumake").options[i].value;
if (<% modelId.equals(%> optionValue <% ) %> ) {
index = i;
document.getElementById("mnumake").options[i].selected = true;
break
}
}
</script>
<% } %>
<% makes[index].models.forEach(function(model){ %>
<tr><td><%= model.name %></td></tr>
<% }) %>
</table>
I am developping a simple web application with sails.js.
In my frontend code, i'm looping over an array of objects sent by the controller, and i am searching for a specific object. I'd like to break the loop once the object has been found. Is there a clean way to do that using the ejs template language ?
I coulnd't find anything about it on the EJS official website, nor on sails website.
I naively tried this until now :
<% objects.forEach(function(object) { %>
<% if(object.someId == someValue) {%>
<%= object.name %>
<% break %> <!-- This raise an illegal statement exception -->
<% } %>
<% } %>
The following code works as expected, but i'm looking for a cleaner solution if any
<% var found = false %>
<% objects.forEach(function(object) { %>
<% if(object.someId == someValue && !found) {%>
<%= object.name %>
<% found = true %>
<% } %>
<% } %>
It's not an EJS-related question.
break statement terminates the current loop. While you are not inside a loop. You are inside callback function, that's called by forEach method once per array element. There's no ability to interrupt forEach (see How to short circuit Array.forEach like calling break?). But, if you need it, you may use for loop for a clean solution:
<% for (var l = objects.length, i = 0; i < l; i++) { %>
<% object = objects[i]%>
<% if(object.someId == someValue) {%>
<%= object.name %>
<% break %> <!-- This will work as expected -->
<% } %>
<% } %>