abdulhaq-e / ngrx-json-api Goto Github PK
View Code? Open in Web Editor NEWA JSON API client for ngrx
License: MIT License
A JSON API client for ngrx
License: MIT License
Currently there is:
isCreating: boolean;
isReading: boolean;
isUpdating: boolean;
isDeleting: boolean;
isCommitting: boolean;
which is not able to cope with concurrent read/inserts/...
Currently it makes use of:
let contentType = _.get(response, 'headers.Content-Type');
in the past it made used of:
contentType = response.headers.get("Content-Type");
maybe the presence of the header should be checked in the old one.
All the selectXy service methods make use of getResultIdentifiers. This one should check for any errors and throw an error accordingly.
to chat, ask questions, etc.
Currently we have
export interface FilteringParam {
field?: string;
value?: any;
type?: string;
path?: string;
api?: string;
}
we may reduce that to:
export interface FilteringParam {
path?: string;
value?: any;
operator?: string;
}
which translates to
?filter[path][operator]=value
ResourceDefinition should hold an additional "attribute" mapping variable which allows to translate "path" to
?filter[api][operator]=value
should the variable names in Javascript not match of what is used as filter URL parameter.
Add meta and links attributes to the Document and Resource interfaces
The use of queries fills up the store if resources that are so far not removed. This leads to a memory leak after time for bigger applications with plenty of resources on the server-side.
A gargabe collection algorithm should be implemented. It may looks like:
e.g. QueryRefreshAction currently refreshes any query from the server regardless whether it was originally a in-memory or remote query. There should be a check for that. Potentially more actions need to be checked.
QueryType is defined as:
export type QueryType
= 'getOne'
| 'getMany'
| 'update'
| 'deleteOne'
| 'create';
otherwise it is quite hard to get started...
e.g. NgrxJsonApi make a local copy of NgrxJsonApiConfig.apiUrl. It should make direct use of NgrxJsonApiConfig to allow to reconfigure or lazily configure ngrx-json-api.
It would be nice to have test coverage.
Current ngrx-json-api cannot be used in applications that make use of AoT (like the ones based on the current version of angular-cli).
Problem is the definition of the reducers that need to change to:
export function NgrxJsonApiStoreReducer(state: NgrxJsonApiStore = initialNgrxJsonApiState, action: Action)
isntead of the current lamda constant.
NgrxJsonApiServiceV2 has a number of select methods returning Resource and ResourceIdentifier object. There should be an operator that allows to denormalize their contents. Some like observable.denormalizeResource(...) or observable.let(xy.denormalizeResource).
In general we should expect that depending on the use case, service consumers either want to work with denormalized or normalized resources. For example editors likely stick with normalized resources. While tables may prefer denormalized ones.
Rather than having service.findOne(query, true, true)
, it is better for this to be:
service.findOne({
query: query,
fromServer: true,
denormalise: true
}
This applies to all other methods. The reason behind this is to make the public API easier to extend. If new options are added in the future, they can easily go in the object rather than a new argument (i.e. this will look very ugly in the future: service.fineMany(query, true, true, false, '10', true)
Hi,
#124 fixed a problem with isUpdating, however, now I have a new problem (using v1.1.0):
On API_POST_INIT
, isUpdating
goes from 0 to 1, and on API_POST_SUCCESS
, isCreating
goes -1. So my loading indicator never disappears just by checking whether the status flags are positive.
currently the queries and selectors do not like it that much if one of the resources gets deleted. The store should check the queries upon a API_DELETE_SUCCESS and if a query is found, the resultIds should be cleared and a refresh from the server triggered.
it sets the queryId of the query if that is not already done. This can break the consumer in varierty of ways. Potentially the input query is supposed to be immutable.
A copy should be created instead and then the queryId assigned.
Hi,
I'm using the latest Angular CLI with Angular 4 final. Could you please fix this:
$ npm shrinkwrap --dev 2>&1 | grep ngrx-json-api
npm ERR! peer invalid: @angular/[email protected], required by [email protected]
npm ERR! peer invalid: @angular/[email protected], required by [email protected]
npm ERR! peer invalid: @angular/[email protected], required by [email protected]
I know that won't solve all problems, as @ngrx/effects
and @ngrx/router-store
are failing too, but it's 1 step forward.
removeQuery is executed once a subscription expires and the finally block is invoked (see services.ts findInternal). This causes problems when doing repeated queries with a specified queryId because usually a selectMany is executed (updating the store), then an observable is returned to some kind of chain (typically making use of switchMap), and then the old observable gets unsubscribed and clears the new query.
currently array values of attributes and relationships in the action and store are merged when the patch action is executed. The patch action should replace the existing values instead.
currently only filtering and includes are supported
currently it checks whether the payload and store StoreResoure are equal (including all the StoreResource fields like errors and state). It hsould only check for equality of Resource attributes. This prevents that a resource is marked as 'UPDATED' even tough nothing changed.
e.g. OperationType is defined as:
= 'CREATING'
| 'UPDATING'
| 'DELETING'
| 'READING'
potentially it should rather be GET, POST, PATCH, DELETE to align with the JSON API / HTTP spec.
to do automatic memory management.
Potentially the compact operation should be optimized towards that and may terminate quickly by checking first in the area the query was operating (type, results,...)
Next to CREATED, IN_SYNC, etc. there should be a state NEW. This would also user interfaces to introduce new resources where the user also later can decide or not whether to actually create them. One such use case is for mutable tables having a "new resource" row at the end.
both methods return StoreResource resp. an Array of it directly. They should rather return something like:
interface class QueryResult{
resources : Array<StoredResource>,
links : any,
meta : any
}
at least for findMany it seems quite important as paging information has to be transmitted as well.
e.g. POST_STORE_RESOURCE should not update the store if there is no change.
the sorting of pending changes has a minor Javascript error leading to an error when updating more than one resource.
Linting will make PRs cleaner and all contributors will code to a set standard.
getOneQueryResult$ and getManyQueryResult$ should directly do denormalization with a single selection of the store.
Currently denormalization is a second step in the selection making use of "combineLatest". Unfortunately, that "combineLatest" leads to multiple emits and intermediate are not properly denormalized. For example, when query results become available, the selection will trigger an update of QueryResult, but combineLatest did not yet get a chance to fetch the newest store contents. That only happens immediately after.
The issue will not happen if it gets moved. It further makes the selection simpler as now combination is necessary.
I have a form that lets the user edit an entity and save the changes in the end, and implemented it like this:
onEntityFieldChange(event: FieldChangeEvent) {
this.ngrxJsonApiService.patchResource({
resource: Object.assign({}, event.entity, {attributes: {someField: event.newValue}}),
toRemote: false
});
}
saveEntity(entity: StoreResource) {
this.ngrxJsonApiService.patchResource({resource: entity, toRemote: true});
}
My problem is, when I click on Save:
API_UPDATE_INIT
, isUpdating
does not change.API_UPDATE_SUCCESS
, isUpdating
goes from 0
to -1
.Am I possibly using the service incorrectly? (Please note: documentation is missing in that department.)
I need this working because I need a way to check whether the application is loading, and I'm checking fields like isUpdating
to achieve that.
This is to discuss how errors and queries that are not found should be dealt with.
For example: service.selectResults('1')
, if the provided queryId
is not found, what should happen? Return null
or undefined
or throw an error. This applies to many other scenarios.
List of issues that must be done before v1 final.
to implement frontend side validation, applications can make use of effects and listen to changes to the json api store. If a violation of input data is detected, there should be an action to add resp. clear errors for that particular resource.
Payload of the action would take a ResourceIdentifier and an array of ResourceError objects.
e.g. selectors.ts getResults$ make use of "ids ? ..." and returns an undefined if no data is returned. It should rather be:
.mergeMap(ids => ids.length > 0 ? state$.let(
this.getManyStoreResource$(ids)) : Observable.of(new Array<StoreResource>()));
Maybe it is my lack of knowledge but then simply correct me a close the issue.
I have been working quite a bit with Angular 2 (through angular2-jsonapi) but I really want to utilise this project in a new Angular 4 application.
After reading through everything there are a few things that I am missing. How can we easily integrate the ngrx-json-api approach with an existing model (like done with decorators in angular2-jsonapi) without having to write a our own reducers on the side. The intention is of course to have one definition utilised across the entire application (and validated against only that one definition). Or maybe this is not even the intention of this project, or maybe I am getting it all wrong?
I understand we define the model definition in ResourceDefinition but can you somehow decorate existing model objects and utilise that as the resource defintion as the below example:
@JsonApiModelConfig({
type: 'users'
})
export class User extends JsonApiModel {
@Attribute()
name: string;
// ...
}
Or do we have any examples? I am really looking for best practices here.
Thanks in advance.
enum numbers are not that readable while debugging, probably better to use as tring type (export type ... = '...' | ...)
potentially something pre 1.0:
currently we have:
ApiReadInitAction
ApiReadSuccessAction
ApiReadFailAction
QueryRefreshAction
QueryStoreInitAction
QueryStoreSuccessAction
QueryStoreFailAction
naming should be aligned to make clear one is in-memory/local and the other is remote/api. One possibility:
ApiQueryInitAction
ApiQuerySuccessAction
ApiQueryFailAction
ApiQueryRefreshAction
LocalQueryInitAction
LocalQuerySuccessAction
LocalQueryFailAction
queries can make asynchrounous api calls but can also be directly updated. The store should only process an incoming response if the query parameters/type/id/... where not changed in the meantime.
instead of "create", "read", "update" the regular http terms "get", "post", "patch" should be used.
In the "official" ngrx example application, selectors were recently refactored to use the reselect
library from redux. This is the related PR which explains the decision.
It would be great if this library made this change as well. I added a branch https://github.com/abdulhaq-e/ngrx-json-api/tree/selectors_using_reselect with a basic attempt. However, it appears that selectors are best used when no dynamic arguments are required (reselect
docs say so). There is a way to use selectors with dynamic arguments but I don't like it (see https://github.com/reactjs/reselect#q-how-do-i-create-a-selector-that-takes-an-argument) and it's difficult to compose them together. What is recommended is to have this "dynamic argument" as part of the state.
Right now I see no way of having the dynamic argument as part of the state (e.g. queryId
). But perhaps we could figure something out. I'll leave this open.
instead of a single Options there should be multiple ones tailored for the various service methods so that it becomes impossible for a consumer to pass incorrect resp. unused parameters. Some of those parameters can then also be enforced as required.
Frequently, at least for us, frontend developers start to work on things while the backend is not yet around. It would be great to have support for mocking where the HTTP json api endpoint is replaced by a local mock file. That mock file could simply be a json file holding an array of Resource objects.
I guess the already existing in-memory/local sorting/filtering/paging support could be used here as well. This way the mocking should be quite easy to get.
needs to clone newState[query.type] as well.
Hello everyone
This is my first ever (public) library. I have never designed an API that is meant to be used by others. Hence, it's tough making correct decisions.
If anyone sees a large flaw that can be avoided from now please say so and we'll happily discuss it ๐
I'll be adding more docs but the core functionality is shown in the READ ME file.
I'm using release 1.0.0. I have this editor-like CRUD application that shows a list of animals, lets the user modify the attributes of an animal, delete an animal, and add a new one. All that on a single page.
I use two queries: one for the existing animals$
, and one for the newAnimal$
to be added:
private initAnimals() {
this.animals$ = this.ngrxJsonApiService.findMany({
query: {
type: JSONAPI_TYPE_ANIMAL,
queryId: JSONAPI_QUERYID_ANIMALS
}
}).map(animals => animals ? animals.data : []);
}
private initNewAnimal() {
let newAnimalId: string = uuid();
this.ngrxJsonApiService.postResource({
resource: Object.assign({}, EMPTY_ANIMAL, {id: newAnimalId}),
toRemote: false
});
this.newAnimal$ = this.ngrxJsonApiService.findOne({
query: {
type: JSONAPI_TYPE_ANIMAL,
queryId: JSONAPI_QUERYID_NEW_ANIMAL,
id: newAnimalId
},
fromServer: false
}).map(newAnimalResult => newAnimalResult.data);
}
When the user presses the Add button, the animal listing should get updated to show the newly added animal, and the form fields should get cleared. I solve that with an effect:
// Based on ngrx-json-api's refresQueriesOnDelete.
@Effect() refreshAnimalsAndClearNewAnimalOnCreate$ = this.actions$
.ofType(NgrxJsonApiActionTypes.API_CREATE_SUCCESS)
.withLatestFrom(this.ngrxJsonApiStore, (action, store) => {
let id = {id: action.payload.query.id, type: action.payload.query.type};
if (!id.id || !id.type) {
throw new Error('API_CREATE_SUCCESS did not carry resource id and type information');
}
let state = store[this.ngrxJsonApiSelectors.storeLocation] as NgrxJsonApiStore;
let actions = [];
for (let queryId in state.queries) {
if (state.queries.hasOwnProperty(queryId)) {
let newAnimalld: string = uuid();
if (queryId == JSONAPI_QUERYID_NEW_ANIMAL) {
actions.push(new PostStoreResourceAction(Object.assign({}, EMPTY_ANIMAL, {id: newAnimalId})));
actions.push(new QueryStoreInitAction({
type: JSONAPI_TYPE_ANIMAL,
queryId: JSONAPI_QUERYID_NEW_ANIMAL,
id: newAnimalId
}));
}
else if (state.queries[queryId].query.type == action.payload.query.type) {
actions.push(new QueryRefreshAction(queryId));
}
}
}
return actions;
})
.flatMap(actions => Observable.of(...actions));
When I press the Add button, everything is okay if the animals listing is so huge that the updated JSON API response for animals$
does not contain the newly added entry. But if the database is small enough to return the newly created entry, I see an odd QUERY_STORE_SUCCESS action emitted as last, which overwrites the newAnimal$
back to what it was before my QueryStoreInitAction
.
After a bit of investigation, I found that this oddity comes from the fact that mergeMap
is used in queryStore$
:
@Effect() queryStore$ = this.actions$
.ofType(NgrxJsonApiActionTypes.QUERY_STORE_INIT)
.map<Action, Query>(toPayload)
.mergeMap((query: Query) => {
return this.store
.select(this.selectors.storeLocation)
.let(this.selectors.queryStore$(query))
.map(results => new QueryStoreSuccessAction({
jsonApiData: {data: results},
query: query
}))
.catch(error => Observable.of(
new QueryStoreFailAction(this.toErrorPayload(query, error))));
});
If we use switchMap
at line 4 of the quoted code, everything works as expected. I think that in general, we should use switchMap
everywhere in similar places of effects.ts
.
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.