TypeORM deletes record on entity change - sqlite

I'm using NestJs + TypeORM with synchronise enabled.
I have 2 entities:
item
import { Entity } from 'typeorm';
import { BaseEntityUUID } from '../../common/entities/base-string.entity';
#Entity('items')
export class ItemEntity extends BaseEntityUUID {
}
and inventory
import { Column, Entity, ManyToOne } from 'typeorm';
import { BaseEntity } from '../../common/entities/base.entity';
import { ItemEntity } from '../../items/entities/item.entity';
import { GameUserEntity } from './game-user.entity';
#Entity('inventory-items')
export class InventoryItemEntity extends BaseEntity {
#Column()
gameUserId: string;
#Column()
itemId: string;
#ManyToOne(() => ItemEntity, {onUpdate: 'CASCADE', onDelete: 'CASCADE'})
item?: ItemEntity;
#Column()
count: number;
#ManyToOne(() => GameUserEntity, user => user.id, {onUpdate: 'CASCADE', onDelete: 'CASCADE'})
gameUser?: GameUserEntity;
}
I need to add new column to Item, so i modify ItemEntity like that:
import { Column, Entity } from 'typeorm';
import { BaseEntityUUID } from '../../common/entities/base-string.entity';
#Entity('items')
export class ItemEntity extends BaseEntityUUID {
#Column({ default: 0 })
levelRequired: number;
}
, but all records from Inventory are deleted after that. All records from Items are updated with levelRequired: 0, but why all related records from Inventory are erased?

Related

mikro-orm Cascade.REMOVE, is it only for RDBMS?

I am using mikro-orm with MongoDB and trying to do a Cascade.REMOVE but i can not get it work.
Business entity:
#Entity({ collection: "business" })
export class BusinessModel implements BusinessEntity {
#PrimaryKey()
public _id!: ObjectId;
#Property()
public name!: string;
#Property()
public description!: string;
#OneToMany({ entity: () => LocationModel, fk: "business", cascade: [Cascade.ALL] })
public locations: Collection<LocationModel> = new Collection(this);
}
export interface BusinessModel extends IEntity<string> { }
Location entity:
#Entity({ collection: "location" })
export class LocationModel implements LocationEntity {
#PrimaryKey()
public _id!: ObjectId;
#Property()
public geometry!: GeometryEmbedded;
#ManyToOne({ entity: () => BusinessModel})
public business!: BusinessModel;
public distance?: number;
}
export interface LocationModel extends IEntity<string> { }
Business Data:
_id: 5cac818273243d1439866227
name: "Prueba"
description: "Prueba eliminacion"
Location Data:
_id: 5cac9807179e380df8e43b6c
geometry: Object
business: 5cac818273243d1439866227
_id: 5cacc941c55fbb0854f86939
geometry: Object
business: 5cac818273243d1439866227
And the code:
export default class BusinessData {
private businessRepository: EntityRepository<BusinessModel>;
public constructor(#inject(OrmClient) ormClient: OrmClient) {
this.businessRepository = ormClient.em.getRepository(BusinessModel);
}
public async delete(id: string): Promise<number> {
return await this.businessRepository.remove(id);
}
}
The "business" is correctly removed but all related "locations" continue there.
The log only show:
[query-logger] db.getCollection("business").deleteMany() [took 0 ms]
Cascades work on application level, so for all drivers, including mongo.
The problem here is that you are removing the Business entity by id. You need to remove it by reference - provide the entity and be sure you have the collection populated, otherwise ORM does not know what entities to cascade remove.
Try it like this:
export default class BusinessData {
public async delete(id: string): Promise<number> {
const business = await this.businessRepository.findOne(id, ['locations'])
return await this.businessRepository.remove(business);
}
}
Your approach with removing by id would work only if the entity was already loaded in identity map (aka managed by EM), including the locations collection.

Property 'map' does not exist on type '{}' rxjs6

