After bump ember-data from 2.13 to 2.14 the request payload not contain hasMany relations when the relation model had empty relation - serialization

with ActiveModelSerializer usage after the ember-data upgrade, the values of has_many case on the request payload had changed when the model has no records by relation, for example:
// app/models/user.js
import Model from 'ember-data/model';
import { hasMany } from 'ember-data/relationships';
export default Model.extend(Validations, {
posts: hasMany('post'),
...
}
// app/models/post.js
import Model from 'ember-data/model';
import { belongsTo } from 'ember-data/relationships';
export default Model.extend(Validations, {
user: belongsTo('user'),
...
}
so, if User has no posts, previously was sent [] into BE, but after ember-data bump to 2.14.11 no key-values sent, for example: (request payload)
Before:
user: { id: 1, post_ids: [] }
After:
user: { id: 1 }
Note:
"active-model-adapter": "2.2.0"
"ember-data": "2.14.11"

It's caused by: https://github.com/emberjs/data/compare/v2.14.3...v2.14.4
workaround: app/serializers/application.js
import EmbeddedRecordsMixin from 'ember-data/serializers/embedded-records-mixin';
import { ActiveModelSerializer } from 'active-model-adapter';
import { pluralize } from 'ember-inflector';
export default ActiveModelSerializer.extend(EmbeddedRecordsMixin, {
isNewSerializerAPI: true,
serializeHasMany(snapshot, model, relation) {
this._super(...arguments);
let relationKey = `${pluralize(relation.key.underscore()).slice(0, -1)}_ids`;
if (relation.kind === 'hasMany' && !model[relationKey]) {
model[relationKey] = Object.freeze([]);
}
}
});

Related

typeorm react (expo SqlLite) native table always exist

I have a problem with SQLite used with Typeorm, i use SDK 46.
Error message: [QueryFailedError: table “temporary_media_base” already exists (code 1 SQLITE_ERROR): , while compiling: CREATE TABLE “temporary_media_base” (“id” varchar PRIMARY KEY NOT NULL, “createdAt” datetime NOT NULL DEFAULT (datetime(’ now’)), “apiId” varchar, “updatedAt” datetime NOT NULL DEFAULT (datetime(‘now’)), “deletedAt” datetime, “label” varchar, “base64” varchar NOT NULL, “type” varchar NOT NULL, “noteId” varchar, CONSTRAINT “UQ_3aa2a1f345b42d934c4a87e7b00” UNIQUE (“apiId”), CONSTRAINT “FK_01c465d2205772621fd3986d004” FOREIGN KEY (“noteId”) REFERENCES “block_note” (“id”) ON DELETE NO ACTION ON UPDATE NO ACTION)]
my entity :
import { Column, Entity, OneToMany } from “typeorm/browser”; import {
BaseEntity } from “./base.entity”; import { EmploiTempsItemEntity }
from “./emploi-temps-item”; import { SectionMatiereEntity } from
“./section-matiere.entity”;
#Entity(‘matiere’) export class MatiereEntity extends BaseEntity{
#Column() label:string
#Column() prof:string
#OneToMany(
type=>SectionMatiereEntity,
(matiere)=>matiere.matiere,
{
nullable:true,
cascade:true
} ) sectionMatiere:MatiereEntity[]
#OneToMany(
type=>EmploiTempsItemEntity,
(matiere)=>matiere.matiere,
{
nullable:true,
cascade:true
} ) emploiTemps:EmploiTempsItemEntity }
my package.json:
{ “name”: “matrive-v2”, “version”: “1.0.0”, “main”:
“node_modules/expo/AppEntry.js”, “scripts”: { “start”: “expo start”,
“android”: “expo start --android”, “ios”: “expo start --ios”, “web”:
“expo start --web”, “typeorm”: “npx typeorm-ts-node-commonjs
--dataSource dataBase/data-source.ts” }, “dependencies”: { “#babel/preset-typescript”: “^7.18.6”, “#expo/vector-icons”:
“^13.0.0”, “#react-native-community/blur”: “^4.2.0”,
“#react-native-community/datetimepicker”: “^6.2.0”,
“#react-native-community/masked-view”: “^0.1.11”,
“#react-native-community/netinfo”: “^9.3.0”,
“#react-navigation/drawer”: “^6.4.4”, “#react-navigation/native”:
“^6.0.12”, “#react-navigation/native-stack”: “^6.8.0”,
“#react-navigation/stack”: “^6.2.3”, “#shopify/flash-list”: “1.1.0”,
“babel-plugin-transform-typescript-metadata”: “^0.3.2”,
“class-transformer”: “^0.5.1”, “class-validator”: “^0.13.2”,
“date-fns”: “^2.29.3”, “expo”: “~46.0.9”, “expo-dev-client”: “~1.2.1”,
“expo-device”: “^4.3.0”, “expo-document-picker”: “^10.3.0”,
“expo-file-system”: “^14.1.0”, “expo-image-picker”: “^13.3.1”,
“expo-media-library”: “^14.2.0”, “expo-notifications”: “~0.16.1”,
“expo-splash-screen”: “^0.16.2”, “expo-sqlite”: “^10.3.0”,
“expo-status-bar”: “~1.4.0”, “expo-system-ui”: “^1.3.0”,
“expo-updates”: “~0.14.6”, “install”: “^0.13.0”, “moment”: “^2.29.4”,
“native-base”: “^3.4.13”, “npm”: “^8.19.2”, “npx”: “^10.2.2”, “react”:
“18.0.0”, “react-class-validator”: “^1.4.0”, “react-dom”: “18.0.0”,
“react-native”: “0.69.5”, “react-native-anchor-carousel”: “^4.0.1”,
“react-native-drawer”: “^2.5.1”, “react-native-dropdown-picker”:
“^5.4.2”, “react-native-fab”: “^1.0.18”, “react-native-flash-message”:
“^0.3.1”, “react-native-floating-action”: “^1.22.0”,
“react-native-gesture-handler”: “~2.5.0”,
“react-native-get-random-values”: “^1.8.0”, “react-native-modal”:
“^13.0.1”, “react-native-modalize”: “^2.1.1”,
“react-native-multi-selectbox”: “^1.5.0”,
“react-native-multi-selectbox-typescript”: “^0.1.2”,
“react-native-pager-view”: “^5.4.24”, “react-native-portalize”:
“^1.0.7”, “react-native-reanimated”: “~2.9.1”,
“react-native-safe-area-context”: “4.3.1”, “react-native-screens”:
“~3.15.0”, “react-native-svg”: “12.3.0”, “react-native-swiper”:
“^1.6.0”, “react-native-tab-view”: “^3.1.1”, “react-native-web”:
“~0.18.7”, “react-redux”: “^8.0.2”, “redux”: “^4.2.0”,
“reflect-metadata”: “^0.1.13”, “typeorm”: “^0.3.9”, “uninstall”:
“^0.0.0”, “uuid”: “^8.3.2” }, “devDependencies”: { “#babel/core”:
“^7.12.9”, “#babel/plugin-proposal-export-namespace-from”: “^7.18.9”,
“#types/node”: “^18.7.14”, “#types/react”: “~18.0.14”,
“#types/react-native”: “~0.69.1”, “ts-node”: “^10.9.1”, “typescript”:
“~4.3.5” }, “private”: true }
typeorm config:
import * as SQLite from “expo-sqlite”;
import { DataSourceOptions } from “typeorm/browser”;
import { BloackNoteEntity } from “./entities/block-note.entity”;
import { ConfigEntity } from “./entities/config.entity”;
import { EmploiTempsItemEntity } from “./entities/emploi-temps-item”;
import { EmploiTempsEntity } from “./entities/emploi-temps.entity”;
import { EventEntity } from “./entities/envent.entity”;
import { EventNotificationEntity } from
“./entities/event-notifiaction.entity”;
import { EventTypeEntity } from “./entities/event-type.entity”;
import { MatiereEntity } from “./entities/matiere.entity”;
import { MediaEntity } from “./entities/media.entity”;
import { NoteEntity } from “./entities/note.entity”;
import { SchoolYearEntity } from “./entities/school-year.entity”;
import { SectionMatiereEntity } from
“./entities/section-matiere.entity”;
import { sectionEntity } from “./entities/section.entity”;
import { TypeNoteEntity } from “./entities/type-note.entity”;
import { UserEntity } from “./entities/user.enity”;
const config:DataSourceOptions = {
database: “mydatabase”,
driver: SQLite,
entities: [ConfigEntity,TypeNoteEntity,NoteEntity,
UserEntity,BloackNoteEntity,EmploiTempsEntity,EmploiTempsItemEntity,
SectionMatiereEntity,EventEntity,EventTypeEntity,EventNotificationEntity,
MatiereEntity, sectionEntity,MediaEntity, SchoolYearEntity],
type: “expo”,
synchronize:true,
entitySkipConstructor:true
};
export default config;

