Hey guys I am currently following a tutorial and learning backbone, but for some reason I cannot get anything besides the Backbone model to work. Everything underneath it such as the collection or view does not seem to respond when entering things in the console. This is currently my code, I cannot find anything wrong with it and it validates in JSLint. One thing I noticed though is that the video is from before the 1.0 update of backbone. I am using jade for the layout and will include the code below as well.
UPDATED: I am working with this now.
(function(){
//app can be the name of the project/app
window.App = {
Models: {},
Collections: {},
Views: {},
Templates: {},
Routes: {}
};
window.template = function (id) {
return _.template($('#' + id).html());
};
//Can get rid of the Collection and views out of the names of each
//User Model
App.Models.User = Backbone.Model.extend({
defaults: {
firstName: 'J.R.',
lastName: 'Smith',
email: 'jsmith#knicks.com',
phone: '212-424-6234',
birthday: '03/05/1982',
city: 'New York'
},
location: function(){
return this.get('firstName') + ' ' + this.get('lastName') + 'is currently in ' + this.get('city') + '.';
}
});
// list of users
App.Collections.UsersCollection = Backbone.Collection.extend({
model: App.Models.User
});
//User View
App.Views.UserView = Backbone.View.extend({
tagName: 'li',
events: {
'click .edit':
},
template: template('userTemplate'),
initialize: function() {
this.render();
},
render: function() {
var template = this.template(this.model.toJSON());
this.$el.html(template);
return this;
//always return this on render methods
}
});
// view for users
App.Views.UsersView = Backbone.View.extend({
tagName: 'ul',
initialize: function() {
},
render: function() {
this.collection.each(function(user) {
//user is the model associated to the new created user
var userView = new App.Views.UserView({model: user});
this.$el.append(userView.el);
}, this);
}
});
var userView = new App.Views.UserView({model: User});
$(document.body).append(userView.render().el);
})();
Jade layout page
doctype 5
html
head
title=title
link(rel='stylesheet', href='/css/style.css', type='text/css')
link(rel='stylesheet', href='/css/bootstrap-responsive.css')
link(href='/css/bootstrap.css', rel='stylesheet', type='text/css')
link(href='/css/font-awesome.min.css', rel='stylesheet', type='text/css')
script(src='/js/jquery.min.js', type='text/javascript')
script(src='/js/jquery.validate.min.js', type='text/javascript')
script(src='/js/script.js', type='text/javascript')
script(src='/js/underscore.min.js', type='text/javascript')
script(src='/js/backbone.min.js', type='text/javascript')
body
div#container
div#header
block content
include footer
Jade index page
extends layout
block content
h1= title
p Welcome to #{title}
script(src='/js/main.js', type='text/javascript')
script(id='userTemplate', type='text/template')
<%=firstName%>
button.edit Edit
<%=lastName%>
button.edit Edit
<%=email%>
button.edit Edit
<%=phone%>
button.edit Edit
<%=birthday%>
button.edit Edit
<%=city%>
button.edit Edit
A view's render method simply fills in the view's el, someone else has to add that el to the page that people will see. You're using tagName in your view:
tagName: 'li'
That just means that Backbone will create an <li> as your view's el, it doesn't mean that that <li> will be added to anything. The usual pattern is for render to return this:
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
and then whoever is calling render can add the el to the page with something like this:
var userView = new UserView({model: user});
$(whatever).append(userView.render().el);
I do my Backbone work client-side so I'm not sure how that would fit into your set up.
I think the problem lies here:
var userView = new App.Views.UserView({model: User});
$(document.body).append(userView.render().el);
Looking at your code I would expect an instance of App.Views.UsersView to be appended, and for it to be populated - in the code above you're pushing in { model: User } to an instance of a UserView instance rather than an instance of UsersCollection to an instance of UsersView.
Firstly your UsersView.render method should return this; in order to allow use of the .render().el pattern, UsersView handles creating and attaching instances of UserView for each model in the collection so you don't need to worry about this again.
render: function() {
this.collection.each(function(user) {
//user is the model associated to the new created user
var userView = new App.Views.UserView({model: user});
this.$el.append(userView.el);
}, this);
return this;
}
Then, the following looks right to me:
var users_data=[{
firstName: 'A',
lastName: 'Person',
email: 'a#knicks.com',
phone: '212-424-6234',
birthday: '03/05/1982',
city: 'New York'
},
firstName: 'Another',
lastName: 'Person',
email: 'another#knicks.com',
phone: '212-424-6234',
birthday: '03/05/1982',
city: 'New York'
},
firstName: 'A',
lastName: 'Person',
email: 'a#knicks.com',
phone: '212-424-6234',
birthday: '03/05/1982',
city: 'New York'
}];
var users = new App.Collections.UsersCollection( users_data );
var usersView = new App.Views.UsersView( users );
$( document.body ).append( usersView.render().el );
Related
EDIT: I fixed it by adding the return-object prop to v-select
When I add a student to a database from a vuetify form, I want to be able to assign them a course. But the course has to be in a list of available courses (also in the db). I managed to do that and show all the available courses in a dropdown menu.
However, when I add the new student to the database, it sends the name of the course but not the ID of the course, so the database doesn't recognize it. I would like to link the name of the course from the v-select dropdown menu to its object ID and send the ID in the POST request.
My form component:
<v-select
:items="courses"
v-model="Courses"
item-value="name"
item-text="name"
label="Available courses"
prepend-icon="folder"
>
<template v-slot:item="{ item, attrs, on }">
<v-list-item
v-bind="attrs"
v-on="on"
>
<v-list-item-title
:id="attrs['aria-labelledby']"
v-text="item.name"
></v-list-item-title>
</v-list-item>
</template>
</v-select>
Where I store all the available courses:
computed: {
courses() {
return this.$store.state.courses;
},
The axios POST method:
methods: {
async addItem(){
const response = await axios.post("http://localhost:4000/api/student", {
name: this.name,
Courses: this.courses,
});
this.items.push(response.data);
this.name = "";
this.courses ="";
},
},
My mongoDB model:
const Student = mongoose.model(
"Student",
new mongoose.Schema({
name: String ,
Courses:
{
type: mongoose.Schema.Types.ObjectId,
ref:"id"
},
})
);
module.exports = Student;
The Course model:
const Course = mongoose.model(
"Course",
new mongoose.Schema({
name: String ,
available: {type:Boolean , default :true} ,
})
);
module.exports = Course;
Need more information on how each course object looks, and your data, but essentially, set the item-value prop to the item's object ID, and under the addItem function,
async addItem(){
const response = await axios.post("http://localhost:4000/api/student", {
id: this.courseId,
Courses: this.courses,
});
this.items.push(response.data);
this.courseId = "";
this.courses ="";
}
EDIT:
It might be a good idea to name your variables better, e.g.
// in your v-select
v-model="selectedCourse"
// in your addItem function
Course: this.selectedCourse
or
Courses: this.selectedCourses
If you just want to get id of the course in v-model of v-select, You can simply use item-value="id" instead of item-value="name".
Live Demo :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
selectedCourse: null,
courses: [{
id: 1,
name: 'Course 1'
}, {
id: 2,
name: 'Course 2'
}, {
id: 3,
name: 'Course 3'
}, {
id: 4,
name: 'Course 4'
}, {
id: 5,
name: 'Course 5'
}],
}),
methods: {
getSelected() {
console.log(this.selectedCourse) // ID of the selected course
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons"/>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-select
:items="courses"
v-model="selectedCourse"
label="Available courses"
prepend-icon="folder"
item-value="id"
item-text="name"
#change="getSelected"
></v-select>
</v-container>
</v-app>
</div>
UPDATE : Solution is at bottom of question
I have an express site using mongoose.
I'll greatly simplify to say that I have adults, kids, and house models. When I create methods on kids, I can call them from within methods on adults and get a result. I can also call them from my .ejs views. However, when I create methods on house, I can only get a result from my .ejs views and get undefined when called from within methods on adults. Example code follows.
adult.js
const mongoose = require('mongoose');
const adultSchema = mongoose.Schema({
name: { type: String },
size: {type: String},
kids: [{type: mongoose.Schema.Types.ObjectId, ref: 'Kid', required: true}]
house:{type: mongoose.Schema.Types.ObjectId, ref: 'House', required: true}
});
adultSchema.method({
getKidsDescription: function() {
if (this.kids.length < 1) {
return 'No kids yet';
} else {
let ev = 'Kids, aged: ';
let kds = this.kids;
kds.forEach(function(k){
ev = ev + 'k.getAge()' // works
})
return ev;
}
},
getHouseDescription: function(){
return 'A fabulous house on '+this.house.getFullStreet(); // does not work
}
})
module.exports = mongoose.model('Adult', adultSchema);
kid.js
const mongoose = require('mongoose');
const kidSchema = mongoose.Schema({
name: { type: String },
size: {type: String},
birthdate: {type:Date}
});
kidSchema.method({
getAge: function() {
return (Math.floor(new Date() - this.birthdate)/(1000*60*60*24*365))
},
})
module.exports = mongoose.model('Kid', kidSchema);
house.js
const mongoose = require('mongoose');
const houseSchema = mongoose.Schema({
name: { type: String },
city: {type: String},
street: {type:String}
});
houseSchema.method({
getFullStreet: function() {
return this.street + ' Road';
},
})
module.exports = mongoose.model('House', houseSchema);
When I make a query for theAdult, it looks like this:
controller.js
exports.main = async (req, res, next) => {
if (req.theAdult) {
try {
const found = await db.fetchAdult(req.theAdult._id)
res.render('/main', {
//theHouse: found.house //below I show this working
});
} catch(e) {
throw new Error(e.message)
}
} else {
res.redirect('/');
}
}
db.js
exports.fetchAdult = (id) => {
return Adult.findById(id)
.populate({ path: 'kids'})
.populate({ path: 'house'})
.exec()
.then(doc => {
return doc;
});
}
Assuming house is passed to view as an object when rendered (commented out above), this works
view.ejs
<p> <%= theHouse.getFullStreet() %></p>
Assuming house populated on the call to load the Adult, this returns undefined.
view.ejs
<p> <%= theAdult.house.getFullStreet() %></p>
At the same time, both of these work
view.ejs
<ul> <% theAdult.kids.forEach(function(k) { %>
<li><%= k.getAge() %> </li>
<% }); %>
</ul>
<p> <% theAdult.getKidsDescription() %> </p>
What I am not understanding is how the method calls work for objects in array and work in the view but do not work for objects on in an array. This is a single child error (for me). If it did not work in the view, I would assume that the method getFullStreet() was the problem, but it works in the view. If the array methods could not be called within the parent, I would assume the issue was with trying to access getFullStreet() in the parent.
What am I missing?
SOLUTION
I was fetching theAdult in my call to show view.ejs, but I was then actually relying on currentAdult which referred to req.adult and did not have the fields populated. My solution was to add a pre hook to the adult schema that always populates house on find.
in adult.js
adultSchema.pre('find', function() {
this.populate('house')
})
Have you tried passing a hydrated theAdult? It might only see the ObjectID, without any other data or methods.
I'm new to Express/Node/Mongo and trying to build an assets database application to store fixed assets.
I'm trying to save the fixed asset data through the web form but only the ID is being created for some reason and the actual data from the form is not saving.
I've looked at the data in my mongo container and can only see an ID for each asset I've created
Here is my route...
/* POST create fixed-assets page. */
router.post('/financial/assets/create-fixed-asset', secured(), function(req, res, next) {
var assetData = {
model_number: req.body.model_number,
manufacturer: req.body.manufacturer,
description: req.body.description,
serial_number: req.body.serial_number,
asset_tag_number: req.body.asset_tag_number,
condition_when_purchased: req.body.condition_when_purchased,
price_paid: req.body.price_paid
};
FixedAsset.create(assetData, function (error, asset) {
if (error) {
return next(error);
} else {
res.redirect('/financial/assets/fixed-assets');
}
});
});
Here is my list view...(using Pug/Jade)
block view
.animated.fadeIn
h1 Fixed Assets
a(href='create-fixed-asset/') Create
br
br
table#example.display
thead
tr
th ID
th Model
th Description
tbody
each asset in assets
tr
td #{asset.id}
td #{asset.model_number}
td #{asset.manufacturer}
Here is my mongoose model...
var mongoose = require('mongoose');
var FixedAssetSchema = new mongoose.Schema({
model_number: {
type: String
},
manufacturer: {
type: String
},
description: {
type: String
},
serial_number: {
type: String
},
asset_tag_number: {
type: Number
},
condition_when_purchased: {
type: String
},
price_paid: {
type: Number
}
});
var FixedAsset = mongoose.model('FixedAsset', FixedAssetSchema);
module.exports = FixedAsset;
Does anyone see why this is happening? Thanks
Edit:
Also I forgot to put the code for my Pug form. Here it is...
extends /default
block scripts
if !starter
// Plugins and scripts required by this view
script(src='/js/main.js')
block view
.animated.fadeIn
.container.row
.col-md-6
h1 #{title}
.container.row
.col-md-6
form(method='POST' action='/financial/assets/create-fixed-asset')
div.form-group
input#model_number.form-control(type='text', placeholder='Model Number')
div.form-group
input#manufacturer.form-control(type='text', placeholder='Manufacturer')
div.form-group
input#serial_number.form-control(type='text', placeholder='Serial Number')
div.form-group
input#description.form-control(type='text', placeholder='Description')
div.form-group
input#asset_tag_number.form-control(type='text', placeholder='Asset Tag Number')
div.form-group
input#condition_when_purchased.form-control(type='text', placeholder='Condition When Purchased')
div.form-group
input#price_paid.form-control(type='text', placeholder='Price Paid')
br
button.btn.btn-success(type='submit') Create
My recommendation is to use an asynchronous route and await the creation of the fixed asset:
router.post('/financial/assets/create-fixed-asset', secured(),async function(req, res, next) {
try{
var assetData = {
model_number: req.body.model_number,
manufacturer: req.body.manufacturer,
description: req.body.description,
serial_number: req.body.serial_number,
asset_tag_number: req.body.asset_tag_number,
condition_when_purchased: req.body.condition_when_purchased,
price_paid: req.body.price_paid
};
await FixedAsset.create(assetData, function (error, asset) {
if (error) {
return next(error);
} else {
res.redirect('/financial/assets/fixed-assets');
}
});
}
catch(err){
res.redirect('/somewhere else/ a 404 page')
}
});
I figured it out. It was my Pug form. I had forgot to put the name attribute in my form fields. Noob mistake. Thanks for your guys' help
I want to show email from object in node.
This is my route
// View users
router.get("/show", function (req, res, next) {
var query = {};
var options = {};
User.paginate(query, options).then(function (result) {
return res.render("../modules/users/views/userList", {
layout: 'cmsLayout',
users: result.docs,
page: parseInt(result.page),
pages: parseInt(result.pages)
});
});
});
and my handlebars
<h1>{{users}}</h1> // WORK
{ created: 2018-11-25T20:33:19.531Z, _id: 5bfb070fa18eb649e0b5ea97, firstname: 'Jxxxx', lastname: 'xxxx', email: 'xxxxx', password: 'xxxxxx', ipadress: '::1', provider: 'local', status: '1', secretToken: 'xxxxxx', __v: 0 }
<h1>{{users.email}}</h1> // WONT WORK
I know its really stupid question but i cant figure out how to show only email or only firstname...
Looks like you're using mongoose with mongoose-paginate. According to the docs, the parameter passed to paginate's callback (ie. result) should be an array. This can be confirmed by checking the return value of typeof result.docs. You might want to do this in your handlebars template:
{{#each users}}
<h1>{{this.email}}</h1>
{{/each}}
First of all, let me tell you that I'm a novice in the world of javascript and node.js. I have been searching for help in trying to do what i want but haven't found yet.
I am using the MEAN stack(http://mean.io/) and I am trying to implement a search feature in the included articles model. The search would look for articles with a specific tag and would be implemented in the index page. Follow me and see if you can find what I am missing please.
In the backend:
app/models/
/**
* Article Schema
*/
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true
},
content: {
type: String,
default: '',
trim: true
},
tag: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
app/controllers/
exports.searcharticle = function(req, res) {
Article.find({'tag': req.params.tag}).sort('-created').populate('user', 'name username').exec(function(err, articles) {
if (err) {
res.render('error', {
status: 500
});
} else {
res.jsonp(articles);
}
});
};
Added the route for the search in app/routes/articles.js
app.get('/articles/search/:tag', articles.searcharticle);
In the frontend:
Created the view for the search wich will display the search results - public/views/articles/search.html
<section data-ng-controller="ArticlesController" data-ng-init="searchart()">
<ul class="articles unstyled">
<li data-ng-repeat="article in articles">
<span>{{article.created | date:'medium'}}</span> /
<span>{{article.user.name}}</span>
<h2><a data-ng-href="#!/articles/{{article._id}}">{{article.name}}</a></h2>
<div>{{article.tag}}</div>
</li>
</ul>
<h1 data-ng-hide="!articles || articles.length">Your search hasn't returned any results. <br> Why don't you Create One?</h1>
</section>
The view for the index.html, where the searchbox will be implemented
<section data-ng-controller="ArticlesController">
<form role="form" data-ng-submit="searchart()">
<div>
<div>
<input type="text" id="tag" ng-model="selected" class="form-control" placeholder="Tag">
</div>
<div>
<button type="submit" class="btn btn-default">Submit</button>
</div>
</div>
</form>
Added the route to the config.js
when('/articles/search/:tag', {
templateUrl: 'views/articles/search.html'
}).
And added the search function to the articles controller
$scope.searchart = function() {
Articles.query(function(articles) {
$scope.articles = articles;
});
};
Right now, with this code implemented, when I click in the submit button in the index page, nothing happens.
Can you find what am I missing?
Thanks in advance!
In order to use a URL in your client Article Service, you should define the URL Parameter in the articles service at: packages/articles/public/services/article.js, like the articleId parameter already defined in there like this:
angular.module('mean.articles').factory('Articles', ['$resource',
function($resource) {
return $resource('articles/:articleId', {
articleId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
Then you need to pass it in your angular controller search method, like the function that gets one by id, like this:
$scope.findOne = function() {
Articles.get({
articleId: $stateParams.articleId
}, function(article) {
$scope.article = article;
});
};
Personally I don't know how to add another parameter to the $resource object in addition to the existing one (articleId), you may have to create another $resource service with the new parameter (:tag) and use it in your search method in your angular controller.
Another way that sounds more simple and flexible to me is to just pass the search parameters in the query method, like this:
$scope.searchart = function() {
Articles.query({tag:$scope.selectedTag}, function(articles) {
$scope.articles = articles;
});
};
and then at the server side controller, read your query parameters like this:
exports.searcharticle = function(req, res) {
Article.find(req.query).sort('-created').populate('user', 'name username').exec(function(err, articles) {
if (err) {
res.render('error', {
status: 500
});
} else {
res.jsonp(articles);
}
});
};