henryruhs / ngx-crud Goto Github PK
View Code? Open in Web Editor NEWCRUD services in Angular with effortless aborting, caching and observing
Home Page: https://ngx-crud.com
License: Other
CRUD services in Angular with effortless aborting, caching and observing
Home Page: https://ngx-crud.com
License: Other
Not sure what is causing this, but I have trouble using @ApiUrl()
and @ApiRoute()
within a project. I assume this is a configuration issue as the decorators work for the testing.
Alternative we can use the old fashion approach:
@Injectable(
{
providedIn: 'root'
})
export class XXXService extends CrudService<unknown, unknown>
{
protected override apiUrl : string = environment.apiUrl;
protected override apiRoute : string = environment.apiRoutes.xxx;
}
Instead of having a fixed CrudService there would be the possibility to use a decorators for each HTTP operation.
export function Read<ReadResponseBody>() : Function
{
return function(service : typeof CommonService) : typeof CommonService
{
return class extends service
{
protected readService : ReadService<ReadResponseBody> = this.injector.get(ReadService);
read<ResponseBody = ReadResponseBody>(id : Id, options ?: Options) : Observable<ResponseBody>
{
return this.readService.bind(this).read(id, options);
}
};
};
}
Then this could be used over extend from CrudService:
@Injectable()
@Read()
export class ExampleService {}
While this is technical working using exampleService.read('1)
will throw an error as TypeScript does not support Class Decoration Mutation. Using (exampleService as any).read('1)
on the other hand proves the point that the class is technical extended. Another issue is passing of typings what is not doable as of writting this in 2022.
This is just for the records to my future self or others who like to suggest that feature.
Developer feedback:
Needed helpers:
Example for setting a single parameter;
public setParam(name: string, value: string): this
{
return this.setParams(
this.getParams().set(name, value)
);
return this;
}
When calling enableAbort() without any parameters, the timeout value is very low (maybe 2 seconds). It would be better to have a higher timeout value, something like 60 seconds.
The default timeout should also be configurable somehow.
As you already reported to the Angular team here (angular/angular#34959), code coverage does not work with Injector.
A possible solution would be to just not use Injector. You can instantiate AbortService and CacheService directly.
A developer experience can be improved by implementing TSDoc to each method.
Hi,
I like to avoid technical expressions when it comes to write abstractions.
That's why I thought it would be cleaner to read if Interface
-Suffix is removed from OptionInterface
.
You could either go with Option or CrudOption.
You can close this issue after reading this, since it is just my opinion and the change has no technical impact on the library itself.
Request Service should be extended to <T>
return type. So <T>
and <T[]>
are allowed to support content types such as text/plan
too.
Since the very beginning of this library, there is an flaw of service wide properties like endpoint, params, headers and other options. Once set for a specific method, it manipulates other (parallel and afterwards) requests.
Using finalize()
inside a pipe()
comes handy but does not solve falsy request being called parallel:
@Injectable()
export class JokeService extends CrudService<JokeInterface>
{
protected apiUrl : string = environment.apiUrl;
protected apiRoute : string = environment.apiRoutes.joke;
public search(query : string) : Observable<Joke[]>
{
return this
.setParam('query', query)
.request<Joke[]>('GET')
.pipe(
finalize(() => this.clearParam('query'))
);
}
}
Implement a custom clone()
method to create another instance of your service that does not touch sibling requests:
@Injectable()
export class JokeService extends CrudService<Joke>
{
protected apiUrl : string = environment.apiUrl;
protected apiRoute : string = environment.apiRoutes.joke;
public search(query : string) : Observable<Joke[]>
{
return this
.clone()
.setParam('query', query)
.request<Joke[]>('GET');
}
protected clone() : JokeService
{
return new JokeService(this.injector);
}
}
takeUntil
exampleOnce Angular 12 is released we can refactor from the X-Header
hacks to a proper HttpContext
approach.
return this.http.get(url,
{
context:
{
cache: true
}
});
Links:
https://github.com/angular/angular/blob/master/CHANGELOG.md#1200-next5-2021-03-17
https://netbasal.com/new-in-angular-v12-passing-context-to-http-interceptors-308a1ca2f3dd
It would be nice to configure enableAbort
, enableCache
and enableObserve
defaults via forRoot()
. I have to rethink about the defaults, properly 10000ms for aborting and caching and observing.
CrudModule.forRoot(
{
abort:
{
method: 'GET',
lifetime: 2000
},
cache:
{
method: 'GET',
lifetime: 2000
},
observe:
{
method: 'GET',
lifetime: 1000
}
});
AbortModule.forRoot(
{
method: 'GET',
lifetime: 2000
}
Further reading:
https://angular.io/guide/singleton-services#the-forroot-pattern
ObserveAfterEffect
and ObserverBeforeEffect
(8.1.0)ObserveEffectInterface
as deprecated (8.1.0)*Interface
and *Type
as deprecated (8.1.0)Endpoint
to ApiRoute
(8.2.0)Endpoint
and related methods as deprecated (8.2.0)In 9.0.0 all deprecations will be remove!
Support for HTTP batching should be added... there is an thread with ZIP files that include conceptional work: jonsamwell/ngx-http-batcher#2
/$batch
endpoint for testingbatch()
that joins different request in a SINGLE request and splits the response after
Currently the as T
approach is the only way to customize the models / types:
custom(): Observable<any> {
return this.request('GET') as Observable<any>;
}
There should be support to use the approach similar to http client:
custom(): Observable<any> {
return this.request<Observable<any>>('GET');
}
Example for an updated request
method:
public request<T>(method : MethodType, options? : OptionWithBodyInterface) : Observable<T | T[]>
Build Log:
2019-12-30T09:12:59.5319870Z TypeError: Cannot read property 'next' of undefined
2019-12-30T09:12:59.5321786Z at
2019-12-30T09:12:59.5322079Z at AbortService.push../node_modules/ngx-crud/fesm5/ngx-crud.js.AbortService.abort (http://localhost:9876/_karma_webpack_/node_modules/ngx-crud/fesm5/ngx-crud.js:23:1)
From Debugger:
AbortService.prototype.abort = function (request) {
this.store.get(request.url).next();
this.store.get(request.url).complete();
return this;
};
While using request('GET') we need to be able to add observe: 'response' ... therefore we have to extend the body interface.
The AbortSignal
needs to be extended by COMPLETED
as the Observable keeps open until timeout kicks in and ABORTED
is thrown.
Interceptor therefore needs this:
takeUntil(this.abortService.get(request).pipe(filter(signal => signal === 'ABORTED' || signal === 'COMPLETED')))
Just in case we need to observe only specific services:
abortService.observe(urlWithParams : string) : Observable<[string, Store]>
abortService.observeMany(url : string) : Observable<[string, Store]>
cacheService.observe(urlWithParams : string) : Observable<[string, Store]>
cacheService.observeMany(url : string) : Observable<[string, Store]>
Done for Abort and Cache since ngx-crud 11+
As TypeScript is fully transparent when it comes to types, there is no need to stick to the JSDocs anymore.
First of all, a service that extends from CrudService
without passing a config should behave like before! That being said - we need a default "fully CRUD operation" config.
operation
can be flat or come with custom typing?{
root: {
route: '/orders'
operation: {
read: (id: string) => T
find: () => T[]
}
},
invoice: {
route: '/orders/{id}/invoices'
operation: [ 'update' ]
}
}
Implement a Proxy
¹² to work like the magic __call
method from PHP to operate like a gate keeper. Is read
in the config on that route than call read()
or throw Error()
.
Non rooted API routes work like a fake property in the class.
The following API should be available according to the config:
crudService->read(id: string) : T;
crudService->find() : T[];
// it should be fine to allow "crudService->root" alias too
crudService->invoice->update(1, {}) : T;
Config()
decorator aswell¹ https://medium.com/@alonronin/magic-methods-in-javascript-meet-proxy-65e6305f4d3e
² https://dev.to/mattzgg_94/typescript-use-mapped-type-to-implement-a-proxy-4im2
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.