How do I change the alias of a column in Typeorm?

I want to change the name of a field with my QueryBuilder in the response body i.e a field called id, I want it to output as staff_id, I am having difficulties with that.
import { getRepository, Like } from 'typeorm';
import { RoomEntity } from '#entity/room.entity';
import { HttpException } from '#exceptions/HttpException';
import { isEmpty } from '#utils/util';
import { Room } from '#/interfaces/room.interface';
class RoomService {
public room = RoomEntity;
public async findQueryRoom(): Promise<Room[]> {
const rooms = await getRepository(this.room)
.createQueryBuilder('room')
.select(['room.id as staff_id', 'room.name'])
.getMany();
return rooms;
}
}
export default RoomService;
In the controller, I have:
import { NextFunction, Request, Response } from 'express';
import { Room } from '#interfaces/room.interface';
import RoomService from '#services/room.service';
class RoomsController {
public roomService = new RoomService();
public getRoomsByQuery = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
try {
const findAllRoomsData: Room[] = await this.roomService.findQueryRoom();
res.status(200).json({ data: findAllRoomsData, message: 'found all' });
} catch (error) {
next(error);
}
};
}
export default RoomsController;
The response I have in my postman does not include the staff_id field, I see
"data": [
{
"name": "Soba"
}
]
How can I solve this problem?
I don't know if you have gotten the solution already, but for those that are facing the same problem using select column alias with TypeORM. I face the same problem some time ago and below was my solution I don't know if it's a convincing solution but it solve my problem.
public async findQueryRoom(): Promise<Room[]> {
const rooms = await getRepository(this.room)
.createQueryBuilder('room')
.select(['room.id AS staff_id', 'room.name AS name'])
.groupBy("room.id")
.getRawMany();
return rooms;
}
Why .groupBy("room.id") when .groupBy("room.id") was absent it keep on selecting each row as many as the total row for example if there are 5 rows in total it will return each row 5 times that is why I introduced .groupBy("room.id") to rid of that.
For 'room.name AS name' if you leave 'room.name' it will return room_name instead of name.

