Comments (11)
@david-bulte I'm working a prototype that improves the transaction API. First, a new operator:
getMovies() {
return timer(1000).pipe(
mapTo(movies),
withTransaction(response => {
this.actorsStore.set(response.entities.actors);
this.genresStore.set(response.entities.genres);
const movies = {
entities: response.entities.movies,
ids: response.result
};
this.moviesStore.set(movies);
})
);
}
The second is a built-in solution for the case you mentioned, something like:
selectActorNames(): Observable<Actor[]> {
return combineLatest(this.movieQuery.selectActive(), this.selectAll({asObject: true})).pipe(
batch(),<====
filter(([movie, actors]) => !!movie && !!actors),
map(([movie, actors]: [Movie, any]) => {
return movie.actorIds.map(actorId => actors[actorId].name)
})
);
}
from akita.
Here's an example: https://stackblitz.com/edit/angular-rqbnpv.
Have a look at ActorQuery's selectActorNames() (see below) which combines the Actor and Movies store. Depending on whether we first add the Actors and call the MovieStore.setActive() method afterwards or vice versa, the selectActorNames() method succeeds or fails. This is because when we first call the setActive() method, the selector is immediately triggered, and at that time we don't find the corresponding Actors.
Ideally the selectors should only be triggered after each 'tick'/event? I dunno. Or otherwise use the transaction mechanism for this?
actor.query.ts
selectActorNames(): Observable<Actor[]> {
return combineLatest(this.movieQuery.selectActive(), this.selectAll({asObject: true})).pipe(
filter(([movie, actors]) => !!movie && !!actors),
map(([movie, actors]: [Movie, any]) => {
return movie.actorIds.map(actorId => actors[actorId].name)
})
);
}
movie.service.ts
load(id: string) {
this.mockHttp(id).subscribe(({movie, actors}) => {
this.movieStore.add(movie);
// this is ok
// this.actorStore.add(actors);
// this.movieStore.setActive(movie.id);
// this is not
this.movieStore.setActive(movie.id);
this.actorStore.add(actors);
})
}
from akita.
As I mentioned in my article, you should use the auditTime
operator:
selectActorNames(): Observable<Actor[]> {
return combineLatest(this.movieQuery.selectActive(), this.selectAll({asObject: true})).pipe(
auditTime(0),
filter(([movie, actors]) => !!movie && !!actors),
map(([movie, actors]: [Movie, any]) => {
return movie.actorIds.map(actorId => actors[actorId].name)
})
);
}
from akita.
@transaction only optimized updates on the same store. So for example, if you call the following code:
@transaction
method() {
this.storeOne.setActive(1);
this.storeOne.add({ name: 'qwe' });
}
Subscribers to the store will get one notification instead twice (one for each call update).
If it answers your question, please close the issue. Thanks.
from akita.
Hi @NetanelBasal. Thanks for the great framework! Do you consider adding this in a future release?
The use case I'm thinking of is when you have 2 related entities, say Movie and Actor, where Movie has a field actorIds. There is a selector that returns the Actors of the "active" Movie. This goes well when you first add the Actors entities and then sets the active Movie. When you do it the other way around though things fall apart.
So there is a workaround, but it would be handy to do these changes in an atomic way.
from akita.
Thanks, can you add a concrete example (stackblitz) so we can look into this?
from akita.
@david-bulte check the new functionality here:
https://stackblitz.com/edit/angular-brebji?file=src%2Fapp%2Factor.query.ts
It's still experimental so let me know if there any issues.
from akita.
Thanks. This is working fine.
Some more thoughts, some may be relevant, others probably out of scope:
- What's not (yet?) working are transactions spanning multiple async operations:
load(id: string) {
this.mockHttp(id).pipe(
withTransaction(({ movie, actors }) => {
this.movieStore.add(movie);
this.movieStore.setActive(movie.id);
// this.actorStore.add(actors);
this.addActors(actors);
}),
).subscribe()
}
addActors(actors) {
timer(500).pipe(
// ---> I also tried with nested transactions <---
// withTransaction(() => {
// this.actorStore.add(actors);
// }),
).subscribe(() => {
this.actorStore.add(actors);
})
}
- I like the waitForTransaction() selector, it clearly communicates what is going on. On the other hand, one could argue that the selectors shouldn't be triggered as long as the transaction hasn't been committed. Selectors shouldn't know whether something was added in a transaction or not (separation of concerns). Would filtering on a transaction state help? Something along these lines?
const tx$ = new BehaviorSubject(false);
_select<R>(project) {
return combineLatest(this.store.asObservable(), tx$).pipe(
filter(([state, tx]) => !!tx),
map(([state]) => state)
map(project),
distinctUntilChanged()
);
}
from akita.
What's not (yet?) working are transactions spanning multiple async operations:
Transactions are only for synchronous operations; we can't know that all the async operations are done. (unless we use zone js and we don't want to)
Selectors shouldn't know whether something was added in a transaction or not (separation of concerns).
The waitForTransaction
is relevant only for a combineLatest/merge
functionality. Adding it per selector won't help.
from akita.
If you have a nice suggestion for a solution to your first issue, share with us.
from akita.
I would say transactions are not necessary over async operations. This can be achieved by waiting for both operations to complete and then manipulate the store.
But manipulating multiple stores so observables only fire once is very important in order to avoid ripple-effects. Otherwise, every selector spanning multiple stores has to use the auditTime operator. Not sure, but I can imagine that this also might add problems (values from single store access are rendered and next tick the things from multiple stores force a re-rendering.
I don't have a stackblitz and just came over this question because we're currently choosing a state mgmt solution (competitors are NGXS and NgRx).
from akita.
Related Issues (20)
- Angular 15 error HOT 1
- UpsertMany middleware equivalent
- this.active.indexOf is not a function on removeActive HOT 2
- Please publish the recently merged Angular 14 fix HOT 11
- docs site is outdated HOT 1
- Compile errors after upgrading to Akita 8.0.0 HOT 2
- sample-app deployment from the docs is outdated
- Loading state is set to `false` unexpectedly. HOT 1
- Angular schematics bug HOT 4
- The wrong rxjs peerDependency for >=8 version HOT 2
- Removal of property does not have any effect HOT 1
- There is no CLI documentation
- Can't install @datorama/akita for Angular 15.2.8. HOT 14
- Query select() – returns incorrect state value, once store update is called synchronisly from query subscription
- Package "@datorama/akita" was found but does not support schematics.
- Issue while updating property of entity
- Query select fires twice and collects observables
- Duplicate entities returned by EntityQuery
- In new angular v16 i'm getting ERROR TypeError: this.userQuery.select is not a function HOT 2
- Missing Configuration Changes For Angular 17 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from akita.