I would like some kind of Model for my Meteor collections. The two options I could find for this would be to use collection helpers:
Users = new Mongo.Collection('users')
Users.helpers({
fullName() {
return `${this.firstName} ${this.lastName}`
}
})
or to map the returned objects to class instances:
class UserCollection extends Mongo.Collection {
fetch() : User[] {
return super.fetch().map(user => new User(user))
}
}
let users = new UserCollection('users')
class User {
collection: Mongo.Collection
_id: string
firstName: string
lastName: string
constructor({firstName: string, lastName: string) {
this.firstName = firstName
this.lastName = lastName
this.collection = users
}
get fullname () {
return `${this.firstName} ${this.lastName}`
}
save () {
if(this._id) {
this.collection.update({ $set: { ...this }})
}
else {
this.collection.insert({...this})
}
}
}
Which variant is preferable? I like B more, but rarely found any examples using it.
Related
New to react here. I'm getting this error: xhr.js:177 POST https://localhost:44355/api/people/addperson 400 and I can't figure out why. I checked all over StackOverflow, but couldn't find a good answer from similar questions.
On my page, I have 3 textboxes (first name, last name, age) and an ADD button to add a person to a table beneath the textboxes. The error occurs when I click the add button.
Here's my controller:
public class PeopleController : ControllerBase
{
private string _connectionString;
public PeopleController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("ConStr");
}
[HttpPost]
[Route("addperson")]
public void AddPerson(Person person)
{
var repo = new PeopleRepository(_connectionString);
repo.AddPerson(person);
}
}
Here's my component:
import React from 'react';
import AddEditPerson from './AddEditPerson';
import PersonRow from './PersonRow';
import axios from 'axios';
import { produce } from 'immer';
class PeopleTable extends React.Component {
state = {
people: [],
person: {
firstName: '',
lastName: '',
age :''
},
isAdd : true
}
componentDidMount = () => {
axios.get('/api/people/getpeople').then(response => {
this.setState({ people: response.data })
})
}
onAddClick = () => {
axios.post('/api/people/addperson', this.state.person).then(() => {
axios.get('/api/people/getpeople').then(response => {
const person = {
firstName: '',
lastName: '',
age:''
}
this.setState({ people: response.data, person})
})
})
}
}
//here I have a render function that shows a component with the textboxes
//and the onClick for the add button is the onAddClick function above.
In newer versions of .Net they made a change to how the json gets parsed on the server.
It used to be that if you had a json like this: {prop: "100"}
and on the server you had a class like this:
public class Foo
{
public int Prop {get; set;}
}
it would be able to convert the json to that C# object -
(notice that in the json prop is a string and in c# it’s an int).
In .Net Core 3.1 they changed this feature, and the json would no longer parse correctly.
Therefore, being that this.state.person.age is a string but in C# Age is an integer, it would be best to create a new object, parse the age, and send that in to the function.
I updated my code:
onAddClick = () => {
const { firstName, lastName, age } = this.state.person;
const person = { firstName, lastName, age: parseInt(age) }
axios.post('/api/people/addperson', person).then(response => {
const newState = produce(this.state, draft => {
const person = {
firstName: '',
lastName: '',
age: ''
}
draft.person = person;
draft.people.push(response.data);
})
this.setState(newState);
})
}
With thanks to #BFree
and #Zied Hf
.
Im getting errors when trying to return array with custom type from getter. Here is the error message:
Type 'Expense[]' is missing the following properties from type 'Expense': name, cost, priority
And here is my code:
import Expense from '../interfaces/expence';
class User {
firstName: string;
lastName: string;
totalCost: number;
private expenses: Expense[];
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = this.lastName;
}
set userExpenses(expence: Expense) {
this.expenses = [...this.expenses, expence]
this.totalCost += expence.cost
}
get userExpenses() {
return this.expenses
}
}
export default User;
interface Expense {
name: string
cost: number;
priority: string,
}
export default Expense;
The problem here is both get and set must have the same type. In your case, set is working on single Expense object and get is returning Expense[].
Better solution would be create a append method for setter, as following code would not make sense
user.userExpenses = new Expense("1", 100, "1"); \\ it appends to expenses array
Here's what I would do
class User {
firstName: string;
lastName: string;
totalCost:number;
private expenses: Expense[] ;
constructor(firstName: string,lastName: string) {
this.firstName = firstName;
this.lastName = this.lastName;
}
set userExpenses(expences: Expense[]) { //assignment
this.expenses = [...expences];
this.expenses.forEach(e => this.totalCost += e.cost);
}
get userExpenses() {
return this.expenses
}
addExpences(expences: Expense[]) { //append
expences.forEach(e => this.totalCost += e.cost);
this.expenses = [...this.expenses, ...expences];
}
}
I have a http request that gets this Json object from a nosql database:
let jsonBody = {
birthday : 1997,
firstname: 'foo',
lastname:'bar'
}
Then I want to load this information into the Student model:
class Student{
constructor(){
}
getFullname(){
return this.lastname+' '+this.firstname
}
getApproxAge(){
return 2018- this.birthday
}
}
Normally, I would add this method to this class:
fromJson(json){
this.studentId = json.studentId;
this.birthday = json.birthday;
this.firstname = json.firstname;
this.lastname = json.lastname;
}
I would use it as follow:
let student = new Student()
student.fromJson(jsonBody)
console.log(student.getFullname())
console.log(student.getApproxAge())
This works fine but my problem is I have: 100 proprieties in reality. Will I have to write all proprities one by one in the fromJson method?
And also, if a propriety name has change, let's say: lastname became LastName, I will have to fix it?
Is there a simpler way to just assign these values to the object student dynamically but keep all of its methods??
Something like this:
fromJson(json){
this = Object.assign(this, json) //THIS IS NOT WORKING
}
Just assign to an instance:
static from(json){
return Object.assign(new Student(), json);
}
So you can do:
const student = Student.from({ name: "whatever" });
Or make it an instance method and leave away the assignemnt:
applyData(json) {
Object.assign(this, json);
}
So you can:
const student = new Student;
student.applyData({ name: "whatever" });
It could also be part of the constructor:
constructor(options = {}) {
Object.assign(this, options);
}
Then you could do:
const student = new Student({ name: "whatever" });
And also, if a property name has changed, let's say: lastname became LastName, I will have to fix it?
Yes you will have to fix that.
There is no way in javascript to deserialize json into classes. So I wrote a library ts-serializable that solves this problem.
import { jsonProperty, Serializable } from "ts-serializable";
export class User extends Serializable {
#jsonProperty(String)
public firstName: string = ''; // default value necessarily
#jsonProperty(String, void 0)
public lastName?: string = void 0; // default value necessarily
#jsonProperty(Date)
public birthdate: Date = new Date(); // default value necessarily
public getFullName(): string {
return [
this.firstName,
this.lastName
].join(' ');
}
public getAge(): number {
return new Date().getFullYear() - this.birthdate.getFullYear();
}
}
const user: User = new User().fromJSON(json);
user.getFullName(); // work fine and return string
user.getAge(); // work fine and return number
// or
const user: User = User.fromJSON(json);
user.getFullName(); // work fine and return string
user.getAge(); // work fine and return number
The library also checks types during deserialization.
I'm trying to use ES6 Deconstructing inside a class constructor but get an unknown token error. Here's an example:
// imports/server/a-and-b.js
class A {
constructor(id) {
// make MongoDB call and store inside this variable
let {
firstName: this._FirstName // => Throws here
} = CollectionName.findOne({userId: id});
}
}
export class B extends A {
constructor(id) {
super(id);
}
get FirstName() {
return this._FirstName;
}
}
// imports/server/test.js
import { B } from 'imports/server/a-and-b.js'
const b = new B('123')
const FirstName = b.FirstName;
The same deconstruction will work outside the class:
// another-test.js
// make MongoDB call and store inside this variable
let {
firstName: FirstName // works fine
} = CollectionName.findOne({userId: id});
Your syntax is incorrect. What you are trying to do is not possible. Assuming the findOne method is synchronous you need to do this:
constructor(id) {
// make MongoDB call and store inside this variable
let { firstName } = CollectionName.findOne({userId: id});
this._FirstName = firstName;
}
I found this can be done like so:
constructor(id) {
// make MongoDB call and store inside this variable
({ firstName: this._FirstName } = CollectionName.findOne({userId: id}));
}
I'm fighting hard with relations inside my bookshelf model. What I'm trying to do is to create schema which contains 4 tables:
users
roles
privileges
privileges_roles
With relations between them like this:
users manyTOone roles manyTOone privileges_roles oneTOmany privileges
I've easily achieved relations between privileges and roles with:
Privileges
class Privileges {
constructor() {
this.model = bookshelf.Model.extend({
tableName: 'privileges',
roles: function () {
return this.belongsToMany(Roles.model).through(PrivilegeRole.model);
}
});
};
}
Roles
class Roles {
constructor() {
this.model = bookshelf.Model.extend({
tableName: 'roles',
privileges: function() {
return this.belongsToMany(Privileges.model).through(PrivilegeRole.model);
},
users: function() {
return this.hasMany(Users.model);
}
});
};
}
PrivilegeRole
class PrivilegeRole {
constructor() {
this.model = bookshelf.Model.extend({
tableName: 'privileges_roles',
role: function() {
return this.belongsTo(Roles.model);
},
privileges: function() {
return this.belongsTo(Privileges.model);
}
});
};
}
Those works really fine. Unfortunately when I'm trying to fetch Privileges from User model it keep inserting id instead of role_id to query.
class Users {
constructor() {
this.model = bookshelf.Model.extend({
tableName: 'users',
role: function () {
return this.belongsTo(Role.model);
},
privileges: function () {
// return this.belongsToMany(Privileges.model).through(PrivilegeRole.model);
return this.belongsToMany(Privileges.model, 'privileges_roles', 'role_id', 'privilege_id', 'role_id');
}
});
};
}
So at the end whatever I do, bookshelf is creating query like this:
select privileges.*, privileges_roles.id as _pivot_id,
privileges_roles.role_id as _pivot_role_id,
privileges_roles.privilege_id as _pivot_privilege_id from
privileges inner join privileges_roles on
privileges_roles.privilege_id = privileges.id where
privileges_roles.role_id in (1)
Instead of role_id in (3) like it's in a record fetched.
Alright so I've finally found a solution. Instead of previously used:
privileges: function () {
return this.belongsToMany(Privileges.model, 'privileges_roles', 'role_id', 'privilege_id', 'role_id');
}
I had to simply use:
privileges: function () {
return this.belongsToMany(Privileges.model).through(PrivilegeRole.model, 'role_id', 'privilege_id', 'role_id');
}