Referencing this NGRX ticket, I am seeing an oddity where a code-based subscription to a factory selector does not fire with state updates, however, the async pipe does work.
To clarify, if I set up a public observable property on a component and bind the HTML to it with an async pipe, the value will update as the state updates.
component.ts
public test$: Observable<ChatInteraction>;
[...]
this.test$ = this.store
.select(getNewMessageForChat(this.chat.chatId));
component.html
test: {{(test$ | async).message}}
Then I end up with output like
test: test1
test: test2
etc etc
However, if I try to subscribe to that same observable, it fires once when the subscription is created, but never again as the state updates.
component.ts
this.store
.select(getNewMessageForChat(this.chat.chatId))
.subscribe(() => {
if (this._calculateScrollDistanceFromBottom(this.chatWindowElem.nativeElement) <= 20) {
this._scrollToBottom();
}
});
If I put a breakpoint on that if statement, it does not get hit as I update state.
Any ideas why?
Figured it out. Turned out I had an undefined object reference in my subscription, which caused the subscription to break on its first run and resulted in it no longer firing. This was difficult to catch as it turns out the subscription code has a try/crush in it unless a setting is set.
__tryOrUnsub(fn, value) {
try {
fn.call(this._context, value);
}
catch (err) {
this.unsubscribe();
if (config.useDeprecatedSynchronousErrorHandling) {
throw err;
}
else {
hostReportError(err);
}
}
}
hostReportError didn't bubble anything up so to me it looked like it just wasn't firing anymore.
Related
I am looking for a smarter way to dispatch a symfony event within entity manager transactional, so that in case of rollback it won't complain.
Here's the sample of code:
$this->em->transactional(
function () use ($xyz, $oldXyz) {
if ($oldXyz !== null) {
$this->doRemove($oldXyz);
}
$this->em->persist($xyz);
}
);
Where:
private function doRemove(XyzInterface $oldXyz): void
{
$this->em->remove($oldXyz);
$this->em->flush();
$this->dispatcher->dispatch(new XyzEvent($oldXyz), XyzEvents::onXyzDeleted);
}
This thing will complain 'app.ERROR: Pending transaction on master connection' because this due to event dispatching cannot be rollback.
I can not move out event dispatch from doRemove, because it's used somewhere else plus it's job of doRemove to dispatch this event here, since actual removing happens here only.
Any help would be appreciated, thank you all in advance.
Just return $oldXyz to the caller (by editing the closure as follows)
function () use ($xyz, $oldXyz) {
if ($oldXyz !== null) {
$this->doRemove($oldXyz);
return $oldXyz;
}
$this->em->persist($xyz);
return null;
}
Then check for returned value and dispatch
$oldXyz = $this->em->transactional(...);
if ($oldXyz instanceof <FQCNForOldXyz>) {
$this->dispatcher->dispatch(new XyzEvent($oldXyz), XyzEvents::onXyzDeleted);
}
Didn't tried directly, but I'm pretty confident it works.
What is the proper way to fetch data from an async method when a property value changes? Currently, I am doing something like this.
<form>
<select class="form-control" #bind="DayFilter">
<option>None</option>
<option>Monday</option>
<option>Tuesday</option>
<option>Wednesday</option>
<option>Thursday</option>
<option>Friday</option>
<option>Saturday</option>
<option>Sunday</option>
</select>
</form>
#code
{
private string dayFilter = NoDayFilter;
private string DayFilter
{
get => dayFilter;
set
{
dayFilter = value;
_ = GetData();
}
}
private async Task GetData()
{
....
StateHasChanged();
}
}
I am guessing this is not correct according to the Blazor Docs:
Asynchronous work when applying parameters and property values must
occur during the OnParametersSetAsync lifecycle event.
Your doubts about whether you employ async calls properly are not really related to the component life cycle methods...
What you're doing is fine, and it should work. If you use the async key word your method is asynchronous... But you don't have to call the StateHasChanged method. It is called automatically when UI events are involved and when an async mehtod is used: If you use async method, you must await the called method, as for instance:
private async Task GetData()
{
forecasts = await httpClient.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}
When the runtime executes this method, and notices the await key word, it yields control to the calling code... When the GetFromJsonAsync method returns, execution continues, synchronously to the end of the method... Also the StateHasChanged method is automatically called, and your component is re-render.
The only issue I find with your code, is calling GetData() from the set accessor. This works and all, but I would call the GetData() from an event handler set for the selection of a new item ( change event).
UPDATE
"firing and forgetting" is fine. But you must use the await key word in your GetData method, which is why you need to call StateHasChanged manually, without which there is no way for the component to know when the call has completed. I guess if you place await Task.CompletedTask;
you're going to solve the issue.
I did consider just using the OnChange event, but I like the aesthetics the property binding approach
'change' event you mean, right?
What aesthetics ? The compiler is going to produce a different code for you, anyway, with a 'value' attribute and 'onchange' event.
I hope that helps! If you get stuck, let me know
Calling an async method from a property setter is definitely not OK if you want the UI to update. After thinking about the answers that enet and Henk Holterman gave, I added some debug code
set
{
dayFilter = value;
_ = GetData();
Debug.WriteLine("Day Filter After GetData()");
}
private async Task GetData()
{
Debug.WriteLine("Before http");
var temp = await Http.GetFromJsonAsync<DailyData[]>("https://...");
Debug.WriteLine("After http");
...
}
Which produced the following output:
Before http
Day Filter After GetData()
After http
Based on that, I think the UI finished updating prior to the data being fetched. Since there is no way to await from a setter, my original solution is not workable without calling StateHasChanged- which I want to avoid as I think it would lead to multiple UI updates.
You are using GetData like an async void. Possible but not advisable.
I would avoid this 'fire and forget' pattern and use the onchange event instead:
<select class="form-control" #onchange="OnChange"> ... </select>
async Task OnChange(ChangeEventArgs e)
{
DayFilter = (string) e.Value;
await GetData();
}
async Task GetData()
{
.... // await stuff
}
No need to call StateHasChanged anymore.
I saw online a example of removing event listeners of a button in a web components dom in its disconnectedCallback:
class MyComponent extends HTMLElement {
constructor() {
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = "<button>Click</button>";
}
myEvent() {
...
}
connectedCallback() {
this.shadowRoot.querySelector("button").addEventListener("click", this.myEvent.bind(this));
}
// Removes event here:
disconnectedCallback() {
this.shadowRoot.querySelector("button").removeEventListener("click", this.myEvent.bind(this));
}
}
Is there a reason for doing this? As the button is out of dom there wouldn't be issues with it firing? Is there memory leak concerns? It is not listed in the events section of web components best practises. I could understand if was event listener on the window etc but don't understand the effects if the event is triggered by something which is not connected
You can reconnect an element any time and by doing that, you will attach twice an event;
const elem = document.createElement('my-component');
document.body.appendChild(elem);
document.body.removeChild(elem);
document.body.appendChild(elem);
if your event is doing an API call, this will result in a duplicated request
I am using rxJava to fetch data from the database and show it in a recyclerview. The relevant code is shown below
function updateUI(){
ContactsLab contactsLab = ContactsLab.get(getActivity());
Subscription sub = contactsLab.getContactList().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toList()
.subscribe(onContactsReceived());
mCompositeSubscription.add(sub);
}
ContactsLab is a singleton that returns an Observable of Contact objects.
onContactsReceived function is shown below
private Observer<List<Contact>> onContactsReceived(){
return new Observer<List<Contact>>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(List<Contact> contacts) {
if(mContactsAdapter == null) {
mContactsAdapter = new ContactsAdapter(contacts);
mRecyclerView.setAdapter(mContactsAdapter);
} else{
mContactsAdapter.setContactList(contacts);
mContactsAdapter.notifyDataSetChanged();
}
}
};
}
The updateUI function is called in my fragment onResume but the view is updated only the first time. If I come back to this fragment from any other fragment (having added more items to db), onResume is called, updateUI runs and onContactsReceived also runs but returns immediately without calling onNext or onComplete.
I think this has something to do with the way rxJava handles observables but no idea how to fix it (read about defer but couldn't understand much). Can somebody please help?
Edit:
The getContactList function look like this :
public rx.Observable<Contact> getContactList() {
List<Contact> contacts = new ArrayList<>();
ContactCursorWrapper cursorWrapper = queryContacts(null, null);
try{
cursorWrapper.moveToFirst();
while (!cursorWrapper.isAfterLast()){
contacts.add(cursorWrapper.getContact());
cursorWrapper.moveToNext();
}
} finally {
cursorWrapper.close();
}
return rx.Observable.from(contacts);
}
Basically it queries the database and maps the returned Cursor into my Contact class(which is a POJO). I added the rx.Observable.from to get an observable that was later collated using toList and updated into the adapter.
I used this approach avoid having to call notifyDataSetChanged after getting each item (and call it only once after getting all that).
What's the right approach to minimize the number of notifyDataSetChanged calls and also, refresh each time onResume is called?
Your observable contactsLab.getContactList().toList() has terminated.toList() collects all emissions from a source observable to a list and emits the entire list once the source Observable terminates (see the documentation). You aren't going to observe any more emissions from it.
I'm using firebase to store my data and it is structured this way:
users {
userid {information here}
}
friends {
userid {
friendid1:true
friendid2:true
}
}
If a friendid is added or removed, then an appropriate event should be called.
If the user information is changed, then an event update should be triggered.
So far I have:
RxFirebaseDatabase.observeFriendList(/*Path to friends id list*/)
.flatMap(new Func1<RxFirebaseChildEvent, Observable<User>>() {
#Override
public Observable<User> call(RxFirebaseChildEvent stringBooleanPair) {
//This is a childeventlistener callback
//The key of RxFirebaseChildEvent is the friendid
switch (stringBooleanPair.getEventType()) {
case ADDED:
return RxFirebaseDatabase.observeUserInformation(/*path to user list*/.child(stringBooleanPair.getKey()), User.class);
case REMOVED:
return null; //What do I do here to unregister the listener?
}
}
}).subscribe(user -> {
//This is a ValueEventListener callback that returns the user
//Add, remove or update here, how?
});
How do I remove the specific user listener when a friendId is removed and also call add/remove/update on that user to update the view?
I came up with a somewhat hacked-together version, would love to see a better implementation though.
//refUsers is path to users
List<String> userKeys = new ArrayList<>();
RxFirebaseDatabase.observeFriendList(/*Path to friends id list*/)
.flatMap(new Func1<RxFirebaseChildEvent, Observable<User>>() {
#Override
public Observable<User> call(RxFirebaseChildEvent stringBooleanPair) {
//This is a childeventlistener callback
//The key of RxFirebaseChildEvent is the friendid
switch (stringBooleanPair.getEventType()) {
case ADDED:
userKeys.add(stringBooleanPair.getKey());//User Key
return RxFirebaseDatabase
.observeUserInformation(refUsers.child(stringBooleanPair.getKey()),
User.class,
Eventype.ADDED)
.takeWhile((user) -> userKeys.contains(user.getKey()));
case REMOVED:
userKeys.remove(userid);
return RxFirebaseDatabase
.observeUserInformation(refUsers.child(userid),
User.class,
Const.EventType.REMOVED)
.take(1);
}
}
}).subscribe(userEvent -> {
//Returns an object that contains the user and the eventype (added, updated or removed)
});
Basically, I'm keeping track of the users that are in the friends list and only returning an event if the userid is still watched.
You don't need to do that RxFirebase library already remove your listener when you unsubscribe your Observable, you can see it if you check the implementation:
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
query.removeEventListener(childEventListener);
}
I suggest you to check as reference(or just use it) my RxJava2 library if you want to update your app from RxJava to RxJava2:
https://github.com/FrangSierra/Rx2Firebase
If you are interested of it, you can see the differences between both RxJava here.