Why vuex-orm returns null in relationship field?

I followed the guide of Defining Relationships in Vuex ORM.
I did everything like the guide, so why I get null value in category field in Article Model?
codesandbox
This is how I defining my models (category on article is: hasOne/belongTo):
export class Category extends Model {
static entity = "categories";
static primaryKey = "_id";
static fields() {
return {
_id: this.attr(null),
name: this.attr("")
};
}
}
export class Article extends Model {
static entity = "articles";
static primaryKey = "_id";
static fields() {
return {
_id: this.attr(null),
name: this.attr(""),
category: this.hasOne(Category, "_id")
};
}
}
Config Vuex-orm with vuex in main.js:
import VuexORM from "#vuex-orm/core";
import { Article, Category } from "./models";
Vue.use(Vuex);
const database = new VuexORM.Database();
const Articles = {
namespaced: true,
actions: {
test() {
console.log(this);
}
}
};
const Categories = {
namespaced: true,
actions: {
test() {
console.log(this);
}
}
};
database.register(Article, Articles);
database.register(Category, Categories);
const store = new Vuex.Store({
plugins: [VuexORM.install(database)]
});
Inside app component I have the data that I insert to vuex and get the values like so:
const articleData = [
{
_id: "6ce9bae00000000000000000",
name: "article-1",
category: "5ce9acd00000000000000000"
}
];
const categoryData = [
{ _id: "5ce9acd00000000000000000", name: "category-1" }
];
const x = await Article.insertOrUpdate({ data: articleData });
const y = await Category.insertOrUpdate({ data: categoryData });
const a = Article.query()
.with("category")
.first();
console.log({ a });
console.log({ type: a.category });
console.log("why a.category is null???");
Firstly, the relationship between an article and a category is likely to be a one to many relationship however the issue with your code is the way you are defining the model and inserting data.
In your example you are attempting to provide a foreign key on the relationship target (I am an article and I know the category id I belong to). This kind of relationship is a one-to-one inverse relationship in Vuex-ORM.
In order to get your expected behavior, change your Article model as follows:
export class Article extends Model {
static entity = "articles";
static primaryKey = "_id";
static fields() {
return {
_id: this.attr(null),
name: this.attr(""),
category_id: this.attr(""),
category: this.belongsTo(Category, "category_id")
};
}
}
Note that you are explicitly defining a category_id as a separate field on the Article model rather than pointing to the id field on the Category model. This is the same pattern in all Vuex-ORM relationships. The category field stores the resolved relationship target object, so you destination category object. This is filled in by Vuex-ORM after you query using .with(Category) and should not be included in the data you insert.
Although it wouldn't make much sense, to fix your original example and use a standard one-to-one define your model as follows:
export class Article extends Model {
static entity = "articles";
static primaryKey = "_id";
static fields() {
return {
_id: this.attr(null),
name: this.attr(""),
category: this.hasOne(Category, "article_id")
};
}
}
export class Category extends Model {
static entity = "categories";
static primaryKey = "_id";
static fields() {
return {
_id: this.attr(null),
name: this.attr(""),
article_id: this.attr(null),
};
}
}
And provide an article_id in the data you pass to Article.insertOrUpdate.

