Cannot read property 'unshift' of undefined - arrays

I don't know where are the issue I tried to push a new book to my books array and I get this error.
This is the book-edit.component.ts
ngOnInit() {
this.authorsService.authorsChanged.subscribe(
(authors: Author[]) => {
this.authors = authors;
}
);
this.authorsService.getAuthors();
this.bookForm = new FormGroup({
'id': new FormControl(Math.floor(Math.random() * 10000000000)),
'title': new FormControl('new book'),
'author': new FormControl(null),
'description': new FormControl('description'),
'publishYear': new FormControl('1991'),
'image': new FormControl('https://www.reduceimages.com/img/image-after.jpg')
});
}
onSubmit() {
const book = {
id: this.bookForm.value.id,
title: this.bookForm.value.title,
author: this.bookForm.value.author,
description: this.bookForm.value.description,
publishYear: this.bookForm.value.publishYear,
image: this.bookForm.value.image
};
this.booksService.addBook(book);
console.log(book);
}
and this is the function I call from the booksService.ts
addBook(book: Book) {
this.books.unshift(book);
this.booksChanged.next(this.books);
}

For the error log, it seems that this.books is not initialized. Can you initialize books variable in bookservice.ts to empty array like books = []
addBook(book: Book) {
this.books.unshift(book);
this.booksChanged.next(this.books);
}

Related

Understanding ExtJs Class properties

I try to understand how properties works in ExtJs class.
Refer to below code:
Ext.define('My.sample.Person', {
name: 'Unknown',
food: undefined,
foodList: [],
constructor: function(name) {
if (name) {
this.name = name;
}
},
eat: function(foodType) {
console.log(this.name + " is eating: " + foodType);
this.food = foodType;
this.foodList.push(foodType);
//this.foodList = [foodType]
},
showFood:function() {
console.log(this.name);
console.log(this.food);
},
showFoodList:function() {
console.log(this.name);
console.log(this.foodList);
}
});
var bob = Ext.create('My.sample.Person', 'Bob');
bob.eat("Salad");
bob.showFood();
bob.showFoodList();
console.log(bob)
var alan = Ext.create('My.sample.Person', 'alan');
console.log(alan)
alan.showFood();
alan.showFoodList();
<script src="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.2.0/ext-all.js"></script>
If you check the result of "alan", the food = undefined and foodList = ['salad'] because somehow foodList was assigned to prototype.
Meanwhile, if you do like this, then it will behave normally like it should. Any idea why? What is the concept behind?
Result:

save array of objects to the mongo database using node, express

I am trying to pre-load array of objects to MongoDB as below:
the below code works if I do one object at a time. that is,
if I set:
tmp_obj = {
id:1,
name: 'Tmp 1'
}
model file
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TmpSchema = new Schema({
id: Number,
name: String
});
var Tmp= mongoose.model('Tmp', TmpSchema);
module.exports = Tmp;
routes file
var express = require('express');
var router = express.Router();
var Tmp = require('../models/tmp');
var tmp_obj = [
{
id:1,
name: 'Tmp 1'
},
{
id:2,
name: 'Tmp 2'
},
{
id:3,
name: 'Tmp 3'
}
];
var tmp = new Tmp(tmp_obj);
tmp.save(function (err) {
if (err) return console.log(err);
console.log('tmp saved to the database');
return res.redirect('/login');
})
how do I push an array of objects to the mongo? and also I have multiple collections to add. so, do I do something like:
tmp1.save(function (err) {
if (err) return console.log(err);
console.log('tmp1 saved to the database');
tmp2.save(function (err) {
if (err) return console.log(err);
console.log('tmp2 saved to the database');
return res.redirect('/login');
})
})
Another alternative is to use .create() method, it could accept an array of objects or a single object, and you don't need to create a model instance (i.e var tmp = new Tmp(tmp_obj);), here is an example:
var Tmp = require('../models/tmp');
var tmp_obj = [
{ id:1, name: 'Tmp 1' },
{ id:2, name: 'Tmp 2' },
{ id:3, name: 'Tmp 3' }
];
Tmp.create(tmp_obj, function (err, temps) {
if (err) {
console.log(err);
// terminate request/response cycle
return res.send('Error saving');
}
res.redirect('/login');
});
One last thing, don't forget to terminate the request/response cycle if an error has been occurred, otherwise the page will hangs
You can use the method insertMany from mongoose to insert multiple document at once.
From the documentation of mongoose v5.0.4
var arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
Movies.insertMany(arr, function(error, docs) {});
An alternative using .save() would be
// Create all objects
const objects = tmp_obj.map(x => new Tmp(x));
try {
// Saves objects
const docs = await Promise.all(objects.map(x => x.save()));
} catch(e) {
// An error happened
}
But you should not use it since insertMany is way better

My service is returning the function's text and not an object

