Strong typing the client model in Angular - jhipster

When we have several entities with different relationships between them (eg. Event --1-> Venue --*-> Rooms) JHipster generates the following for the Java back-end, which is fine :
#Entity
public class Event implements Serializable {
#ManyToOne
private Venue venue;
}
#Entity
public class Venue implements Serializable {
#OneToMany(mappedBy = "venue")
private Set<Room> rooms = new HashSet<>();
}
#Entity
public class Room implements Serializable {
}
The equivalent model in Angular is not as strongly typed. Instead, the model uses the BaseEntity when there is a relation :
export class Event implements BaseEntity {
constructor(public venue?: BaseEntity) {}
}
export class Venue implements BaseEntity {
constructor(public rooms?: BaseEntity[]) {}
}
export class Room implements BaseEntity {
constructor( ) {}
}
With TypeScript we would highly benefit to type this code so we could navigate between objects, such as :
this.event.venue.rooms;
this.event.venue.rooms[0].name;
It would be a matter of generating the model classes without BaseEntity but the classes themselves :
export class Event implements BaseEntity {
constructor(public venue?: Venue) {}
}
export class Venue implements BaseEntity {
constructor(public rooms?: Room[]) {}
}
export class Room implements BaseEntity {
constructor( ) {}
}
WDYT ? Is there a reason why the Angular model is not as typed as the Java one ?

The original reason for introducing the BaseEntity(It was by me) was to avoid circular reference in Typescript.
The build was taking unusually long and sometimes was crashing when two entities had relationships with each other resulting in the Entity model being imported in each other for the relation. This was the quick solution I could find at that time to avoid the circular reference.
Note: This was done some time back and maybe the problem doesn't exist anymore with the latest Typescript and Webpack so probably this can be revisited.

Related

How can Implement custom repository in nestjs

I am using Nest JS v8 and Type ORM v8.1.2. I want to create custom repository such as following sample and add custom methods to it. but the version of Type ORM doesn't support this feature.
#EntityRepository(User)
export class UserRepository extends Repository<User>
customFind() {}
}
Tip: Actually i want make a generic repository.
How can i implementing it?
Thanks for helping :)
Typeorm current version doesn't support this feature, you can try this method to create your custom repository
#Injectable()
export class UserRepository {
constructor(
#InjectRepository(UserEntity)
private readonly repository: BaseRepository<UserEntity>,
) {}
get instance() {
return this.repository;
}
}
at ^0.2.45 version
#EntityRepository(UserEntity)
export class UserRepository extends BaseRepository<UserEntity> {}
note: i'm using BaseRepository in typeorm-transactional-cls-hooked
Yes that can be done. With custom repositories, the DI is not managed by Nestjs. I learned that the hard way. The trick is to use getCustomRepository.
This is how you would do it
#EntityRepository(SomeEntity)
export class SomeCustomRepository extends Repository<SomeEntity> {
customFind() {}
}
#Injectable()
export class SomeService {
async doStuff() {
const repository = getCustomRepository(
SomeCustomRepository
);
repository.doStuff()
}
}

Express + Typescript + Objection pass model to super class using generic type not working

class BaseService<Models> {
public m: Models;
constructor(model: Models) {
this.m = model;
}
}
class MyService extends BaseService<UserModel>{
constructor() {
super(UserModel); //It is require new keyword
}
}
We are using knex.js with Objection.js models.
I'm able to run queries in MyService class.
e.g.
const user = await UserModel.query().findById(1);
But instead i want a base service that to set Model using generic type.
What i need from child class id.
super(UserModel); //It is not working
If i pass UserModel using new keyword then error removed but queries not working
super(new UserModel())
Typescript Playground Link
You tried to pass class to super class, but it expected instance of that class
class BaseService<Models> {
public m: Models;
constructor(model: Models) {
this.m = model;
}
}
class UserModel {}
class MyService extends BaseService<typeof UserModel>{
constructor() {
super(UserModel); //It is require new keyword
}
}
new MyService();

How do you create a base service class using Nest.js + TypeORM?

