2

I have the following property in a component:

  get isUploading() {
    return this.files.length > 0 && this.files.some((f) => f.state === FileUpLoadState.uploading);
  }

I then have a progress component that is bound to that property:

<md-progress-bar *ngIf="this.isUploading" mode="indeterminate">loading...
</md-progress-bar>

I have an upload function that loops through an array of file objects and directly mutates the array of objects:

 upload(){
    this.files.forEach(async (uploadedFile) => {
      uploadedFile.state = FileUpLoadState.uploading;

      try {
        uploadResponse = await this.blobStorageService.upload(uploadedFile.file, {
          maxSingleShotSize,
          progress: (progress) => file.progress = progress.
        });

        uploadedFile.state = FileUpLoadState.success;

        this.uploadActions.persistUpload({ ...uploadedFile, ...uploadResponse });
      } catch (err) {
        uploadedFile.error = err.message;
        uploadedFile.state = FileUpLoadState.error;
      }
    });
  }

I am coming from the react world were observing property changes like this is not something you do.

Would using an rxjs observable be a better way or what is the common idiom for doing this in angular?

8
  • oops, updated. Sorry yes, this is observing as opposed to property changes being pushed down Commented Feb 7, 2019 at 14:41
  • What property is being pushed down? Commented Feb 7, 2019 at 14:41
  • I am saying in react you generally don't observe like this in react. no props are getting pushed down in this example. It is observing the property change using dirty checking Commented Feb 7, 2019 at 14:42
  • Please show how you're updating this.files Commented Feb 7, 2019 at 14:43
  • I have updated the question, thank you Commented Feb 7, 2019 at 14:47

1 Answer 1

2

I'll try to explain what's happening now and why it's not recommended and an alternative approach.

get isUploading() {
   return this.files.length > 0 && this.files.some((f) => f.state === FileUpLoadState.uploading);
}

The above is a state property where the value is mutated asynchronously by the upload(). Angular will need to render the component's view to reflect any changes in the HTML. This is all done automatically via change detection, and the reason you're seeing these changes is because Angular uses zones to detect async operations which trigger change detection.

https://www.joshmorony.com/understanding-zones-and-change-detection-in-ionic-2-angular-2/

Your example works, because you're creating promises inside an Angular zone. When you're inside a zone and you create an async listener, then Angular will mocky patch the operation so that Angular change detection happens when the async operation completes.

await this.blobStorageService.upload(...)

So the above line of code is executed inside a zone, and when async completes Angular will walk down all of the component views to see if any have changed. It will then render any components that are dirty.

I am coming from the react world were observing property changes like this is not something you do.

You definitely wouldn't do this in React, and I can't honestly say that I like this aspect of Angular.

Out of the box Angular requires less understanding of how views, zones and change detection works. You can do things like this and quickly create a working application, but it does not scale up to a larger application. As you add more and more views from components then change detection slows down and begins to lag.

We call this technical debt because if your application grows in size, then you'll have to go back and refactor things to use OnPush change detection. After you've been burned by this feature in Angular, then you'll always use OnPush from there afterwards.

https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4

As a React developer. You've spotted this very early and it just feels wrong but you don't understand why, and I think that really demonstrates why React has become so popular. It solves an important problem in Angular by never allowing the problem to exist in the first place.

I just want to know if this is how angular devs do this kind of thing or do they use rxjs

So the recommended approach is to use OnPush change notification. Once the component is configured to use OnPush then it's view will not be updated after the async operation is completed. Angular will not detect changes because none of the input bindings have changed.

Now we can actually answer your question better. How do you update the view when an async operation has changed?

Mark the view dirty

You can inject the ChangeDetectRef class into the component, and then mark the view as dirty.

https://angular.io/api/core/ChangeDetectorRef#markforcheck

This is by far the easiest solution when Reactive programming requires that you refactor the source code, or you can't use reactive programming at all.

    this.uploadActions.persistUpload({ ...uploadedFile, ...uploadResponse });
    this.changeDetectorRef.markforcheck();

You would just add one line to your code and this will tell Angular that the component is dirty.

I wouldn't say that this one approach is a best practice, but it's how you would make it work with OnPush and that's what it's important. This component will now only need updating when it's actually changed, and Angular won't have to detect changes all the time.

Reactive programming with async

Reactive programming allows you to update the view using the async pipe.

https://angular.io/api/common/AsyncPipe

The advantage is that the pipe handles marking the view as dirty. So you can create a component that uses OnPush and responds to async operations without having to manually call markforcheck() all the time.

Your template would become:

<md-progress-bar *ngIf="isUploading | async" mode="indeterminate">loading...

The property would then be an observable that emits true/false values. There are many ways to convert your upload() function to emit the value, but I don't want to attempt to rewrite it for you. I think that's not really what the question is about.

Sign up to request clarification or add additional context in comments.

6 Comments

what an answer! Very much appreciated
tbh i’m amazed anybody would choose angular. it’s needlessly and insanely complicated. i am amazed anybody uses it although i do have a big react bias. i took a contract using angular out of curiosity. now i know
@dagda1 I can understand why you see it as complicated, because it truly is more complicated but the benefits of using a framework when trying to build a large SASS platform outweigh the advantages of using composition of smaller libraries to achieve the same results. Angular and React achieve the exact same goals, but React is composition of smaller libraries and harder to hire another developer who knows all the libraries you're using. An Angular developer is an Angular developer and that's very appealing when you need to scale a team working on a large project.
As I said, I am shrouded in bias and have been using react a long time so I am not sure if I can look at this without the bias. Lots of people use angular so it can't be as bad as I am perceiving it. JSX is another thing I miss, all just javascript, no cryptic DSL to bind things together or having to put input attributes to bring it all together. I'm only on angular 4 but all the providers, declarations and another module system because angular has to know everything straight up seems nuts
@dagda1 I would be happy to trade. You teach me React and I'll teach you Angular ;)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.