I have a service to share an object in my app... I want to post that object to the mongo db but when I call the function that should return the object it gives me the function's text.
The service is here:
angular.module('comhubApp')
.service('markerService', function () {
this.markers = [];
this.newMarker = { title: '',
description: '',
lat: '',
lon: '',
user: '',
created_at: '' };
// This is supposed to return the marker object
this.newMarker = function () {
return this.newMarker;
};
this.setTitle = function (title) {
this.newMarker.title = title;
console.log('title service set: ' + title);
};
this.setDescription = function (description) {
this.newMarker.description = description;
console.log('Description service set: ' + description);
};
this.setLat = function (lat) {
this.newMarker.lat = lat;
console.log('lat service set: ' + lat);
};
this.setLon = function (lon) {
this.newMarker.lon = lon;
console.log('lon service set: ' + lon);
};
this.reset = function () {
this.newMarker = { title: '',
description: '',
lat: '',
lon: '',
user: '',
created_at: ''};
}
this.setMarkers = function (markers) {
this.markers = markers;
}
this.markers = function () {
return this.markers;
}
this.addMarker = function (marker) {
//todo append marker
}
});
newMarker returns:
this.newMarker = function () {
return this.newMarker;
};
The Controller using the service is here
$scope.addMarker = function() {
if($scope.newMarker.title === '') {
console.log('newMarker title is empty');
return;
}
markerService.setTitle($scope.newMarker.title);
markerService.setDescription($scope.newMarker.description);
console.log(markerService.newMarker());
// $http.post('/api/markers', { name: $scope.newMarker });
// $scope.newMarker = '';
};
$scope new marker is form data.. i tried to put that right into my service with no success. Instead I out the form data into the controller then push it to the service. If there is a better way to do that please let me know.
If this service is bad in any other way let me know I am new to all this and so I followed another answer I saw on here.
You are overriding your object with function. Just give them different names and it should work just fine.
this.newMarker = { ... };
this.getNewMarker = function () { return this.newMarker };
EDIT:
You should also always create new instance from marker. Otherwise you just edit the same object all the time. Here is example I made. Its not best practice but hope you get the point.
angular.module('serviceApp', [])
.factory('Marker', function () {
function Marker() {
this.title = '';
this.descrpition = '';
}
// use setters and getters if you want to make your variable private
// in this example we are not using these functions
Marker.prototype.setTitle = function (title) {
this.title = title;
};
Marker.prototype.setDescription = function (description) {
this.description = description;
};
return Marker;
})
.service('markerService', function (Marker) {
this.markers = [];
this.getNewMarker = function () {
return new Marker();
}
this.addMarker = function (marker) {
this.markers.push(marker);
}
})
.controller('ServiceCtrl', function ($scope, markerService) {
$scope.marker = markerService.getNewMarker();
$scope.addMarker = function () {
markerService.addMarker($scope.marker);
$scope.marker = markerService.getNewMarker();
}
$scope.markers = markerService.markers;
});
You could also create Marker in controller and use markerService just to store your object.
And working demo:
http://jsfiddle.net/3cvc9rrs/
So, that function is the problem. I was blindly following another example and it was wrong in my case. The solution is to remove that function and access markerService.newMarker directly.
I am still a big enough noob that I am not sure why the call was returning the function as a string. It seems to have something to do with how it is named but it is just a guess.

Populate method in mongoose virtual: nothing is being returned. [duplicate]

I have two mongoose schemas as follow:
var playerSchema = new mongoose.Schema({
name: String,
team_id: mongoose.Schema.Types.ObjectId
});
Players = mongoose.model('Players', playerSchema);
var teamSchema = new mongoose.Schema({
name: String
});
Teams = mongoose.model('Teams', teamSchema);
When I query Teams I would to get also the virtual generated squad:
Teams.find({}, function(err, teams) {
JSON.stringify(teams); /* => [{
name: 'team-1',
squad: [{ name: 'player-1' } , ...]
}, ...] */
});
but I can't get this using virtuals, because I need an async call:
teamSchema.virtual('squad').get(function() {
Players.find({ team_id: this._id }, function(err, players) {
return players;
});
}); // => undefined
What is the best way to achieve this result?
Thanks!
This is probably best handled as an instance method you add to teamSchema so that the caller can provide a callback to receive the async result:
teamSchema.methods.getSquad = function(callback) {
Players.find({ team_id: this._id }, callback);
});

How to sort collections in backboneJs

im learning BackboneJs using the documentation and a book called "Beginning backbone".
But I have been stuck at the sorting collections part for hours.
Also tried to research but I find the results complicated =/
I know I have to use the comparator, as shown in the documentation but I don't understand how to apply it to the current code syntax-wise
http://backbonejs.org/#Collection-comparator
var Book = Backbone.Model.extend({
defaults:
{
title: "default title",
author: "default author",
pages: 20
},
comparator: function(item)
{
//sort by title
return item.get('title');
}
});
var book1 = new Book({ title:"Book of wonders",author:"author1",pages:1 });
var book2 = new Book({ title:"Zelda",author:"author2",pages:2 });
var book3 = new Book({ title: "Drake's out", author: "author3",pages:3});
var book4 = new Book({ title: "AutoCad",author: "author4",pages: 4});
var Library = Backbone.Collection.extend({
model: Book
});
var library = new Library([book1,book2]);
library.add([book3,book4]);
library.forEach(function(model){
console.log('Book is called '+model.get("title"));
});
console.log('Library contains '+library.length+' books');
This is a working solution, it will sort everything by title.
to sort it by anything else just change the parameter of the get function inside the comparator function
var Book = Backbone.Model.extend({
defaults:
{
title: "default title",
author: "default author",
pages: 20
}
});
var book1 = new Book({ title:"Book of wonders",author:"author1",pages:1 });
var book2 = new Book({ title:"Zelda",author:"author2",pages:2 });
var book3 = new Book({ title: "Drake's out", author: "author3",pages:3});
var book4 = new Book({ title: "AutoCad",author: "author4",pages: 4});
var Library = Backbone.Collection.extend({
model: Book,
initialize: function()
{
console.log("new collection");
},
comparator: function(a,b)
{
//sort by title
return a.get('title') < b.get('title') ? -1 : 1;
}
});
var library = new Library([book1,book2]);
library.add([book3,book4]);
library.sort();
library.forEach(function(model){
console.log('Book is called '+model.get("title"));
});
console.log('Library contains '+library.length+' books');

Resources