ngx-chart error "TypeError: Object(...) is not a function"

I am trying to implements some statistics in my develepping platform and I try to use ngx-charts to display them. However I get an error and I can't figure out why.
I am using storedProcedures for MySQL statistics which I call from Java Restful Backend and return them in Angular 5 front-end. The returned table has the following two fields: Date and number of incidents per day. So the table returned by the backend has those two columns.
My code for the component rendering the chart is the following:
import {Component, OnInit} from '#angular/core';
import {StatisticsService} from '../../statistics.service';
class Data {
private _name: string;
private _value: number;
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
get value(): number {
return this._value;
}
set value(value: number) {
this._value = value;
}
}
#Component({
selector: 'app-daily-incidents-statistics',
templateUrl: './daily-incidents-statistics.component.html',
styleUrls: ['./daily-incidents-statistics.component.css']
})
export class DailyIncidentsStatisticsComponent implements OnInit {
view: any[] = [700, 400];
data: any[] = [];
// options
showXAxis = true;
showYAxis = true;
gradient = false;
showLegend = false;
showXAxisLabel = true;
xAxisLabel = 'Ημέρα';
showYAxisLabel = true;
yAxisLabel = 'Αρ. Περιστατικών';
constructor(private statisticsService: StatisticsService) {
// Object.assign(this, { single })
// Object.assign(this, { data } );
}
colorScheme = {
domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA']
};
onSelect(event) {
console.log(event);
}
async ngOnInit() {
console.log('NG ON INIT EXECUTION');
await this.getIncidentsByDay();
}
getIncidentsByDay() {
this.statisticsService.getIncidentsByDay()
.subscribe(
(results) => {
let temp = new Data();
for (let i in results) {
console.log(results[i][0] + '>>=====>> ' + results[i][1]);
temp.name = results[i][0];
temp.value = results[i][1];
this.data.push(temp);
}
const test = this.data;
// for (let i = 0; i < this.data.length; i++) {
// console.log('wtf: ' + this.data[i][0] + '::::' + this.data[i][1]);
// }
// console.log(results);
// console.log(JSON.stringify(results));
// Object.assign(this, {test});
}
);
}
}
However when I run the above code I get in JavaScript console the error:
ERROR TypeError: Object(...) is not a function
at BarVerticalComponent../src/common/base-chart.component.ts.BaseChartComponent.bindWindowResizeEvent (index.js:7818)
at BarVerticalComponent../src/common/base-chart.component.ts.BaseChartComponent.ngAfterViewInit (index.js:7730)
at callProviderLifecycles (core.js:12689)
at callElementProvidersLifecycles (core.js:12656)
at callLifecycleHooksChildrenFirst (core.js:12639)
at checkAndUpdateView (core.js:13794)
at callViewAction (core.js:14136)
at execComponentViewsAction (core.js:14068)
at checkAndUpdateView (core.js:13791)
at callViewAction (core.js:14136)
My Html Template File:
<div>
lalalal <br/>
ante pali... <br/>
kala ti na pw... <br/>
Gamiete pali... <br/>
<ngx-charts-bar-vertical
[view]="view"
[scheme]="colorScheme"
[results]="data"
[gradient]="gradient"
[xAxis]="showXAxis"
[yAxis]="showYAxis"
[legend]="showLegend"
[showXAxisLabel]="showXAxisLabel"
[showYAxisLabel]="showYAxisLabel"
[xAxisLabel]="xAxisLabel"
[yAxisLabel]="yAxisLabel"
(select)="onSelect($event)">
</ngx-charts-bar-vertical>
</div>
While the service for retreiving the values is:
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import {catchError} from 'rxjs/operators';
import {ErrorHandler} from '../shared/lib/error-handler';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class StatisticsService {
constructor(private http: HttpClient) {
}
public getIncidentsByDay(): Observable<any> {
console.log("FEtching Incidents All By Day");
const url = 'statistics/incidents/day';
return this.http.get(url)
.pipe(catchError(ErrorHandler.handleError));
}
}
What am I doing wrong?
I am using Angular version 5.3 and ngx-charts 8.0 which is compatible with Angular 6 and not Angular 5. I installed ngx-charts version 7.4 and everything works fine.
I fixed the problem for me by downgrading to version 7.3.0
yarn add #swimlane/ngx-charts#7.3.0
I think I see the same with ngx-charts-bar-horizontal, whereas before this was not the case. The documentation page seems to be broken at the moment as well, so I assume the software has recently been updated in a broken way.
If you really need to use the 8.0 version, you can upgrade to angular 6 to solve the problem. Here is how you can do the upgrade from v5 to v6 https://stackoverflow.com/a/49474334
You can also think that the documention page is broken by now but ou can find it here https://swimlane.gitbook.io/ngx-charts/v/docs-test/installing

