I am working on Sails.js CRUD application (article management) with MongoDB based on #bradtraversy tutorial. I have encountered issue while trying to display articles from the database using EJS syntax and forEach loop. Error says "articles data is not defined".
Error image
Basically I pass examplary record to Mongo via Sails /Create?title=ArticleOne%20body=BodyOne URL blueprint. The article can be clearly seen in Mongo. MongoDB article image
datastores.js is configured for Mongo:
module.exports.datastores = {
mongodb: {
adapter: 'sails-mongo',
host: 'localhost',
port: 27017,
database: 'articlebase'
},
};
routes.js points out list.ejs view:
module.exports.routes = {
'/': { view: 'pages/homepage' },
'/articles/list': { view: 'pages/list' },
'/articles/add': { view: 'pages/add' },
};
Article.js model:
module.exports = {
attributes: {
title:{
type: 'string'
},
body:{
type: 'string'
}
},
datastore: 'mongodb'
};
ArticlesController.js
module.exports = {
list:function(req, res){
Articles.find({}).exec(function(err, articles){
if(err){
res.send(500, {error: 'Database Error'});
}
res.view('list', {articles:articles});
});
},
add: function(req, res){
res.view('add');
}
};
And finally troublesome list.ejs view:
<h1 class="display-4">Articles</h1>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th></th>
</tr>
</thead>
<tbody>
<% articles.forEach(function(article){ %>
<tr>
<td><%= article.id %></td>
<td><%= article.title %></td>
<td>
Edit
<form class="d-inline" action="/articles/delete/<%= article.id %>" method="post">
<input type="submit" value="Delete" class="btn btn-danger">
</form>
</td>
</tr>
<% }); %>
</tbody>
</table>
How can I display articles from MongoDB "articles" collection with EJS correctly? Thanks for your help in advance!
I suspect your controller method is encountering an error, but you are not returning when you get the error. My first guess would be Article vs Articles but I could be wrong. Change to this to help you see what's up:
list:function(req, res){
Articles.find({}).exec(function(err, articles){
if(err){
sails.log('Error summary: ' + err);
sails.log(err);
return res.send(500, {error: 'Database Error'}); // note the return!
}
res.view('list', {articles:articles});
});
},
Otherwise, nice work on the error tracking!
So I was getting this error repeatedly, and my fix was to check the definition of "articles".
If your ejs file is accessing the model "articles" after updating the model using jquery functions, then when you render the ejs file, you can pass the updated "articles" model as a parameter.
Like,
const articles = await Article.find().sort('-entryTime');
res.render('route/ejsfilename', { articles:articles });
This part of the code can be specified in your controller or app.js file(wherever you render your ejs).
In that way when you render the ejs file, you won't get an error of "Cannot read property 'forEach' of undefined" or "articles data is not defined".
go to config/blueprints.js, set
actions: true
blueprints will do auto route for you.
Related
Hey guys I'm really new at sails.js and I'm using version 1.0 of it. I just tried to create a CRUD application on adding article to my list and showing them. All goes right but when I want to delete or edit it says 404 error. I am going through this tutorial series and as I know this series is using sails js version 0.12.
I am using mongodb as my database as a default. Data insertion is going well but problem is when I wanted to edit or delete it. Here I think the problem is getting the id value from the url. I skipped the edit portion here and posted the delete portion here.
api/controllers/ ArticleController.js
module.exports = {
delete: function(req, res){
Articles.destroy({id:req.params.id}).exec(function(err){
if(err){
res.send(500,'Database Error');
}
res.redirect('/articles/list');
});
return false;
}
};
api/models/ Articles.js
module.exports = {
attributes: {
title:{
type:'string'
},
body:{
type:'string'
}
},
};
views/pages list.ejs
<h1 class="display-4">Articles</h1>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th></th>
</tr>
</thead>
<tbody>
<% articles.forEach(function(article){ %>
<tr>
<td><%= article.id %></td>
<td><%= article.title %></td>
<td>
<form class="d-inline" action="/articles/delete/<%= article.id %>" method="POST">
<input type="submit" class="btn btn-danger" value="Delete">
</form>
</td>
</tr>
<% }) %>
</tbody>
</table>
config/route.js
module.exports.routes = {
'/': {
view: 'pages/homepage'
},
};
I have kendo grid with a button to remove the current item and for that grid I have a row template. Here is my HTML:
<div class="form-horizontal">
<table class="fixed-table-width"
data-role="grid"
data-bind="source: data"
data-scrollable="false"
data-row-template="row-template">
<thead>
<tr>
<th>Item</th>
<th></th>
</tr>
</thead>
</table>
</div>
<script type="text/x-kendo-tmpl" id="row-template">
<tr>
<td><span data-bind="text: item"></span></td>
<td><button class="link" data-bind="click: remove"><i class="icon-trash"></i> Remove</button></br ></td>
</tr>
</script>
And that is my model:
function AddItemComponent($scope) {
if ($scope === null || $scope === undefined) throw new Error("Unknown scope, please provide the scope");
var self = this;
self.itemModel = {
item: "Item to Remove",
remove: function(i) {
self.viewModel.items = self.viewModel.items.splice(i);
}
};
self.viewModel = kendo.observable({
items: []
});
self.viewModel.items.push(self.itemModel);
};
But when I open the modal with this HTML, I get the following error:
kendo.binder.min.js?cdv=40:25 Uncaught TypeError: t.get is not a function(…)
If I remove the data-bind from the click event, there is no error and it just works fine, so what is wrong?
What you are trying to achieve is better done with a commands column
http://docs.telerik.com/kendo-ui/api/javascript/ui/grid#configuration-columns.command
data-columns="[{ field: 'item' }, { command: 'destroy' }]"
You don't neeed a row template anymore.
If you don't want to do this, try to wrap your itemModel in an observable, as get() is a method from kendo.data.ObservableObject
self.itemModel = kendo.observable({
item: "Item to Remove",
remove: function(i) {
self.viewModel.items = self.viewModel.items.splice(i);
}
});
I have a collection that handles default values for forms. I need to build a UI to update the default values themselves, instead of force updating via mongo backed.
I've used aldeed's update-each functionality. The data is being fetched from the DB and displayed in the table. However, when I try to update by inputting new values in the textbox, it does not persist. In fact, it keeps throwing this error which i'm not aware of.
Exception in template helper: TypeError: Cannot read property 'namedContext' of undefined
at Object.autoFormFieldIsInvalid
As a sample, here is what I'm working with:
Mongo Collection:
meteor:PRIMARY> db.testCollection.find()
{ "_id" : ObjectId("577ccd87f57f43d790c3ec49"), "schemaName" : "test_schema", "label" : "test_lable", "value" : "test_value" }
Schema:
test_schema = new SimpleSchema({
"schemaName": {
type: String,
},
"label": {
type: String,
},
"value": {
type: String,
}
});
testCollection.attachSchema(test_schema);
Template:
<template name = "testTemplate">
<table class="table table-bordered table-condensed">
<thead>
<tr>
<td style="width: 85px">Schema Name</td>
<td style="width: 85px">Label</td>
<td>Default Value</td>
<td style="width: 250px">New Default Value</td>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td>{{this.schemaName}}</td>
<td>{{this.label}}</td>
<td>{{this.value}}</td>
<td>
{{#autoForm id=updateDefaiultsID type="update" collection=testCollection doc=this autosave=true}}
{{> afFormGroup name="value" label=false}}
{{/autoForm}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</template>
Helper
import { Template } from 'meteor/templating';
import '../templates/testTemplate.html';
if (Meteor.isServer) {
Meteor.publish(null, function () {
return testCollection.find();
});
testCollection.allow({
update: function () {
return true;
}
});
}
else if (Meteor.isClient) {
Template["testTemplate"].helpers({
items: function () {
return testCollection.find({}, {sort: {name: 1}});
},
updateDefaiultsID: function () {
return "testTemplate" + this._id;
}
});
}
Change this
from
<td>{{this.schemaName}}</td>
to
<td>{{this.schema_name}}</td>
I'm getting an error from instantsearch.js that doesn't make any sense to me. The error is:
Error: Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.
I've created a fiddle with the code in question:
https://jsfiddle.net/qkqzgsv9/
Here's the HTML:
<div type="text" id="search-box" class="form-control"></div>
<table class="table table-striped">
<thead>
<tr>
<th>Number</th>
<th>Name</th>
<th>Starts</th>
<th>Duration</th>
<th>Room</th>
<th><span class="glyphicon glyphicon-info-sign"></span></th>
</tr>
</thead>
<tbody id="hits-container">
</tbody>
</table>
<div id="pagination-container"></div>
And here's the Javascript:
var search = instantsearch({
appId: '5V0BUFDX8J',
apiKey: 'a25692c12853aea7a77c5a7125498512',
indexName: 'C86FE050-6C48-11E5-84AA-BA5F164D0BA4_events',
urlSync: { useHash: true }
});
search.addWidget(
instantsearch.widgets.searchBox({
container: '#search-box',
autofocus: true,
placeholder: 'Search for events by keyword, description, or event number.'
})
);
search.addWidget(
instantsearch.widgets.hits({
container: '#hits-container',
templates: {
empty: 'No events found',
item: '<tr><td>{{event_number}}</td><td>{{name}}</td><td>{{startdaypart_name}}</td><td>{{duration_name}}</td><td>{{room_name}}</td><td><span class="glyphicon glyphicon-info-sign" data-toggle="tooltip" title="{{description}}"></span></td></tr>'
},
})
);
search.addWidget(
instantsearch.widgets.pagination({
container: '#pagination-container'
})
);
search.start();
It happens in both Safari and Chrome. And I'm not even using the minified version of instantsearch.js. Any idea what's causing it?
This is a tricky bug and it is generated becauses this widget uses React internally.
The widget tries to render a div that will contain your template. But this is incorrect because your template contains td's which can't be rendered in div's, so the browser tries to fix that. This leads to React throwing an invariant violation error because the DOM is not what it expected. And finally you can see this specific error because React is minified in the build.
The fix would be to not use td's or wait for this issue https://github.com/algolia/instantsearch.js/issues/707 to be fixed.
I have a KendoUI dropdownlist which fetches data from a Web Service, depending on the selected item a 2nd dropdown is populated. I am using MVVM binding.
My code looks like:
<div id="ddlDiv">
<div data-container-for="MEASURE" required class="k-input k-edit-field">
<select id="MEASURE"
name="MEASURE"
data-role="dropdownlist"
data-text-field="FIELD_NAME"
data-value-field="FIELD_ID"
data-bind="value: summaryDef.MeasureID, source: fieldList"
></select>
</div>
<div data-container-for="OPERATION" required class="k-input k-edit-field">
<select id="OPERATION"
data-cascade-from: "MEASURE"
data-role="dropdownlist"
data-text-field="TYPE"
data-value-field="OP_ID"
data-source=opListDS
data-bind="value: summaryDef.OperationID"
></select>
</div>
datasetMetaModel = kendo.observable({
fieldList: datasetModel.FieldDTOList,
summaryDef: datasetModel.SummaryDef
});
kendo.bind($("#ddlDiv"), datasetMetaModel);
var opListDS = BuildOperationFields();
function BuildOperationFields() {
opListDS = new kendo.data.DataSource({
transport: {
read: {
url: '#Url.Action("GetMeasureOperations", "Wizard")',
dataType: 'json',
contentType: "application/json; charset=utf-8",
serverFiltering: true,
type: "GET"
}
}
});
return opListDS;
}
Both lists have their data populated correctly initially and are correctly bound to the model. However changing the value of the first dropdown does not cause the 2nd dropdown to have it's data refreshed. The call to the web service is never triggered.
I've seen a similar situation here that uses local data:
MVVM binding for cascading DropDownList
I don't know if this is still an issue for you as it's been a while since the question was asked but I thought I would answer as I had a similar problem myself today and managed to solve it with a workaround.
What I did was to bind a handler to the onChange event of the parent combo box and in that manually call read() on the data source of the child combo box:
e.g.
HTML:
<div id="content-wrapper">
<div class="editor-row">
<div class="editor-label"><label>Country:</label></div>
<div class="editor-field">
<select id="Country" name="Country" data-role="combobox"
data-text-field="CountryName"
data-value-field="CountryId"
data-filter="contains"
data-suggest="false"
required
data-required-msg="country required"
data-placeholder="Select country..."
data-bind="source: dataSourceCountries, value: country, events: { change: refreshCounties }">
</select>
<span class="field-validation-valid" data-valmsg-for="Country" data-valmsg-replace="true"></span>
</div>
</div>
<div class="editor-row">
<div class="editor-label"><label>County:</label></div>
<div class="editor-field">
<select id="County" name="County" data-role="combobox"
data-text-field="CountyName"
data-value-field="CountyId"
data-filter="contains"
data-auto-bind="false"
data-suggest="false"
data-placeholder="Select county..."
data-bind="source: dataSourceCounties, value: county">
</select>
<span class="field-validation-valid" data-valmsg-for="County" data-valmsg-replace="true"></span>
</div>
</div>
then my view model:
$ns.viewModel = kendo.observable({
dataSourceCountries: new kendo.data.DataSource({
transport: {
read: {
url: href('~/Api/GeographicApi/ListCountries'),
dataType: 'json'
}
},
schema: {
data: function (response) { return response.Data || {}; }
}
}),
dataSourceCounties: new kendo.data.DataSource({
transport: {
read: {
url: function () { var combobox = $('#Country').data('kendoComboBox'), id = combobox !== undefined ? combobox.value() : 0; return href('~/Api/GeographicApi/ListCountiesByCountryId/' + id) },
dataType: 'json'
}
},
schema: {
data: function (response) { return response.Data || {}; }
}
}),
refreshCounties: function (e) {
var countiesList = $('#County').data('kendoComboBox');
e.preventDefault();
countiesList.dataSource.read();
}
});
kendo.bind($('#content-wrapper'), $ns.viewModel);
Hope this helps if you have not already found a solution...