Please view the following code:
import { Component } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection } from 'angularfire2/firestore';
import { Observable} from 'rxjs';
import { map } from 'rxjs/operators';
import { StudentsInfo } from '../studentInfo';
#Component({
selector: 'app-delete-student',
templateUrl: './delete-student.component.html',
styleUrls: ['./delete-student.component.css']
})
export class DeleteStudentComponent {
public itemsCollection: AngularFirestoreCollection<StudentsInfo>;
public items: Observable<StudentsInfo[]>;
constructor(private db: AngularFirestore) {
this.itemsCollection = this.db.collection<StudentsInfo>('/Stud_Info');
//this.items = this.itemsCollection.valueChanges();
// .snapshotChanges() returns a DocumentChangeAction[], which contains
// a lot of information about "what happened" with each change. If you want to
// get the data and the id use the map operator.
this.items = this.itemsCollection.snapshotChanges().pipe(
map(changes => changes.map(a => {
const data = a.payload.doc.data() as StudentsInfo;
const id = a.payload.doc.id;
return { id, ...data}
})
));
}
// Deleting a Student from Firestore
public deleteStudent(docId:string) {
this.itemsCollection.doc(docId).delete();
}
}
Problem: Property 'map' does not exist on type '{}'.
I have followed this documentation of angularfire2.
I have done everything according to the documentation needed and the according to the latest rxjs6 release.
The final angular application Runs fine but while doing ng build --prod --aot this error occurs.

retrieve data from firebase in ionic 3 and angular 5