I am working with Nest.js + TypeORM and hit a snag when trying to add inheritance to service classes.
I want to have a User service class that extends off of a Base service class, inheriting all the methods the it has.
This is what I've done:
export class BaseService<T> {
private repo;
constructor(repo: Repository<T>){
this.repo = repo;
}
async findAll(opts?): Promise<T[]> {
return this.repo.find(opts);
}
......
}
Then on my User service:
export class UserService extends BaseService<User> {
constructor(
#InjectRepository(User)
private userRepository: Repository<User>,
private readonly mailerService: MailerService,
) {
super(userRepository);
}
}
This works fine where I just need a single repository in the Service class but once I need more such as productRepository, as you can see it would fail due to constructor being hardcoded to accept a single repository.
I can't seem to figure out what would be the most elegant way of achieving something like this.
Does anyone know?
It is an old thread, anyways I meat this same problem and this answer may help someone else...
You can have an abstract class with common functions and boilerplate that you wich to be available in all your repositories to maintain patterns and so on...
export abstract class BaseService<T extends ObjectLiteral> {
protected repo: Repository<T>;
async findAll(opts?): Promise<T[]> {
return this.repo.find(opts);
}
......
}
Observe that constructor is omitted here and as an abstract class you should never have an instance of it, it is meant to be just an extension with common boilerplates and features that you don't want to write every single time you need a new service with the same features like a CRUD service or so...
Then you will use it like this:
export class UserService extends BaseService<User> {
constructor(
#InjectRepository(User) protected readonly repo: Repository<User>,
private readonly mailerService: MailerService,
) {
super();
}
...... extended stuff
}
I'm not sure if you need to inject repo with the same name and visibility but as it worked for me I didn't dive any further to get to know...
The bad side of this approach to me is that even if you don't have anything else to inject via constructor in your service you need to declare constructor and inject the base repository... it is a boilerplate that could be avoided too but I couldn't achieve this right now...

Entity enum is not exported

When generating an Entity (Configuration) containing an enumeration (Status), the Angular model looks like this :
const enum Status {
'DRAFT',
'DONE',
'ARCHIVED'
}
export class Configuration implements BaseEntity {
constructor(
public id?: number,
public json?: string,
public status?: EventConfigurationStatus,
) {
}
}
The problem is that the enum is not exported. Because I want to use the enum on its own, I add the export keyword. This is hard-coded in _entity.model.ts at line 86
Won't you think that having export by default would be better ?
Yes it makes sense.
Beside you use case, it would be also required for JHipster entity generator in order to generate compiling code if you had a Customer entity with a relationship with Configuration and using "otherEntityField": "status".

Variant Generic Interfaces

I have a generic interface, and a class implementing that interface with a concrete type parameter. I also have a generic class using the generic interface as its type constraint, but the type parameter is restricted to be a subclass of a certain base class. I want to instance the generic class with the class implementing that interface but have a problem of converting the class to that interface. The following code illustrates all the classes I mentioned:
The base class:
class DomainBase
{
}
The class used as the type parameter in the interface
class Person : DomainBase
{
}
The generic interface:
public interface IRepository<T> where T : class
{
IEnumerable<T> Fetch();
T Persist(T item);
}
The class implementing the generic interface:
class PersonRepository : IRepository<Person>
{
public IEnumerable<Person> Fetch()
{
...
}
public Person Persist(Person item)
{
...
}
}
The generic class using the generic interface:
class DomainBaseViewModel<Repository>
where Repository : IRepository<DomainBase>, new()
{
private Repository repository = new Repository();
private ObservableCollection<DomainBase> items;
}
However, the following line can't get compiled because PersonRepository is unable to be converted to IRepository<DomainBase>:
var viewModel = new DomainBaseViewModel<PersonRepository>();
Although I can solve this issue by covariance but it disallows the use of the type parameter in parameter lists:
public interface IRepository<out T> where T : class
{
...
T Persist(object item);
}
class PersonRepository : IRepository<Person>
{
public Person Persist(object item)
{
...
}
}
So I have to convert the parameter to Person, which compromises type safety.
Is there a better way to allow covariance and the use of type parameter in parameter lists in this case?
No - the whole point of the restriction on covariance is that it guarantees safety. A PersonRepository isn't an IRepository<DomainBase> because you can't ask it to persist any arbitrary DomainBase object. What would you expect this code to do?
class Product : DomainBase {}
...
IRepository<DomainBase> repository = new PersonRepository();
repository.Persist(new Product());
PersonRepository doesn't know how to persist Product values.
If in some cases you only need the "read" parts of the repository interface, you could always call that out explicitly:
public interface IRepositoryReader<out T>
{
IEnumerable<T> Fetch();
}
public interface IRepository<T> : IRepositoryReader<T>
{
T Persist(T item);
}
Then your DomainBaseViewModel class could be:
class DomainBaseViewModel<TRepository>
where TRepository : IRepositoryReader<DomainBase>, new()
{
private TRepository repository = new TRepository();
private ObservableCollection<DomainBase> items;
}
That doesn't work if you want your DomainBaseViewModel to persist items as well though. In that case, perhaps it should be generic in the type of model as well:
class DomainBaseViewModel<TRepository, TEntity>
where TRepository : IRepository<TEntity>, new()
{
private TRepository repository = new Repository();
private ObservableCollection<TEntity> items;
}
Then:
var viewModel = new DomainBaseViewModel<PersonRepository, Person>();

Resources