{"id":27839,"date":"2021-04-07T19:18:15","date_gmt":"2021-04-07T19:18:15","guid":{"rendered":"http:\/\/How-to-upload-images-in-Angular-with-a-Drag-and-Drop-component-Part-II"},"modified":"2025-03-02T08:54:42","modified_gmt":"2025-03-02T16:54:42","slug":"how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/","title":{"rendered":"How to upload images in Angular with a Drag &amp; Drop component &#8211; Part II"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>In a previous post, I explained the main aspects of <a href=\"\">uploading images and rendering them in the browser<\/a> through a Drag &amp; Drop component written in Angular and TypeScript.<\/p>\n<p>In this <em>MediaJam<\/em> we\u2019ll use the same Angular project as a starting point to enable the files uploading in a secure cloud storage system, provided by <a href=\"https:\/\/cloudinary.com\/\">Cloudinary<\/a>, in just a few steps.<\/p>\n<h2>Cloudinary<\/h2>\n<h3>What is the Cloudinary Platform?<\/h3>\n<p>According to the official website:<\/p>\n<blockquote>\n<p>Developers and marketers use Cloudinary to quickly and easily create, manage, and deliver their digital experiences across any browser, device, and bandwidth.<\/p>\n<\/blockquote>\n<p>The platform provides image and video APIs to store, transform, optimize, and deliver media assets. The APIs are easy-to-use, and the platform comes with powerful widgets too!<\/p>\n<p>You can <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">create an account<\/a> for free, and you\u2019ll be ready to upload your images along with this tutorial.<\/p>\n<h3>Uploading Assets to Cloudinary<\/h3>\n<p>The Cloudinary platform provides a variety of options for customizing how the files can be uploaded from your application:<\/p>\n<ul>\n<li>Upload from a <a href=\"https:\/\/cloudinary.com\/documentation\/upload_images#uploading_assets_to_the_cloud\">server-side code<\/a>. It supports different programming languages already.<\/li>\n<li>Upload using the <a href=\"https:\/\/cloudinary.com\/documentation\/upload_images#uploading_with_a_direct_call_to_the_rest_api\">REST API<\/a>, which is customizable, and can be used from the browser.<\/li>\n<li>Upload using the Cloudinary\u2019s client libraries(<a href=\"https:\/\/cloudinary.com\/documentation\/cloudinary_sdks\">SDK<\/a>). These libraries wrap Cloudinary\u2019s REST APIs and add useful helper methods.<\/li>\n<\/ul>\n<p>In this example, we\u2019ll use the REST API available to perform the request directly from the browser.<\/p>\n<h3>Account Details<\/h3>\n<p>Please be logged into the Cloudinary platform, and have the following data ready to use in the source code:<\/p>\n<ol>\n<li>The <strong>Cloud Name<\/strong>. This can be found under the <code>Dashboard &gt; Account Details<\/code> menu.<\/li>\n<li>The <strong>Upload Preset<\/strong>. To get that value go to the <code>Settings &gt; Upload &gt; Upload presets<\/code> menu and you\u2019ll see the option \u201cEnable unsigned uploading\u201d enabled by default, as the next screenshot shows:<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1616805872\/e-60354e32ba3535006813ce00\/uh4qjhbqhppgp9j7oeed.png\" alt=\"Default Upload Preset Configuration\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1860\" height=\"1078\"\/><\/p>\n<p>This means the platform expects parameters for authenticated requests by default. However, you can enable <strong>Unsigned Uploading<\/strong> requests after a click on \u201cEnable unsigned uploading\u201d link, and then you\u2019ll have a new <strong>Upload Preset<\/strong> with the <em>Mode<\/em> set as <strong>Unsigned<\/strong>. Take note of the preset name since we\u2019ll use that value later.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1616805966\/e-60354e32ba3535006813ce00\/wdawdrehsjlggnqyvepm.png\" alt=\"Unsigned Uploading Enabled\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"828\"\/><\/p>\n<h2>Project Setup<\/h2>\n<h3>Prerequisites<\/h3>\n<p>You\u2019ll need to have the following tools installed in your local environment:<\/p>\n<ul>\n<li>I recommed having the latest LTS version of <a href=\"https:\/\/nodejs.org\/en\/about\/releases\/\">Node.js<\/a> installed<\/li>\n<li>Either NPM or Yarn as a package manager<\/li>\n<li>The <a href=\"https:\/\/cli.angular.io\/\">Angular CLI<\/a> tool (Command-line interface for Angular)<\/li>\n<\/ul>\n<p>Also, make sure you have your Cloudinary account ready to use. You must verify your email if it\u2019s your first time using the platform.<\/p>\n<h3>Initialize the Project<\/h3>\n<p>Let\u2019s create a <em>clone<\/em>, or download the project seed before adding any changes in the source code:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">git <span class=\"hljs-keyword\">clone<\/span> https:<span class=\"hljs-comment\">\/\/github.com\/luixaviles\/angular-upload-images-demo.git<\/span>\ncd angular-upload-images-demo\/\ngit checkout -b <span class=\"hljs-number\">01<\/span>-drag-drop tags\/<span class=\"hljs-number\">01<\/span>-drag-drop\nnpm install \n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the previous step, you will download a local copy of the project, and create a new branch <code>01-drag-drop<\/code> in preparation for the following steps.<\/p>\n<h3>Source Code Files<\/h3>\n<p>Pay attention to the current project structure since it comes with a set of files and default configurations for Angular and TypeScript. Open the <code>angular-upload-images-demo<\/code> folder in your favorite code editor.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">|- angular-upload-images-demo\/\n    |- src\/\n        |- app\/\n            |- app.module.ts\n            |- app.component.ts\n            |- directives\/\n                |- image-uploader.directive.ts\n            |- model\/\n                |- image-file.ts\n<\/code><\/span><\/pre>\n<p>Please take a look at these files and identify the purpose of them: Application Module, Angular Component, Angular Directive, and the Model.<\/p>\n<h2>Implementation<\/h2>\n<h3>Creating the Model<\/h3>\n<p>Every time you upload an asset over the Cloudinary Platform through the REST API, the response comes as a JSON object with several details related to it. Find an example below:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n  <span class=\"hljs-attr\">\"asset_id\"<\/span>: <span class=\"hljs-string\">\"7a6d4839fe25a041ac1a9cbe8e62097c\"<\/span>,\n  <span class=\"hljs-attr\">\"public_id\"<\/span>: <span class=\"hljs-string\">\"smvwr6wvuanpo0mhfhvb\"<\/span>,\n  <span class=\"hljs-attr\">\"version\"<\/span>: <span class=\"hljs-number\">1616802885<\/span>,\n  <span class=\"hljs-attr\">\"version_id\"<\/span>: <span class=\"hljs-string\">\"b171b2b766188643ea8d20fd1ecff304\"<\/span>,\n  <span class=\"hljs-attr\">\"signature\"<\/span>: <span class=\"hljs-string\">\"802a90cec7e5feb54473828f753f20ec2b4ca1b8\"<\/span>,\n  <span class=\"hljs-attr\">\"width\"<\/span>: <span class=\"hljs-number\">250<\/span>,\n  <span class=\"hljs-attr\">\"height\"<\/span>: <span class=\"hljs-number\">250<\/span>,\n  <span class=\"hljs-attr\">\"format\"<\/span>: <span class=\"hljs-string\">\"png\"<\/span>,\n  <span class=\"hljs-attr\">\"resource_type\"<\/span>: <span class=\"hljs-string\">\"image\"<\/span>,\n  <span class=\"hljs-attr\">\"created_at\"<\/span>: <span class=\"hljs-string\">\"2021-03-26T23:54:45Z\"<\/span>,\n  <span class=\"hljs-attr\">\"tags\"<\/span>: &#91;],\n  <span class=\"hljs-attr\">\"bytes\"<\/span>: <span class=\"hljs-number\">2385<\/span>,\n  <span class=\"hljs-attr\">\"type\"<\/span>: <span class=\"hljs-string\">\"upload\"<\/span>,\n  <span class=\"hljs-attr\">\"etag\"<\/span>: <span class=\"hljs-string\">\"9db278d630f5fabd8e7ba16c2e329a3a\"<\/span>,\n  <span class=\"hljs-attr\">\"placeholder\"<\/span>: <span class=\"hljs-literal\">false<\/span>,\n  <span class=\"hljs-attr\">\"url\"<\/span>: <span class=\"hljs-string\">\"http:\/\/res.cloudinary.com\/luixaviles\/image\/upload\/v1616802885\/smvwr6wvuanpo0mhfhvb.png\"<\/span>,\n  <span class=\"hljs-attr\">\"secure_url\"<\/span>: <span class=\"hljs-string\">\"https:\/\/res.cloudinary.com\/luixaviles\/image\/upload\/v1616802885\/smvwr6wvuanpo0mhfhvb.png\"<\/span>,\n  <span class=\"hljs-attr\">\"original_filename\"<\/span>: <span class=\"hljs-string\">\"angular (1)\"<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Let\u2019s focus on some relevant properties only to create a TypeScript interface and model this response:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">ng generate <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">model<\/span>\/<span class=\"hljs-title\">cloudinary<\/span>-<span class=\"hljs-title\">asset<\/span>\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This command will create the <code>cloudinary-asset.ts<\/code> file where we can add a couple of attributes and types as the below code snippet shows.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ cloudinary-asset.ts<\/span>\n\nexport <span class=\"hljs-class\"><span class=\"hljs-keyword\">interface<\/span> <span class=\"hljs-title\">CloudinaryAsset<\/span> <\/span>{\n    asset_id: string;\n    url: string;\n    width: number;\n    height: number;\n    format: string;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h3>Creating the Angular Service<\/h3>\n<p>Since we have the Angular component already, we may think to send the request from there. However, as best practice, we should create an Angular Service instead to encapsulate all the business logic related to the client-server communication (HTTP request\/response processing).<\/p>\n<p>Let\u2019s create it using the Angular CLI command <code>ng generate service<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">ng generate service services\/image-uploader\n<\/code><\/span><\/pre>\n<p>This command will create the <code>image-uploader.service.ts<\/code> file under a new folder <code>services<\/code>. Next, let\u2019s update the auto-generated content.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ image-uploader.service.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { HttpClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@angular\/common\/http'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Injectable } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@angular\/core'<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> uploadUrl = <span class=\"hljs-string\">'https:\/\/api.cloudinary.com\/v1_1\/&lt;cloud-name&gt;\/image\/upload'<\/span>;\n<span class=\"hljs-keyword\">const<\/span> uploadPreset = <span class=\"hljs-string\">'s55foqri'<\/span>;\n\n@Injectable({\n  <span class=\"hljs-attr\">providedIn<\/span>: <span class=\"hljs-string\">'root'<\/span>,\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ImageUploaderService<\/span> <\/span>{\n  <span class=\"hljs-keyword\">constructor<\/span>(private httpClient: HttpClient) {}\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Let\u2019s understand what\u2019s happening there.<\/p>\n<ul>\n<li>The <code>uploadUrl<\/code> value is set using the <strong>Cloud Name<\/strong> which is assigned to your account.<\/li>\n<li>The <code>uploadPreset<\/code> value is set with the name of your <strong>Upload Preset<\/strong> set as \u201cUnsigned\u201d.<\/li>\n<li>The <code>HttpClient<\/code> gets injected into the Angular Service.<\/li>\n<\/ul>\n<p>In general, the <code>uploadUrl<\/code> value can be configured as:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">https:\/\/api.cloudinary.com\/v1_1\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloud_name<\/span>&gt;<\/span>\/<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">resource_type<\/span>&gt;<\/span>\/upload\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The <code>cloud_name<\/code> is the name of your Cloudinary account and the <code>resource_type<\/code> can be: <code>image<\/code>, <code>raw<\/code>, <code>video<\/code> or <code>auto<\/code>. You can find more details about these configurations <a href=\"https:\/\/cloudinary.com\/documentation\/upload_images#uploading_with_a_direct_call_to_the_rest_api\">here<\/a>.<\/p>\n<p>Since the <code>HttpClient<\/code> has been injected already, don\u2019t forget to import the <code>HttpClientModule<\/code> in the <code>app.module.ts<\/code> file:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ app.module.ts<\/span>\n\n<span class=\"hljs-comment\">\/\/ ... other imports<\/span>\n<span class=\"hljs-keyword\">import<\/span> { HttpClientModule } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@angular\/common\/http\"<\/span>;\n\n@NgModule({\n  <span class=\"hljs-attr\">declarations<\/span>: &#91;\n   <span class=\"hljs-comment\">\/\/ ... declarations<\/span>\n  ],\n  <span class=\"hljs-attr\">imports<\/span>: &#91;\n    BrowserModule,\n    AppRoutingModule,\n    HttpClientModule\n  ],\n  <span class=\"hljs-attr\">providers<\/span>: &#91;],\n  <span class=\"hljs-attr\">bootstrap<\/span>: &#91;AppComponent]\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AppModule<\/span> <\/span>{ }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Next, we\u2019ll need to define a method to be able to process incoming files and send them to the cloud. But first, let\u2019s create a method to process a single file.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ image-uploader.service.ts<\/span>\n\n<span class=\"hljs-comment\">\/\/ ... other imports<\/span>\n<span class=\"hljs-keyword\">const<\/span> uploadUrl = <span class=\"hljs-string\">'https:\/\/api.cloudinary.com\/v1_1\/luixaviles\/image\/upload'<\/span>;\n<span class=\"hljs-keyword\">const<\/span> uploadPreset = <span class=\"hljs-string\">'s44foqri'<\/span>;\n\n@Injectable({\n  <span class=\"hljs-attr\">providedIn<\/span>: <span class=\"hljs-string\">'root'<\/span>,\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ImageUploaderService<\/span> <\/span>{\n  \n  <span class=\"hljs-keyword\">constructor<\/span>(private httpClient: HttpClient) {}\n\n  private getFormData(file: File): FormData {\n    <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n    formData.append(<span class=\"hljs-string\">'file'<\/span>, file);\n    formData.append(<span class=\"hljs-string\">'upload_preset'<\/span>, uploadPreset);\n    <span class=\"hljs-keyword\">return<\/span> formData;\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The <code>getFormData()<\/code> method takes a <code>File<\/code> object as an input and returns a <code>FormData<\/code> object.<\/p>\n<ul>\n<li>\n<code>FormData<\/code> is an interface to construct an object using key\/value pairs. This object can be easily sent using the <code>XMLHttpRequest.send()<\/code> or even <code>fetch()<\/code>.<\/li>\n<li>The required parameters for <strong>unauthenticated requests<\/strong> are <code>file<\/code> and <code>upload_preset<\/code> only. Find more information about them <a href=\"https:\/\/cloudinary.com\/documentation\/upload_images#uploading_with_a_direct_call_to_the_rest_api\">here<\/a>.<\/li>\n<\/ul>\n<p>It\u2019s time to implement the main function to process all the image files.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ image-uploader.service.ts<\/span>\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n@Injectable({\n  <span class=\"hljs-attr\">providedIn<\/span>: <span class=\"hljs-string\">'root'<\/span>,\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ImageUploaderService<\/span> <\/span>{\n\n <span class=\"hljs-comment\">\/\/ ...constructor  <\/span>\n\n uploadImages(imageFiles: ImageFile&#91;]): Observable&lt;CloudinaryAsset&#91;]&gt; {\n    <span class=\"hljs-keyword\">const<\/span> files = imageFiles.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">imageFile<\/span>) =&gt;<\/span> imageFile.file);\n    <span class=\"hljs-keyword\">const<\/span> files$ = <span class=\"hljs-keyword\">from<\/span>(files);\n    <span class=\"hljs-keyword\">return<\/span> files$.pipe(\n      map(<span class=\"hljs-function\">(<span class=\"hljs-params\">file<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">this<\/span>.getFormData(file)),\n      mergeMap(<span class=\"hljs-function\">(<span class=\"hljs-params\">formData<\/span>) =&gt;<\/span>\n        <span class=\"hljs-keyword\">this<\/span>.httpClient.post&lt;CloudinaryAsset&gt;(uploadUrl, formData)\n      ),\n      toArray()\n    );\n  }\n\n  <span class=\"hljs-comment\">\/\/ ... private method<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Let\u2019s describe what\u2019s happening in this code snippet.<\/p>\n<ul>\n<li>The <code>uploadImages()<\/code> method will receive a set of <code>ImageFile<\/code> objects and is expected to return a set of <code>CloudinaryAsset<\/code>(model defined above) objects as an <em>Observable<\/em>.<\/li>\n<li>The <code>imageFiles.map()<\/code> operation makes sure to \u201cextract\u201d the <code>File<\/code> objects only.<\/li>\n<li>The <code>files$ = from(files)<\/code> instruction says we\u2019ll take the <code>files<\/code> array as an input to create an <em>Observable<\/em>. Next, we\u2019ll use a couple of RxJS operators to process each file arriving in the data stream.<\/li>\n<li>The <code>map((file) =&gt; this.getFormData(file))<\/code> operation says we\u2019ll \u201cmap\u201d a <code>file<\/code> to a <code>FormData<\/code> object.<\/li>\n<li>The <code>mergeMap()<\/code> operator takes the previous <code>FormData<\/code> object to send the HTTP request: <code>POST<\/code> using the URL and the <code>formData<\/code> as a payload. The result will be an <code>Observable&lt;CloudinaryAsset&gt;<\/code>.<\/li>\n<li>Finally, the <code>toArray()<\/code> operator \u201ccollects\u201d all previous results to emit all of them as a single array. This happens when the stream has been finished (All files have been uploaded).<\/li>\n<\/ul>\n<h3>Updating the Angular Component<\/h3>\n<p>Since the Business Logic has been updated, and we have an <em>Angular Service<\/em> ready to be injected, we\u2019ll need to update the Angular Component, which displays the dropbox, and also renders the uploaded images.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ app.component.ts<\/span>\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AppComponent<\/span> <\/span>{\n  <span class=\"hljs-attr\">imageFiles$<\/span>: Observable&lt;CloudinaryAsset&#91;]&gt;;\n\n  <span class=\"hljs-keyword\">constructor<\/span>(private imageUploaderService: ImageUploaderService) {\n\n  }\n\n  onDropFiles(imageFiles: ImageFile&#91;]): <span class=\"hljs-keyword\">void<\/span> {\n    <span class=\"hljs-keyword\">this<\/span>.imageFiles$ = <span class=\"hljs-keyword\">this<\/span>.imageUploaderService.uploadImages(imageFiles);\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The logic around this component involves:<\/p>\n<ul>\n<li>\n<code>imageFile$<\/code> replaces the previous <code>Array<\/code>. It\u2019s now an <em>Observable<\/em> that will \u201cemit\u201d a set of <code>CloudinaryAsset<\/code> objects.<\/li>\n<li>The <code>ImageUploaderService<\/code> gets injected using the <code>constructor<\/code>. This means we\u2019ll have an instance of the service ready to be used.<\/li>\n<li>The <code>onDropFiles<\/code> method invokes the Angular Service method and expects to get an <em>Observable<\/em>.<\/li>\n<\/ul>\n<p>However, the template still expects an <code>Array<\/code>, and it\u2019s not ready to process the Observable. Let\u2019s fix that by opening the same <code>app.component.ts<\/code> file:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n      <span class=\"hljs-comment\">&lt;!--Previous code not changes at all--&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"row\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span>\n          *<span class=\"hljs-attr\">ngFor<\/span>=<span class=\"hljs-string\">\"let file of imageFiles$ | async\"<\/span>\n          &#91;<span class=\"hljs-attr\">href<\/span>]=<span class=\"hljs-string\">\"file.url\"<\/span>\n          <span class=\"hljs-attr\">target<\/span>=<span class=\"hljs-string\">\"_blank\"<\/span>\n        &gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> &#91;<span class=\"hljs-attr\">src<\/span>]=<span class=\"hljs-string\">\"file.url\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The main change here is the use of the <code>async<\/code> pipe, which is required to <strong>subscribe<\/strong> to the <code>imageFiles$<\/code> Observable.<\/p>\n<p>The template will render the image after the upload process is complete. Also, you can click over any image to open it in a separate tab\/window using the original URL provided by Cloudinary in the response.<\/p>\n<h2>Live Demo<\/h2>\n<p>Find the source code available in <a href=\"https:\/\/github.com\/luixaviles\/angular-upload-images-demo\">GitHub<\/a>.<\/p>\n<p>If you prefer, you can play around with the project in <a href=\"https:\/\/codesandbox.io\/s\/angular-upload-images-cloudinary-ylojh\">CodeSandbox &#8211; Uploading Images to Cloudinary with Angular<\/a> too:<\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/angular-upload-images-cloudinary-ylojh?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"Angular Upload Images to Cloudinary\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n  <div class=\"wp-block-cloudinary-markdown \"><h2>Conclusion<\/h2>\n<p>Cloudinary offers a practical way to upload our assets, using different methods and APIs.<\/p>\n<p>The solution implemented here doesn\u2019t involve any server-side code, and that\u2019s the reason why we\u2019re creating an <em>Unsigned Upload Preset<\/em>.  In other words, if you need to manage authenticated requests, you\u2019ll need to have at least one server endpoint to <a href=\"https:\/\/cloudinary.com\/documentation\/upload_images#generating_authentication_signatures\">generate a signature<\/a>, using your <strong>API secret<\/strong> (Dashboard &gt; Account Details). Be sure to never expose your API secret in the client-side code!<\/p>\n<hr \/>\n<p>Feel free to reach out on <a href=\"https:\/\/twitter.com\/luixaviles\">Twitter<\/a> if you have any questions. Follow me on <a href=\"https:\/\/github.com\/luixaviles\">GitHub<\/a> to see more about my work.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[391,134,370,371,373],"class_list":["post-27839","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-angular","tag-guest-post","tag-image","tag-under-review","tag-upload"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How to upload images in Angular with a Drag &amp; Drop component - Part II<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to upload images in Angular with a Drag &amp; Drop component - Part II\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-04-07T19:18:15+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-02T16:54:42+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"How to upload images in Angular with a Drag &amp; Drop component &#8211; Part II\",\"datePublished\":\"2021-04-07T19:18:15+00:00\",\"dateModified\":\"2025-03-02T16:54:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/\"},\"wordCount\":15,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"keywords\":[\"Angular\",\"Guest Post\",\"Image\",\"Under Review\",\"Upload\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2021\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/\",\"name\":\"How to upload images in Angular with a Drag &amp; Drop component - Part II\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"datePublished\":\"2021-04-07T19:18:15+00:00\",\"dateModified\":\"2025-03-02T16:54:42+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to upload images in Angular with a Drag &amp; Drop component &#8211; Part II\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to upload images in Angular with a Drag &amp; Drop component - Part II","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/","og_locale":"en_US","og_type":"article","og_title":"How to upload images in Angular with a Drag &amp; Drop component - Part II","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/","og_site_name":"Cloudinary Blog","article_published_time":"2021-04-07T19:18:15+00:00","article_modified_time":"2025-03-02T16:54:42+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/"},"author":{"name":"","@id":""},"headline":"How to upload images in Angular with a Drag &amp; Drop component &#8211; Part II","datePublished":"2021-04-07T19:18:15+00:00","dateModified":"2025-03-02T16:54:42+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/"},"wordCount":15,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"keywords":["Angular","Guest Post","Image","Under Review","Upload"],"inLanguage":"en-US","copyrightYear":"2021","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/","name":"How to upload images in Angular with a Drag &amp; Drop component - Part II","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"datePublished":"2021-04-07T19:18:15+00:00","dateModified":"2025-03-02T16:54:42+00:00","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component-part-ii\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to upload images in Angular with a Drag &amp; Drop component &#8211; Part II"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=27839"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27839\/revisions"}],"predecessor-version":[{"id":37074,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27839\/revisions\/37074"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=27839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=27839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=27839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}