{"id":28358,"date":"2021-04-07T20:24:03","date_gmt":"2021-04-07T20:24:03","guid":{"rendered":"http:\/\/How-to-upload-images-in-Angular-with-a-Drag-and-Drop-component"},"modified":"2025-02-08T15:30:45","modified_gmt":"2025-02-08T23:30:45","slug":"how-to-upload-images-in-angular-with-a-drag-and-drop-component","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/","title":{"rendered":"How to upload images in Angular with a Drag &amp; Drop component"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>There is no doubt that we\u2019re living in an era in which images and videos are an important part of modern web applications. For example, it is very common to find a requirement to upload images for a photo gallery, or to upload a photo for your profile.<\/p>\n<p>In this <em>MediaJam<\/em>, we\u2019ll use the Angular framework, and a TypeScript-oriented solution, to create a component with the capability to upload image files from your computer from scratch.<\/p>\n<h2>The HTML Drag and Drop API<\/h2>\n<p>There is an interesting <a href=\"https:\/\/www.w3.org\/TR\/2010\/WD-html5-20101019\/dnd.html\">specification<\/a> about the Drag and drop behavior. This has been implemented in modern browsers as part of the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\">Web APIs<\/a>, available today.<\/p>\n<p>This API allows us to use JavaScript to handle the drag-and-drop features in browsers. In terms of the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTML_Drag_and_Drop_API\">MDN documentation<\/a>:<\/p>\n<blockquote>\n<p>The user may select draggable elements with a mouse, drag those elements to a droppable element, and drop them by releasing the mouse button. A translucent representation of the draggable elements follows the pointer during the drag operation.<\/p>\n<\/blockquote>\n<p>The good news about this API is the availability of several event types, which are going to be fired, and we can take control over them. Also, the API defines useful Interfaces such as <code>DragEvent<\/code>, <code>DataTransfer<\/code>, and others which we\u2019ll see later.<\/p>\n<h2>The Problem<\/h2>\n<p>Let\u2019s suppose you\u2019re building a web application that needs to upload multiple image files. A <em>drop-box<\/em> area should be defined so that users can drag and drop the files over it. Then, the application should display a preview section with all the images uploaded. Finally, the user can add images anytime.<\/p>\n<p>To clarify the idea, see the next screenshot.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1616288715\/e-60354e32ba3535006813ce00\/afj8hoslzqu7xmn3gfwd.jpg\" alt=\"A Drag and Drop Component to upload Images - Mockup\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1678\" height=\"1088\"\/><\/p>\n<p>Please note that this <em>MediaJam<\/em> is meant to define the frontend logic to meet these requirements. At least for now, we will not transfer the image files to any external server. All of them will be processed and rendered in the browser only.<\/p>\n<h2>The Solution<\/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>The latest LTS version of <a href=\"https:\/\/nodejs.org\/en\/about\/releases\/\">Node.js<\/a> version available is recommended.<\/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<h3>Initialize the Project<\/h3>\n<p>Let\u2019s create a small project from scratch using the <a href=\"https:\/\/cli.angular.io\/\">Angular CLI<\/a> tool.<\/p>\n<p>You can create an initial project using the <code>ng new<\/code> tool. By default, it will ask some parameters while you\u2019re creating it. However, you have the option to configure them in a single command line as you will see below.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">ng <span class=\"hljs-keyword\">new<\/span> angular-upload-images-demo --routing --prefix corp --style css --minimal\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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>This command will initialize a base project using some configuration options:<\/p>\n<ul>\n<li>\n<code>--routing<\/code>. It will create a routing module.<\/li>\n<li>\n<code>--prefix corp<\/code>. It defines a prefix to be applied to the selectors for created components(<code>corp<\/code> in this case). The default value is <code>app<\/code>.<\/li>\n<li>\n<code>--style css<\/code>. The file extension for the styling files.<\/li>\n<li>\n<code>--minimal<\/code>. Creates the project without any testing framework. Useful when you\u2019re working on a proof-of-concept project, for example.<\/li>\n<\/ul>\n<p>The output of the previous command will be:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">CREATE angular-upload-images-demo\/README.md (1032 bytes)\nCREATE angular-upload-images-demo\/.gitignore (631 bytes)\nCREATE angular-upload-images-demo\/angular.json (3157 bytes)\nCREATE angular-upload-images-demo\/package.json (774 bytes)\nCREATE angular-upload-images-demo\/tsconfig.json (538 bytes)\nCREATE angular-upload-images-demo\/.browserslistrc (703 bytes)\nCREATE angular-upload-images-demo\/tsconfig.app.json (287 bytes)\nCREATE angular-upload-images-demo\/src\/favicon.ico (948 bytes)\nCREATE angular-upload-images-demo\/src\/index.html (311 bytes)\nCREATE angular-upload-images-demo\/src\/main.ts (372 bytes)\nCREATE angular-upload-images-demo\/src\/polyfills.ts (2830 bytes)\nCREATE angular-upload-images-demo\/src\/styles.css (80 bytes)\nCREATE angular-upload-images-demo\/src\/assets\/.gitkeep (0 bytes)\nCREATE angular-upload-images-demo\/src\/environments\/environment.prod.ts (51 bytes)\nCREATE angular-upload-images-demo\/src\/environments\/environment.ts (662 bytes)\nCREATE angular-upload-images-demo\/src\/app\/app-routing.module.ts (245 bytes)\nCREATE angular-upload-images-demo\/src\/app\/app.module.ts (393 bytes)\nCREATE angular-upload-images-demo\/src\/app\/app.component.ts (1502 bytes)\n\u2714 Packages installed successfully.\n    Successfully initialized git.\n<\/code><\/span><\/pre>\n<p>If you pay attention to the generated files and directories, you\u2019ll see a minimal project structure for the source code too:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">|- src\/\n    |- app\/\n        |- app.module.ts\n        |- app-routing.module.ts\n        |- app.component.ts\n<\/code><\/span><\/pre>\n<p>On the other hand, the <code>app.component.ts<\/code> file will define the template, and the styles as part of the <a href=\"https:\/\/angular.io\/guide\/component-styles#styles-in-component-metadata\">metadata<\/a>.<\/p>\n<p>Also, it\u2019s good to remind you that the styles defined there will be applied only to that component, and they are not inherited by any nested component.<\/p>\n<h3>Creating the Model<\/h3>\n<p>Let\u2019s create a model to represent an image file. Use the command <code>ng generate interface<\/code> to create it as follows:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" 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\">image<\/span>-<span class=\"hljs-title\">file<\/span>\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><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>Then, update the content of the <code>image-file.ts<\/code> file with a TypeScript interface:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">export<\/span> <span class=\"hljs-selector-tag\">interface<\/span> <span class=\"hljs-selector-tag\">ImageFile<\/span> {\n  <span class=\"hljs-attribute\">file<\/span>: File;\n  <span class=\"hljs-attribute\">url<\/span>: string;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h3>Creating the Angular Directive<\/h3>\n<p>Since we\u2019ll need control over a \u201cDrop Box\u201d element defined in the DOM. It would be a good idea to implement an <a href=\"https:\/\/angular.io\/guide\/attribute-directives#attribute-directives\">attribute directive<\/a> first.<\/p>\n<p>With that attribute directive, you can change the background color and control the behavior over the box.<\/p>\n<p>So, let\u2019s create it using the CLI command <code>ng generate directive<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">ng generate directive directives\/image-uploader\n<\/code><\/span><\/pre>\n<p>This command will create the <code>image-uploader.directive.ts<\/code> file under a new folder <code>directives<\/code>. Next, let\u2019s change the auto-generated content with the following code snippet.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> {\n  Directive,\n  HostBinding,\n  HostListener,\n  Output,\n  EventEmitter,\n} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@angular\/core'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { ImageFile } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'..\/model\/image-file'<\/span>;\n\n@Directive({\n  <span class=\"hljs-attr\">selector<\/span>: <span class=\"hljs-string\">'&#91;corpImgUpload]'<\/span>,\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ImageUploaderDirective<\/span> <\/span>{\n  @Output() dropFiles: EventEmitter&lt;ImageFile&#91;]&gt; = <span class=\"hljs-keyword\">new<\/span> EventEmitter();\n  @HostBinding(<span class=\"hljs-string\">'style.background'<\/span>) backgroundColor;\n}\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\">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>There are a couple of notes to cover, related to the previous code:<\/p>\n<ul>\n<li>The <code>@Directive()<\/code> decorator\u2019s configuration specifies the attribute <code>[corpImgUpload]<\/code>. That means we can apply it later as <code>&lt;div corpImgUpload&gt;&lt;\/div&gt;<\/code> to \u201cdraw\u201d the drop box, and receive the files later.<\/li>\n<li>The <code>@Output()<\/code> decorator defines the <code>dropFiles<\/code> property to be able to have an <em>event binding<\/em> through it. This can be important to process the \u201coutput\u201d of this directive, which is a set of image files.\n<ul>\n<li>Please note the <code>EventEmitter<\/code> expects to <em>emit<\/em> an array of objects: <code>ImageFile[]<\/code>. The <code>ImageFile<\/code> was defined above as the model.<\/li>\n<\/ul>\n<\/li>\n<li>The <code>@HostBinding<\/code> decorator set the styles to the host element of the directive.\n<ul>\n<li>For example, if the directive is applied to a <code>&lt;div&gt;<\/code> element. Then the <code>HostBinding<\/code> will be equivalent to do: <code>&lt;div [style.background]=&quot;backgroundColor&quot;&gt;&lt;\/div&gt;<\/code>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Instead of assigning a set of <em>hard-coded<\/em> values for the background color, we can create a TypeScript enum to help. You can create it in the same file of the directive since it will be used in that context only:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ image-uploader.directive.ts<\/span>\n\nenum DropColor {\n  <span class=\"hljs-keyword\">Default<\/span> = <span class=\"hljs-string\">'#C6E4F1'<\/span>, <span class=\"hljs-comment\">\/\/ Default color<\/span>\n  Over = <span class=\"hljs-string\">'#ACADAD'<\/span>, <span class=\"hljs-comment\">\/\/ Color to be used once the file is \"over\" the drop box<\/span>\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\">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>All right! We\u2019re ready to use the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/HTML_Drag_and_Drop_API#drag_events\">Drag Events<\/a> in the directive implementation. Let\u2019s start with the <code>dragover<\/code> and <code>dragleave<\/code> events:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ image-uploader.directive.ts<\/span>\n\n<span class=\"hljs-comment\">\/\/ ... imports <\/span>\n@Directive({\n  selector: <span class=\"hljs-string\">'&#91;corpImgUpload]'<\/span>,\n})\nexport <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ImageUploaderDirective<\/span> <\/span>{\n  @Output() dropFiles: EventEmitter&lt;ImageFile&#91;]&gt; = <span class=\"hljs-keyword\">new<\/span> EventEmitter();\n  @HostBinding(<span class=\"hljs-string\">'style.background'<\/span>) backgroundColor = DropColor.<span class=\"hljs-keyword\">Default<\/span>;\n\n  @HostListener(<span class=\"hljs-string\">'dragover'<\/span>, &#91;<span class=\"hljs-string\">'$event'<\/span>]) <span class=\"hljs-keyword\">public<\/span> dragOver(event: DragEvent) {\n    event.preventDefault();\n    event.stopPropagation();\n    this.backgroundColor = DropColor.Over;\n  }\n\n  @HostListener(<span class=\"hljs-string\">'dragleave'<\/span>, &#91;<span class=\"hljs-string\">'$event'<\/span>]) <span class=\"hljs-keyword\">public<\/span> dragLeave(event: DragEvent) {\n    event.preventDefault();\n    event.stopPropagation();\n    this.backgroundColor = DropColor.<span class=\"hljs-keyword\">Default<\/span>;\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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>Let\u2019s explain what\u2019s happening in that code snippet:<\/p>\n<ul>\n<li>The <code>backgroundColor<\/code> attribute gets initialized with a default color (using the brand new enum).<\/li>\n<li>A method <code>dragOver<\/code> has been added.\n<ul>\n<li>The parameter matches with the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/DragEvent\">DragEvent<\/a> interface, which represents a drag-and-drop interaction.<\/li>\n<li>To \u201clisten\u201d to the DOM event, the <code>@HostListener<\/code> decorator has been used and the <code>'dragover'<\/code> string matches with the event name of the API.<\/li>\n<li>Finally, the background color gets updated with a different color.<\/li>\n<\/ul>\n<\/li>\n<li>A method <code>dragLeave<\/code> has been added with the same configuration as the previous method.\n<ul>\n<li>The only difference here is the background color change with the default value again.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Now, it\u2019s time to define the main method of the directive, which allows handling the <code>'drop'<\/code> event.<\/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\">\/\/ image-uploader.directive.ts<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ImageUploaderDirective<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ attributes and other methods...<\/span>\n\n  @HostListener(<span class=\"hljs-string\">'drop'<\/span>, &#91;<span class=\"hljs-string\">'$event'<\/span>]) public drop(event: DragEvent) {\n    event.preventDefault();\n    event.stopPropagation();\n    <span class=\"hljs-keyword\">this<\/span>.backgroundColor = DropColor.Default;\n\n    <span class=\"hljs-keyword\">let<\/span> fileList = event.dataTransfer.files;\n    <span class=\"hljs-keyword\">let<\/span> files: ImageFile&#91;] = &#91;];\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">let<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; fileList.length; i++) {\n      <span class=\"hljs-keyword\">const<\/span> file = fileList&#91;i];\n      <span class=\"hljs-keyword\">const<\/span> url = <span class=\"hljs-built_in\">window<\/span>.URL.createObjectURL(file);\n      files.push({ file, url });\n    }\n    <span class=\"hljs-keyword\">if<\/span> (files.length &gt; <span class=\"hljs-number\">0<\/span>) {\n      <span class=\"hljs-keyword\">this<\/span>.dropFiles.emit(files);\n    }\n  }\n}\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>So what\u2019s happening there? Let\u2019s take a closer look:<\/p>\n<ul>\n<li>Once the event is captured, again, the background color will be changed to the default one.<\/li>\n<li>The <code>event<\/code> object now should have the details about the files that have been \u201cdropped\u201d over the component.\n<ul>\n<li>The <code>event.dataTransfer<\/code> represents a <code>DataTransfer<\/code> interface, which refers to the data that is transferred during a drag and drop interaction.<\/li>\n<li>Then, <code>event.dataTransfer.files<\/code> represents a list of the files that have been transferred.<\/li>\n<li>For every transferred file, the method is creating an <code>ImageFile<\/code> object (It contains the file itself, and an Object URL to be used to render the image).<\/li>\n<\/ul>\n<\/li>\n<li>If there\u2019s at least one file transferred, then the <code>dropFiles<\/code> will emit the data.<\/li>\n<\/ul>\n<p>The directive is ready to be integrated with the app. Let\u2019s see how we can use it inside an Angular component next.<\/p>\n<h3>Creating the Wrapper Component<\/h3>\n<p>Let\u2019s update the <code>app.component.ts<\/code> file with the following template content:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" 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-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\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"drop-box\"<\/span> <span class=\"hljs-attr\">corpImgUpload<\/span> (<span class=\"hljs-attr\">dropFiles<\/span>)=<span class=\"hljs-string\">\"onDropFiles($event)\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"message\"<\/span>&gt;<\/span>Drop File Images Here<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/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    <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\">img<\/span> *<span class=\"hljs-attr\">ngFor<\/span>=<span class=\"hljs-string\">\"let file of files\"<\/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\">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-8\"><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 above code snippet defines the layout of the component.<\/p>\n<ul>\n<li>The first row will display the drop box and applies the directive as an attribute (<code>&lt;div corpImgUPload&gt;<\/code>). At the same time, it creates an <em>event binding<\/em> to \u201ccapture\u201d the incoming files (<code>&lt;div corpImgUpload (dropFiles)=&quot;onDropFiles($event)&quot;&gt;<\/code>).<\/li>\n<li>The second row will render every image through the <code>*ngFor<\/code>, which is a structural directive in Angular.<\/li>\n<\/ul>\n<p>To have the template ready, it would be useful to define some styles:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\">  <span class=\"hljs-selector-class\">.container<\/span> {\n    <span class=\"hljs-attribute\">display<\/span>: flex;\n    <span class=\"hljs-attribute\">flex-direction<\/span>: column;\n  }\n\n  <span class=\"hljs-selector-class\">.drop-box<\/span> {\n    <span class=\"hljs-attribute\">min-height<\/span>: <span class=\"hljs-number\">300px<\/span>;\n    <span class=\"hljs-attribute\">min-width<\/span>: <span class=\"hljs-number\">300px<\/span>;\n    <span class=\"hljs-attribute\">display<\/span>: table;\n    <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#c6e4f1<\/span>;\n    <span class=\"hljs-attribute\">border<\/span>: solid <span class=\"hljs-number\">1px<\/span> <span class=\"hljs-number\">#75c5e7<\/span>;\n  }\n\n  <span class=\"hljs-selector-class\">.row<\/span> {\n    <span class=\"hljs-attribute\">display<\/span>: flex;\n    <span class=\"hljs-attribute\">flex-direction<\/span>: row;\n    <span class=\"hljs-attribute\">align-items<\/span>: center;\n    <span class=\"hljs-attribute\">justify-content<\/span>: center;\n  }\n\n  <span class=\"hljs-selector-class\">.message<\/span> {\n    <span class=\"hljs-attribute\">display<\/span>: table-cell;\n    <span class=\"hljs-attribute\">text-align<\/span>: center;\n    <span class=\"hljs-attribute\">vertical-align<\/span>: middle;\n    <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#686868<\/span>;\n  }\n\n  <span class=\"hljs-selector-tag\">img<\/span> {\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">200px<\/span>;\n    <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">200px<\/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\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Remember, both the <code>template<\/code> and the <code>styles<\/code> are defined inline in the <code>app.component.ts<\/code> file.<\/p>\n<p>Finally, let\u2019s complete the component with the missing method and property.<\/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<span class=\"hljs-keyword\">import<\/span> { Component } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@angular\/core'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { ImageFile } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'.\/model\/image-file'<\/span>;\n\n@Component({\n  <span class=\"hljs-attr\">selector<\/span>: <span class=\"hljs-string\">'corp-root'<\/span>,\n  <span class=\"hljs-attr\">template<\/span>: <span class=\"hljs-string\">`\n    \/\/ Template go here\n  `<\/span>,\n  <span class=\"hljs-attr\">styles<\/span>: &#91;\n    <span class=\"hljs-string\">`\n      \/\/ Styles go here\n    `<\/span>,\n  ],\n})\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\">files<\/span>: ImageFile&#91;] = &#91;];\n\n  onDropFiles(files: ImageFile&#91;]): <span class=\"hljs-keyword\">void<\/span> {\n    <span class=\"hljs-keyword\">this<\/span>.files = &#91;...this.files, ...files];\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 previous code snippet shows the <code>onDropFiles<\/code> method, which receives the transferred images as objects(<code>ImageFile<\/code> interface) and the app takes the new values to be added to the existing <code>files<\/code> array.<\/p>\n<h2>Fixing the Unsafe URL Values<\/h2>\n<p>If you give it a try with the current implementation, you\u2019ll see the images you drop in the application are not being rendered by the browser, and the browser\u2019s console will display a couple of errors:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">WARNING: sanitizing unsafe URL value blob:http:<span class=\"hljs-comment\">\/\/localhost:4200\/a1bcee8c-4833-4578-a470-41669838efeb (see https:\/\/g.co\/ng\/security#xss)<\/span>\nunsafe:blob:http:<span class=\"hljs-comment\">\/\/localhost:4200\/<\/span>\nGET unsafe:blob:http:<span class=\"hljs-comment\">\/\/localhost:4200\/a1bcee8c-4833-4578-a470-41669838efeb net::ERR_UNKNOWN_URL_SCHEME<\/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\">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>You can see them in the screenshot below too.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1616289010\/e-60354e32ba3535006813ce00\/jhninvlnrpheck9toa3z.png\" alt=\"Unsafe Blob Error - Screenshot\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"754\"\/><\/p>\n<p>To prevent these errors, and ensure the image rendering, let\u2019s update the <code>image-uploader.directive.ts<\/code> file.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" 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.directive.ts<\/span>\n\n<span class=\"hljs-comment\">\/\/ Other imports...<\/span>\n<span class=\"hljs-keyword\">import<\/span> { DomSanitizer } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@angular\/platform-browser'<\/span>;\n\n@Directive({\n  <span class=\"hljs-attr\">selector<\/span>: <span class=\"hljs-string\">'&#91;corpImgUpload]'<\/span>,\n})\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ImageUploaderDirective<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ attributes...<\/span>\n\n  <span class=\"hljs-keyword\">constructor<\/span>(private sanitizer: DomSanitizer) {}\n\n  <span class=\"hljs-comment\">\/\/ Other event handlers...<\/span>\n\n  @HostListener(<span class=\"hljs-string\">'drop'<\/span>, &#91;<span class=\"hljs-string\">'$event'<\/span>]) public drop(event: DragEvent) {\n    <span class=\"hljs-comment\">\/\/ No changes here...<\/span>\n\n    <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">let<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; fileList.length; i++) {\n      <span class=\"hljs-keyword\">const<\/span> file = fileList&#91;i];\n      <span class=\"hljs-keyword\">const<\/span> url = <span class=\"hljs-keyword\">this<\/span>.sanitizer.bypassSecurityTrustUrl(\n        <span class=\"hljs-built_in\">window<\/span>.URL.createObjectURL(file)\n      );\n      files.push({ file, url });\n    }\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><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 take a closer look at the previous code snippet.<\/p>\n<ul>\n<li>The <code>DomSanitizer<\/code> has been injected through the constructor.\n<ul>\n<li>This class helps to prevent Cross-Site Scripting Security(XSS) bugs. More details can be found <a href=\"https:\/\/angular.io\/api\/platform-browser\/DomSanitizer\">here<\/a>.<\/li>\n<\/ul>\n<\/li>\n<li>On other hand, the URL value we had before as a <code>string<\/code> is processed by the <code>bypassSecureityTrusUrl<\/code> function.\n<ul>\n<li>This method bypasses security and trusts the given value to be a safe URL.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Now, the URL is not a <code>string<\/code> anymore. Instead, it has the <code>SafeUrl<\/code> as the type, and that means the <code>ImageFile<\/code> interface needs to be updated as follows.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ image-file.ts<\/span>\n<span class=\"hljs-keyword\">import<\/span> { SafeUrl } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@angular\/platform-browser'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> interface ImageFile {\n  <span class=\"hljs-attr\">file<\/span>: File;\n  url: SafeUrl;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><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>Try the source code again using the <code>ng serve -o<\/code> command, and the images will be rendered after dropping them in the app!<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1616289098\/e-60354e32ba3535006813ce00\/yv1cbpo0l59ubdr98mu0.png\" alt=\"The App rendering the uploaded images\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1864\" height=\"1310\"\/><\/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-demo-3dlhh\">CodeSandbox<\/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-demo-3dlhh?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 Demo\"\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 \"><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-28358","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<\/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\/\" \/>\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\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-04-07T20:24:03+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-08T23:30:45+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\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"How to upload images in Angular with a Drag &amp; Drop component\",\"datePublished\":\"2021-04-07T20:24:03+00:00\",\"dateModified\":\"2025-02-08T23:30:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/\"},\"wordCount\":12,\"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\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/\",\"name\":\"How to upload images in Angular with a Drag &amp; Drop component\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"datePublished\":\"2021-04-07T20:24:03+00:00\",\"dateModified\":\"2025-02-08T23:30:45+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/#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\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/#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\"}]},{\"@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","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\/","og_locale":"en_US","og_type":"article","og_title":"How to upload images in Angular with a Drag &amp; Drop component","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/","og_site_name":"Cloudinary Blog","article_published_time":"2021-04-07T20:24:03+00:00","article_modified_time":"2025-02-08T23:30:45+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\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/"},"author":{"name":"","@id":""},"headline":"How to upload images in Angular with a Drag &amp; Drop component","datePublished":"2021-04-07T20:24:03+00:00","dateModified":"2025-02-08T23:30:45+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/"},"wordCount":12,"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\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/","name":"How to upload images in Angular with a Drag &amp; Drop component","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"datePublished":"2021-04-07T20:24:03+00:00","dateModified":"2025-02-08T23:30:45+00:00","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/#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\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-upload-images-in-angular-with-a-drag-and-drop-component\/#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"}]},{"@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\/28358","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=28358"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28358\/revisions"}],"predecessor-version":[{"id":36737,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28358\/revisions\/36737"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28358"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28358"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28358"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}