ember-data-url-templates - how to use snapshot API with Ember 3

In the add-on wi-ki it is explained that we can use belongsTo to discover the relations between models:
urlSegments: {
postId: function(type, id, snapshot, query) {
return snapshot.belongsTo('post', { id: true });
},
},
but I can't find any more in Ember 3 API docs. How to do that ?
More of that, I get the error:
Uncaught TypeError: snapshot.belongsTo is not a function
at Class.shopId (shop-language.js:13)
at url-templates.js:39
at subFunction (uri-templates.js:103)
when using it in an adapter:
#adapters/shop-language.js
import ApplicationAdapter from './application';
import UrlTemplates from "ember-data-url-templates";
export default ApplicationAdapter.extend(UrlTemplates, {
findAllUrlTemplate: '/shops/{shopId}/languages',
createRecordUrlTemplate: '/shops/{shopId}/languages',
urlSegments: {
shopId: function(type, id, snapshot, query) {
return snapshot.belongsTo('shop', { id: true });
},
},
});
I figured out how to use it with description model that belongsTo a shop. Here is description.js adapter:
import ApplicationAdapter from './application';
import UrlTemplates from "ember-data-url-templates";
export default ApplicationAdapter.extend(UrlTemplates, {
urlTemplate: '{+host}/shops/{shopId}/descriptions',
findAllUrlTemplate: '{+host}/shops/{shopId}/descriptions',
createRecordUrlTemplate: '{+host}/shops/{shopId}/descriptions',
updateRecordUrlTemplate: '{+host}/shops/{shopId}/descriptions/{id}',
urlSegments: {
shopId: function(type, id, snapshot, query) {
if (query && query.shop_identifier) {
return query.shop_identifier;
}
return snapshot.belongsTo('shop').attr('identifier');
},
id: function(type, id, snapshot) {
return snapshot.id;
}
}
});
In the above example iwas using another shop attribute - identifier, but you can pass in shop's id instead.
Hope this helps.