Pls I need help in retrieving user data from firebase with AngularFireObject on logging in.
I have been able to save data to firebase but having issues retrieving it. Pls someone help out.
Many thanks in advance.
Ok, first you've to configure your AngularFireModule (I think you already do that). AngularFireModule.initializeApp(FIREBASE_CONFIG).
So, is a good way to create a model/service to handle your entities requests with firebase, something like this:
Model:
export interface Cutom {
key?: string,
name: string,
quantity: number,
price: number
}
Service:
import { Injectable } from '#angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { Custom } from './../../models/custom.model';
#Injectable()
export class CustomService {
private customListRef;
constructor(private db: AngularFireDatabase) {
this.customListRef = this.db.list<Custom>('your-endpoint-at-firebase');
}
getCustomList() {
return this.customListRef;
}
}
In Component you will use your recently created service:
...
export class HomePage implements OnInit {
// remember to import the correct model
customList$: Observable<Custom[]>
constructor(
public navCtrl: NavController,
private customService: CustomListService
) {}
ngOnInit() {
this.customList$ = this.customService
.getCustomList()
.valueChanges();
}
or if you need the metadata too (like the ID):
ngOnInit() {
this.customList$ = this.customService
.getCustomList()
.snapshotChanges()
.pipe(
map(items => { // this needs to be imported with: import { map } from 'rxjs/operators';
return items.map(a => {
const data = a.payload.val();
const key = a.payload.key;
return {key, ...data};
});
}));
}
...
And the finally at your template:
<ion-item *ngFor="let item of customList$ | async">
{{item.name}}
</ion-item>
I hope it helps.

Ionic2 load rows from sqlite after database ready

I have an ionic 2 app with a database service powered by sqlite. The database stores a couple rows of items. The first page of the app displays those items. The issue I'm running into is that the page attempts to load the items before the service has loaded the database. I get this error:
Cannot read property 'executeSql' of undefined
If i navigate to a different page and back to home, it loads correctly. This is the database service. I'm attempting to call getScanables on the first page.
import { Injectable } from '#angular/core';
import {SQLite} from 'ionic-native';
import {Platform} from 'ionic-angular';
#Injectable()
export class Database {
private storage: SQLite;
public isOpen: boolean;
public constructor(private platform: Platform,) {
console.log('Creating storage.');
platform.ready().then(() => {
console.log('Platform Ready.');
if(!this.isOpen) {
console.log('Database Unopened');
this.storage = new SQLite();
this.storage.openDatabase({name: "data.db", location: "default"}).then(() => {
console.log('Generating Database');
this.storage.executeSql("CREATE TABLE IF NOT EXISTS scanables (id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT, type TEXT, name TEXT, date TEXT)", []);
//this.storage.executeSql("CREATE TABLE IF NOT EXISTS journal (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT, descriptors TEXT, image TEXT, behaviors TEXT)", []);
console.log('Generated.');
this.isOpen = true;
}, (error) => {
console.log("Error opening database.", error);
});
}
});
}
public getScanables() {
console.log('Getting Scanables');
return new Promise((resolve, reject) => {
this.storage.executeSql("SELECT * FROM scanables", []).then((data) => {
let journal = [];
if(data.rows.length > 0) {
for(let i = 0; i < data.rows.length; i++) {
journal.push({
id: data.rows.item(i).id,
value: data.rows.item(i).value,
type: data.rows.item(i).type,
name: data.rows.item(i).name,
date: data.rows.item(i).date,
letter_one: data.rows.item(i).name.substring(0,1)
});
}
}
resolve(journal);
}, (error) => {
reject(error);
});
});
}
...
}
This is the relevant code for the first page:
#Component({
templateUrl: 'journal.html'
})
export class JournalPage implements AfterViewInit {
public orderType: string = 'delivery';
public autocomplete: {term: string, place: any};
public regions: any;
public itemList: Array<Object>;
public addList: Array<number>;
constructor(private navCtrl: NavController,
private menu: MenuController,
private modalCtrl: ModalController,
private database: Database,
public userService: UserService,
private helper: Helper,
private l: LoggerService) {
this.itemList = [];
this.addList = [1,2,3,4,5,6,7];
}
ngAfterViewInit(): any {
console.log('journal.ngAfterViewInit');
this.load();
}
public load() {
console.log('journal.load');
this.database.getScanables().then((result) => {
console.log('entered getScanables');
this.itemList = <Array<Object>> result;
console.log('itemList', this.itemList);
}, (error) => {
this.l.error('journal.ts:', error);
});
}
...
The solution for this issue was to make the service's database a public variable and load the database on load. Then only after loading the database we set the root page. This forces the database to load first. May not be the ideal solution though, so feel free to post additional answers.

Ionic 2: Unable to create table TypeError: Cannot read property 'apply' of undefined

I am trying to open a database and create a table in my Ionic 2 app.
The following method is part of a service and is supposed to open the db and create the table:
initDb() {
let db = new SQLite();
db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
db.executeSql("CREATE TABLE IF NOT EXISTS people (avatarUrl VARCHAR, firstName VARCHAR, lastName VARCHAR)", []).then((data) => {
console.log("Table created: ", data);
}, (error) => {
console.error("Unable to create table", error);
})
}, (error) => {
console.error("Unable to open database", error);
});
}
The method is called in my home page's constructor:
constructor(public platform: Platform, public navCtrl: NavController, public dbService: DBService) {
this.platform.ready().then(() => {
this.dbService.initDb();
});
}
I have no idea why I am getting this error (refer to the title).
Thanks
Sorry, I could not reproduce this error but build a testapp on my own. This app works with me, despite this is called within ready as well:
app.component.ts:
import { Component } from '#angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar, Splashscreen, SQLite } from 'ionic-native';
import { TabsPage } from '../pages/tabs/tabs';
import { DbService } from '../providers/db-service';
#Component({
templateUrl: 'app.html',
providers: [DbService]
})
export class MyApp {
rootPage = TabsPage;
constructor(public platform: Platform, public dbService: DbService) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
Splashscreen.hide();
this.dbService.initDb();
});
}
}
I made this service by using this ionic-command:
ionic g provider DbService
db-service.ts:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
import { SQLite } from 'ionic-native';
/*
Generated class for the DbService provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class DbService {
constructor(public http: Http) {
console.log('Hello DbService Provider');
}
initDb() {
let db = new SQLite();
db.openDatabase({
name: "data.db",
location: "default"
}).then(() => {
db.executeSql("CREATE TABLE IF NOT EXISTS people (avatarUrl VARCHAR, firstName VARCHAR, lastName VARCHAR)", []).then((data) => {
console.log("Table created: ", data);
}, (error) => {
console.error("Unable to create table", error);
})
}, (error) => {
console.error("Unable to open database", error);
});
}
}
ionic-version: 2.1.18
cordova-version 6.0.0
Hope it helps.

Resources