avatarOliver S

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

7759

Abstract

component template how the upload blue button and the invisible file input are linked. When the user clicks on the blue button, a click handler triggers the file input via <code>fileUpload.click()</code>.</p><p id="ac29">The user will then choose a file from the file upload dialog, and the <code>change</code> event will be triggered and handled by <code>onFileSelected()</code>.</p><h1 id="7571">Uploading a file to the backend using the Angular HTTP Client</h1><p id="fff6">Let’s now have a look at our component class and the implementation of <code>onFileSelected()</code>:</p><div id="98fa"><pre><span class="hljs-meta">@Component</span>({ <span class="hljs-attr">selector</span>: <span class="hljs-string">'file-upload'</span>, <span class="hljs-attr">templateUrl</span>: <span class="hljs-string">"file-upload.component.html"</span>, <span class="hljs-attr">styleUrls</span>: [<span class="hljs-string">"file-upload.component.scss"</span>] }) <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FileUploadComponent</span> {

fileName = <span class="hljs-string">''</span>;

<span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> http: HttpClient</span>) {}

<span class="hljs-title function_">onFileSelected</span>(<span class="hljs-params">event</span>) {

    <span class="hljs-keyword">const</span> <span class="hljs-attr">file</span>:<span class="hljs-title class_">File</span> = event.<span class="hljs-property">target</span>.<span class="hljs-property">files</span>[<span class="hljs-number">0</span>];

    <span class="hljs-keyword">if</span> (file) {

        <span class="hljs-variable language_">this</span>.<span class="hljs-property">fileName</span> = file.<span class="hljs-property">name</span>;

        <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FormData</span>();

        formData.<span class="hljs-title function_">append</span>(<span class="hljs-string">"thumbnail"</span>, file);

        <span class="hljs-keyword">const</span> upload$ = <span class="hljs-variable language_">this</span>.<span class="hljs-property">http</span>.<span class="hljs-title function_">post</span>(<span class="hljs-string">"/api/thumbnail-upload"</span>, formData);

        upload$.<span class="hljs-title function_">subscribe</span>();
    }
}

}</pre></div><p id="efe8">Here is what is going in this component:</p><ul><li>we are getting a reference to the files that the user selected by accessing the <code>event.target.files</code> property.</li><li>we then build a form payload by using the <code>FormData</code> API. This is a standard browser API, it's not Angular-specific.</li><li>we then use the Angular HTTP client to create an HTTP request and send the file to the backend</li></ul><p id="8224">At this point, we would already have a working file upload component. But we want to take this component one step further. We want to be able to display a progress indicator to the user, and also support upload cancelation.</p><h1 id="2e9b">How to Display a File Upload Progress Indicator</h1><p id="0abb">We are going to add a few more elements to the UI of our file upload component. Here is the final version of the file upload component template:</p><div id="2cff"><pre><input <span class="hljs-keyword">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"file-input"</span> [accept]=<span class="hljs-string">"requiredFileType"</span> (change)=<span class="hljs-string">"onFileSelected($event)"</span> #fileUpload>

<span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"file-upload"</span>></span>

{{fileName || "No file uploaded yet."}}

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">mat-mini-fab</span> <span class="hljs-attr">color</span>=<span class="hljs-string">"primary"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"upload-btn"</span>
  (<span class="hljs-attr">click</span>)=<span class="hljs-string">"fileUpload.click()"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">mat-icon</span>&gt;</span>attach_file<span class="hljs-tag">&lt;/<span class="hljs-name">mat-icon</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>

<span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"progress"</span>></span>

<span class="hljs-tag"><<span class="hljs-name">mat-progress-bar</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"progress-bar"</span> <span class="hljs-attr">mode</span>=<span class="hljs-string">"determinate"</span> [<span class="hljs-attr">value</span>]=<span class="hljs-string">"uploadProgress"</span> *<span class="hljs-attr">ngIf</span>=<span class="hljs-string">"uploadProgress"</span>></span>

<span class="hljs-tag"></<span class="hljs-name">mat-progress-bar</span>></span>

<span class="hljs-tag"><<span class="hljs-name">mat-icon</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"cancel-upload"</span> (<span class="hljs-attr">click</span>)=<span class="hljs-string">"cancelUpload()"</span> *<span class="hljs-attr">ngIf</span>=<span class="hljs-string">"uploadProgress"</span>></span>delete_forever<span class="hljs-tag"></<span class="hljs-name">mat-icon</span>></span>

<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span></pre></div><p id="6548">The two main elements that we have added to the UI are:</p><ul><li>An Angular Material progress bar, which is visible only while the file upload is still in progress.</li><li>A cancel upload button, also only visible when an upload is still ongoing</li></ul><h2 id="8e22">How to know how much of the file has been uploaded?</h2><p id="318b">The way that we implement the progress indicator, is by using the <code>reportProgress</code> feature of the Angular HTTP client.</p><p id="7c34">With this feature, we can get notified of the progress of a file upload via multiple events emitted by the HTTP Observable.</p><p id="8436">To see it in action, let’s have a look at the final version of the file upload component class, with all its features implemented:</p><div id="88a0"><pre><span class="hljs-meta">@Component</span>({ <span class="hljs-attr">selector</span>: <span class="hljs-string">'file-upload'</span>, <span class="hljs-attr">templateUrl</span>: <span class="hljs-string">"file-upload.component.html"</span>, <span class="hljs-attr">styleUrls</span>: [<span class="hljs-string">"file-upload.component.scss"</span>] }) <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FileUploadComponent</span> {

<span class="hljs-meta">@Input</span>()
<span class="hljs-attr">requiredFileType</span>:<span class="hljs-built_in">string</span>;

fileName = <span class="hljs-string">''</span>;
<span class="hljs-attr">uploadProgress</span>:<span class="hljs-built_in">number</span>;
<span class="hljs-attr">uploadSub</span>: <span class="hljs-title class_">Subscription</span>;

<span class="hljs-title function_">constructor</span>(<span class="hljs-

Options

params"><span class="hljs-keyword">private</span> http: HttpClient</span>) {}

<span class="hljs-title function_">onFileSelected</span>(<span class="hljs-params">event</span>) {
    <span class="hljs-keyword">const</span> <span class="hljs-attr">file</span>:<span class="hljs-title class_">File</span> = event.<span class="hljs-property">target</span>.<span class="hljs-property">files</span>[<span class="hljs-number">0</span>];
  
    <span class="hljs-keyword">if</span> (file) {
        <span class="hljs-variable language_">this</span>.<span class="hljs-property">fileName</span> = file.<span class="hljs-property">name</span>;
        <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FormData</span>();
        formData.<span class="hljs-title function_">append</span>(<span class="hljs-string">"thumbnail"</span>, file);

        <span class="hljs-keyword">const</span> upload$ = <span class="hljs-variable language_">this</span>.<span class="hljs-property">http</span>.<span class="hljs-title function_">post</span>(<span class="hljs-string">"/api/thumbnail-upload"</span>, formData, {
            <span class="hljs-attr">reportProgress</span>: <span class="hljs-literal">true</span>,
            <span class="hljs-attr">observe</span>: <span class="hljs-string">'events'</span>
        })
        .<span class="hljs-title function_">pipe</span>(
            <span class="hljs-title function_">finalize</span>(<span class="hljs-function">() =&gt;</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">reset</span>())
        );
      
        <span class="hljs-variable language_">this</span>.<span class="hljs-property">uploadSub</span> = upload$.<span class="hljs-title function_">subscribe</span>(<span class="hljs-function"><span class="hljs-params">event</span> =&gt;</span> {
          <span class="hljs-keyword">if</span> (event.<span class="hljs-property">type</span> == <span class="hljs-title class_">HttpEventType</span>.<span class="hljs-property">UploadProgress</span>) {
            <span class="hljs-variable language_">this</span>.<span class="hljs-property">uploadProgress</span> = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">round</span>(<span class="hljs-number">100</span> * (event.<span class="hljs-property">loaded</span> / event.<span class="hljs-property">total</span>));
          }
        })
    }
}

<span class="hljs-title function_">cancelUpload</span>(<span class="hljs-params"></span>) { <span class="hljs-variable language_">this</span>.<span class="hljs-property">uploadSub</span>.<span class="hljs-title function_">unsubscribe</span>(); <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">reset</span>(); }

<span class="hljs-title function_">reset</span>(<span class="hljs-params"></span>) { <span class="hljs-variable language_">this</span>.<span class="hljs-property">uploadProgress</span> = <span class="hljs-literal">null</span>; <span class="hljs-variable language_">this</span>.<span class="hljs-property">uploadSub</span> = <span class="hljs-literal">null</span>; } }</pre></div><p id="6e91">As we can see, we have set the <code>reportProgress</code> property to true in our HTTP call, and we have also set the <code>observe</code> property to the value <code>events</code>.</p><p id="cbac">This means that, as the POST call continues, we are going to receive event objects reporting the progress of the HTTP request.</p><p id="294a">These events will be emitted as values of the <code>http</code> Observable, and come in different types:</p><ul><li>events of type <code>UploadProgress</code> report the percentage of the file that has already been uploaded</li><li>events of types <code>Response</code> report that the upload has been completed</li></ul><p id="aa3c">Using the events of type <code>UploadProgress</code>, we are saving the ongoing upload percentage in a member variable <code>uploadProgress</code>, which we then use to update the value of the progress indicator bar.</p><p id="ea5b">When the upload either completes or fails, we need to hide the progress bar from the user.</p><p id="b2ef">We can make sure that we do so by using the RxJs <code>finalize</code> operator, which is going to call the <code>reset()</code> method in both cases: upload success or failure.</p><h1 id="29cb">How to Cancel an Ongoing File Upload</h1><p id="d74e">In order to support file upload cancellation, all we have to is keep a reference to the RxJs Subscription object that we get when the <code>http</code> Observable gets subscribed to.</p><p id="aa0f">In our component, we store this subscription object in the <code>uploadSub</code> member variable.</p><p id="5ae9">While the upload is still in progress, the user might decide to cancel it by clicking on the cancel button. Then the <code>cancelUpload()</code> upload method is going to get triggered and the HTTP request can be canceled by unsubscribing from the <code>uploadSub</code> subscription.</p><p id="5e61">This unsubscription will trigger the immediate cancelation of the ongoing file upload.</p><h1 id="9aad">How to accept only files of a certain type</h1><p id="c8b0">In the final version of our file upload component, we can require the user to upload a file of a certain type, by using the <code>requiredFileType</code> property:</p><div id="8c53"><pre><span class="hljs-tag"><<span class="hljs-name">file-upload</span> <span class="hljs-attr">requiredFileType</span>=<span class="hljs-string">"image/png"</span>></span><span class="hljs-tag"></<span class="hljs-name">file-upload</span>></span></pre></div><p id="fe67">This property is then passed directly to the <code>accept</code> property of the file input in the file upload template, forcing the user to select a png file from the file upload dialog.</p><h1 id="a737">How to Upload Multiple Files</h1><p id="d62f">By default, the browser file selection dialog will allow the user to select only one file for upload.</p><p id="cb3a">But using the <code>multiple</code> property, we can allow the user to select multiple files instead:</p><div id="af5a"><pre><<span class="hljs-built_in">input</span> <span class="hljs-built_in">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"file-upload"</span> multiple></pre></div><p id="a900">Notice that this would need a completely different UI than the one that we have built. A styled upload button with a progress indicator only works well for the upload of a single file.</p><p id="14d6">For a multi-file upload scenario, there are a variety of UIs that could be built: a floating dialog with the upload progress of all files, etc.</p><h1 id="f2f4">Summary</h1><p id="a227">The best way to handle file upload in Angular is to build one or more custom components, depending on the supported upload scenarios.</p><p id="b3e1">A file upload component needs to contain internally an HTML input of type file, that allows the user to select one or more files from the file system.</p><p id="e4ad">This file input should be hidden from the user as it’s not styleable and replaced by a more user-friendly UI.</p><p id="289e">Using the file input in the background, we can get a reference to the file via the <code>change</code> event, which we can then use to build an HTTP request and send the file to the backend.</p><p id="245e">Also, if you have some questions or comments please let me know in the comments below and I will get back to you.</p></article></body>

Lambda Functions in Python

Python Shorts — Part 1

In this series of posts we will cover neat Python features every developer should know about. In Part 1, we take a look at lambda functions.

Photo by Chris Ried on Unsplash

Lambda functions in Python are small, anonymous functions, mainly used for brevity and convenience.

Their syntax is as follows:

lambda arguments: expression

They can take an arbitrary number of arguments, and will execute expression and return its value.

As mentioned above, this is often used for convenience and to shorten code, e.g. in iterators or generator function.

As afirst example, consider defining a lambda to square a number:

square = lambda x: x**2
print(square(5))

Here, we make use of the fact that functions are first class objects in Python, assign our lambda function to the variable square, and later call it and print the result.

Let us now put this into a somewhat more realistic setting using Python’s map function: this takes a function f and a list, and returns a new list, in which f was applied to all values in it:

standard_list = [0, 1, 2, 3, 4, 5]
squared_list = list(map(lambda x: x**2, standard_list))
print(squared_list)

That’s it for Part 1, thanks for tuning in — and feel free to come back for more:

Python
Lambda Function
Recommended from ReadMedium