<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cezar Pleșcan</title>
    <description>The latest articles on DEV Community by Cezar Pleșcan (@cezar-plescan).</description>
    <link>https://dev.to/cezar-plescan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1054232%2F0a499636-cc3f-4fb4-bdec-03440e4ae94d.jpg</url>
      <title>DEV Community: Cezar Pleșcan</title>
      <link>https://dev.to/cezar-plescan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cezar-plescan"/>
    <language>en</language>
    <item>
      <title>My Journey in Software Development</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Sun, 29 Sep 2024 13:32:20 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/my-journey-in-software-development-9j3</link>
      <guid>https://dev.to/cezar-plescan/my-journey-in-software-development-9j3</guid>
      <description>&lt;h2&gt;
  
  
  Early days as a programmer
&lt;/h2&gt;

&lt;p&gt;The first instruction I wrote on a computer was the CIRCLE command from the BASIC language. I was fascinated to see the circle drawn on the display after executing the program. I experimented further with drawing lines, rectangles and other figures. But I fancied some animation, I wanted those figures to move on the screen. My parents borrowed an informatics book and I started to simply copy and paste the implementation of some exercises. At that moment I realized that programming wasn’t just about drawing figures on the screen but also implied more complex logic and algorithms. I discovered IF, FOR, WHILE and other statements. &lt;/p&gt;

&lt;p&gt;I became attracted to computers and pursued the mathematics-informatics specialization in high school. We learned Pascal, C and C++, which is the foundation for many other programming languages. We were also taught more advanced and interesting topics like algorithms, data structures, object oriented programming, classes, pointers.&lt;/p&gt;

&lt;p&gt;One homework assignment involved drawing some buttons that could perform some actions. I paid particular attention to crafting the visuals, especially the clicked state. Another homework involved creating movable figures. While others used keyboard commands to select and define new coordinates, I was determined to make them draggable by mouse, a more intuitive and practical approach. I was passionate about getting that homework done, it was one of the most complex I did at that time. Beyond these exercises, I also developed a C++ version of the classic snake game from Nokia phones. &lt;/p&gt;

&lt;p&gt;My final high school project for graduation was a two-player card game. There, I first encountered Internet Explorer and JavaScript. I chose to develop my project in a browser because it offered an easier way to manipulate the cards on the screen, compared to a C++ desktop application. Apart from displaying the cards, I've also programmed the computer to play against it. Once again, I focused heavily on visuals and on instructing the computer to play as better as possible, as they brought me immense satisfaction. Creating practical and useful applications became a core principle for me. &lt;/p&gt;

&lt;p&gt;At the faculty, I continued my journey in Computer Science. The most challenging project was to build a physical device with a photodiode capable of tracking a light source. For that I needed a microcontroller and various electronic components. I had to write the entire source code in the assembler language, which offered me the lowest possible level to interact with the microcontroller, unlike higher-level languages. I had to control every instruction and bit of information, there were no operations executed under the hood. I worked on that project for about six months and I managed to complete it and make it work as I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery of web development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  First paid project
&lt;/h3&gt;

&lt;p&gt;There were colleagues discussing PHP, but it didn’t raise my interest initially. A close friend approached me about upgrading a Wordpress website he’d previously worked on. I wasn’t initially familiar with PHP (the language used by Wordpress), but I remembered when some colleagues from the faculty were discussing it and that raised my interest and decided to embrace the challenge. This project opened my doors to web development. &lt;/p&gt;

&lt;p&gt;The existing website was a presentation of fitness products and articles. The owner wanted to add an online sales feature for fitness magazines. The payment process involved sending an SMS to a payment provider, followed by their service accessing a link on our website to verify the payment success. Having studied MySQL during the faculty, I was able to integrate it for data management, like user registration, login, and keeping track of the bought magazines. &lt;/p&gt;

&lt;p&gt;We successfully implemented the new feature, which allowed the website owner to start selling fitness magazines online. This marked my first paid project creating software.&lt;/p&gt;

&lt;h3&gt;
  
  
  The first personal web project
&lt;/h3&gt;

&lt;p&gt;Realizing the benefit of creating web software, I began to learn more about PHP, Zend framework, JavaScript, jQuery, HTML, and CSS. One of the challenges in JS was to write a more structured code, since the language didn’t provide classes like PHP or C++. Initially I used plain objects to group related methods and properties, to have a way of working closer to the OOP principles. &lt;/p&gt;

&lt;p&gt;When I started to use jQuery, I was curious to see how the source code was written. What impressed me was the way the code was structured, how the data was encapsulated, and a mechanism for defining private and public-like methods. I can say that jQuery was a turning point in developing my JS knowledge.&lt;/p&gt;

&lt;p&gt;Even from that time, I believed the best way to learn was through practice, not just theory. A real-world problem I identified was the lengthy and error-prone process homeowners’ associations faced when managing invoices.&lt;/p&gt;

&lt;p&gt;I began to sketch a solution for that problem. Initially I didn’t know what to start with, then I asked myself what were the main functionalities I needed to cover. I realized the process wasn’t simple and straightforward. If someone would have told me to solve an already defined problem, I could have it done, but I had to first define the problem, which proved to be challenging. The tools I used were a pen, a notebook, and my imagination. Writing down any ideas I had helped me to better visualize and clarify the requirements.&lt;/p&gt;

&lt;p&gt;I then set out to create my own library with components, like form submission and validation, error display, and field specific validation rules for entering invoice data. My goal was to create a single-page application using Ajax requests for a more user-friendly experience, avoiding traditional page reloads.&lt;/p&gt;

&lt;p&gt;My focus was on code structure too. This proved challenging, especially considering that it involved abstraction and experience, neither of which were heavily emphasized in school. There, unfortunately, we were asked to solve specific problems without delving into the broader applicability of the solutions.&lt;/p&gt;

&lt;p&gt;Many times, I found myself caught up in implementation details, which were beneficial for my technical growth, but didn’t bring any valuable progress to the project outcome. A crucial step I discovered was taking a step back and clearly defining the desired outcome and functionalities. I had some appointments with a few administrators of homeowners’ associations to understand the real needs and the time consuming activities, so my project would be valuable to them.&lt;/p&gt;

&lt;p&gt;The project spanned several months. While I made progress, I eventually acknowledged that a clear vision for the product was lacking. However, this wasn’t wasted time. Experimenting on this project equipped me with valuable knowledge. Furthermore, I learned the limitations of working solo and the importance of collaboration and seeking guidance from experienced developers. &lt;/p&gt;

&lt;h3&gt;
  
  
  Beginning of my web career
&lt;/h3&gt;

&lt;p&gt;I decided to enter the web development field, and I started looking for job opportunities. During an interview, the manager was impressed by my solid experience and keen attention to details when I presented my previous project to him. I got the job as a full-stack developer, working with PHP for the backend.&lt;/p&gt;

&lt;p&gt;I joined a team of five full-stack developers, and project manager who was responsible, amongst others, with task definition, organization, and assignment. We utilized agile methodology and Jira for task management, both new concepts for me. I was impressed by our team's organized approach. We openly debated technical topics, and I enjoyed working in this collaborative atmosphere. My initial tasks involved creating algorithms to detect different website types, such as e-commerce, forums, educational platforms, and government websites. &lt;/p&gt;

&lt;p&gt;I improved my PHP knowledge, gained experience working with databases, task organization and prioritization. I learned that prioritizing was paramount because we had a time frame in which we needed to deliver the software. I grasped the core concept of agile methodologies: incremental development, continuous feedback gathering, and process improvement. While not fully comprehending the power of agile frameworks like Scrum at that time, I adhered to the established practices. &lt;/p&gt;

&lt;p&gt;Another project I worked on was a social media platform for sports players. Players could register profiles, share posts, receive comments, and exchange messages with users. I continued working as a full stack developer, being part of another team ranging from two to four developers. A significant accomplishment for me was independently implementing the entire messaging system, designing and developing both the user interface for composing and displaying messages and the backend functionality for handling message communication and database updates. Taking charge of integrating Facebook helped me feel more confident in my ability to handle tasks on my own and needed less help from others.&lt;/p&gt;

&lt;p&gt;Another feature I developed was an infinite carousel to display various images. The carousel needed to work both on desktop and mobile so it was the first time when I had to write JavaScript code that had to be mobile compatible. I also needed to ensure compatibility across different browsers like Safari, Opera, Firefox, Internet Explorer (Google Chrome wasn’t as popular yet).&lt;/p&gt;

&lt;p&gt;As I saw the increasing complexity of web application, I realized the code needed to be organized in a better way, so I thought about modularization, utilizing IIEF blocks to define and separate different application logic and components. Even if our large JavaScript files only partially adhered to the single responsibility principle, the code functioned well, but that wasn’t the only goal to achieve. However, it lacked clarity for new team members, requiring additional guidance to understand existing code.&lt;/p&gt;

&lt;h3&gt;
  
  
  The first large-scale single-page application
&lt;/h3&gt;

&lt;p&gt;Let me tell you about a project that was a real game changer for me. I had the opportunity to  build a web-based version of an existing complicated desktop app. I was assigned the full responsibility of implementing it from scratch and almost couldn’t believe that I got such a chance!&lt;/p&gt;

&lt;p&gt;I went back to my personal old project to get some inspiration from there, and thought again about how to structure the application, what were the main abstract parts of it and how they had to interact. The main role of an HTML page was to present content, not to interact with it. I had to come up with a structure that would help me to easily build a web application that would resemble a desktop app look and feel.&lt;/p&gt;

&lt;p&gt;At that time jQuery was very popular and I used it too. It was quite straightforward to define event handlers, or to show and hide specific elements in the page. Being obsessed with the idea of working with abstractions, a pattern I first noticed was that specific actions were tied to specific elements only, and other parts of the webpage were influenced by the results of some of those operations. I tried to visualize the web page being composed of black boxes, each having inputs and outputs, being inspired by how electrical circuits systems work. Those black boxes could be composed from others as well and they could communicate with others. OK, so I had an abstract concept, but how could I apply it for a web page? The immediate connection I made was to associate an HTML element with its specific logic and treat it as a black box, or to visualize it as an object from the OOP context. If I needed some information connected to an HTML element, I would have used the Object, not the element itself, more specifically to call some methods on it. The idea of being able to use OOP inside a web page was a revelation at that time and I couldn't have imagined something better!&lt;/p&gt;

&lt;p&gt;JS didn’t have native support for classes, but I found a very nice implementation of them from John Resig, the author of jQuery. Every class was defined by specifying its prototype as an object of functions, and had the possibility to be extended as well. But what could those black boxes I’ve talked earlier about actually represent? As I’ve mentioned, HTML elements and their associated logic were a specific type of classes (or black boxes), which I named ‘Container’, equivalent to today’s Component from Angular. To create an instance, I needed to pass the jQuery element and eventually a set of options to the class constructor. Those options could include functions that were executed when internal events were triggered; the functions had to start with ‘on’ prefix; now they can be considered an equivalent of the Angular event binding. &lt;/p&gt;

&lt;p&gt;Another crucial concept I introduced was about data handling: how it was retrieved from the server, how it was updated, and how to trigger those actions. This part wasn’t related at all to any HTML content, so I had to introduce new types of classes to separate this concern: Resource, Request, Command. &lt;/p&gt;

&lt;p&gt;The Resource class represented a specific piece of data that implemented the Observer pattern: other objects could register handlers for specific data events, and when those events occurred, the Resource instance would trigger the registered handlers. I have to mention that at that time I was unaware of the Observer design pattern, which I read about later. A special derived class was ApiResource, which provided a mechanism to communicate with the backend and update its internal data.&lt;/p&gt;

&lt;p&gt;The Request class was responsible for the actual request to the backend. It also checked if the server returned an authentication error and also sent a secret token to prevent CSRF attacks. As may you expect, the ApiResource class used this class for communication with the server.&lt;/p&gt;

&lt;p&gt;The Command and CommandManager classes were a way to define and trigger global actions (similar to today’s NgRx actions).&lt;/p&gt;

&lt;h3&gt;
  
  
  Emergence of various frontend tools
&lt;/h3&gt;

&lt;p&gt;The JS code was split between multiple files, most of them using IIFE to avoid exposing the variables to the global namespace. The HTML files were generated by PHP in Zend Framework. It was clear that the JS files were not so easy to manage, for example, when I needed to load the files in a specific order, I had to use numbers as their prefixes. Later on, minification tools appeared, like JSMin and I created a PHP script to combine these multiple JS files and then minify them into a single one, which reduced the page load time.&lt;/p&gt;

&lt;p&gt;The introduction of Node.js and NPM changed the frontend development and I found myself confused about what tools or libraries to use. After days of researching I finally figured out how to set up and properly use CommonJS modules and Browserify to bundle the JS files into a single one. I had to adapt to the new technologies and standards, which wasn’t so easy because I got used to my own mindset of writing code. This was a challenge for me, but I had no alternatives and I continued to study, practice, and learn. &lt;/p&gt;

&lt;p&gt;One of the coolest things I found was the Backbone.js framework, which had similar concepts to the library I had developed, but it looked cleaner and more structured. I had the chance to use it on a real large-scale project, which also used npm private packages for separating shared components, styles, or logic. I witnessed how the web applications complexity started to grow, and the need for specialized tools or libraries became obvious. &lt;/p&gt;

&lt;p&gt;Those internal packages were restricted from being written, with the exception of the frontend lead developers. Proving my skills in writing good code, I was granted access to push code into those repositories and also to review and approve any pull requests. &lt;/p&gt;

&lt;p&gt;Backbone.js also used tempting engines, like Mustache, Handlebars, or Pug (formerly known as Jade), which separated the display logic from data. I found this approach very useful, I always loved when things could be decoupled and had well defined responsibilities. Later on, I acknowledged that this concept was named “separation of concerns” and it has been one of the main pillars of software design. &lt;/p&gt;

&lt;p&gt;Frontend tools started to emerge and evolve. It was challenging for me to visualize the benefits of them and to use them properly. Sometimes I asked myself “Do we really need it?”, as long as we had the workflow in place and the team was used to the established procedures. I realized that development was not only about writing code, but also about working with tools that automated some processes, in order to avoid any human unintended errors.&lt;/p&gt;

&lt;p&gt;With the release of ES6, many developers wanted to adopt the new JS features, but the browsers didn’t fully support the new standard, so we used Babel to write ES6 code and transpile it to the ES5 version. This meant that another process needed to be run, so automating them was really necessary. At that time we used Grunt to run all the processes, including live reloading in the browser, which was really a very cool thing to have.&lt;/p&gt;

&lt;p&gt;These build tools changed the way the web development was done. One noticeable thing I faced was that the frontend became separated from the backend and most of the communication between them was done through API requests. The backend became unaware of how the frontend rendered the data, it just simply returned the requested data to the client. That implied that the frontend had to be served from a different server, named development server, mostly provided by BrowserSync or Webpack dev server. But where was the actual backend server with its database? The answer was offered by Docker. &lt;/p&gt;

&lt;p&gt;This was the moment when I made the transition from being a full-stack developer to frontend development only, being more attracted to the visual part of an application, but without underestimating the work on the backend. I had to face a lot of tools, the web industry changed compared to the times when I started to work on my first web projects. I have to admit now that at those times I felt a bit overwhelmed to use that multitude of tools around me. I had to learn and understand how each tool needed to be used, and I had to do that by myself, there were no mentors around to teach me. Was it easy? Not really. Did I survive? Yes, and now I can say that I made a good choice to be patient and not give up. As humans, we have a natural tendency to stay in our comfort zone and have a daily routine. The reality is that things around us are evolving and I chose to adapt to changes, even if that implied some effort. What is the consequence in the present? That I’m open to new things and challenges and I really like them. It brings me satisfaction to face and deal with something new, and I’m not talking about programming only. I do encourage you to try new things you have in mind, even if your first thoughts are against doing it. Of course, you’d need to carefully weigh the benefits and consequences of your actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Angular Journey
&lt;/h3&gt;

&lt;p&gt;Back to my frontend career, the tools I mentioned earlier quickly evolved and so I discovered the Angular v2 framework. I played around with its “Heroes tutorial”, and I was excited to work on a real project with this new framework. The component based architecture with data binding and event handling really caught my interest, there were principles similar to what I had used in my own library. The way of encapsulating the view and the logic into a single entity was something I could have only dreamt about in the past. &lt;/p&gt;

&lt;p&gt;Everything seemed straightforward when reading the documentation and the examples, but when I had to implement things that weren’t so common, I found myself a bit lost. I knew what I had to do, but couldn’t easily translate that into Angular language. A lot of things happened under the hood in Angular and I didn’t have enough knowledge to understand everything at once there. &lt;/p&gt;

&lt;p&gt;I then discovered the change detection mechanism, which was a powerful feature, but with its own caveats. The view was automatically updated when a component property changed, that was very good, but there were times when things didn’t go as expected.&lt;/p&gt;

&lt;p&gt;Then I encountered injectable services but couldn’t figure out where was the “new” keyword used, how was the class instantiated. I was used to writing code from scratch and having more flexibility in handling the logic, but Angular was like saying: “don’t worry about the internal workings, I’ll handle it for you”. I felt that Angular put some boundaries around me and couldn’t customize the implementation as much as I preferred, which was initially frustrating, but then realized that the role of a framework was to build an app on top of it, using its rules.&lt;/p&gt;

&lt;p&gt;It was another moment when I had to adapt, and mostly to a new programming paradigm, that is, declarative programming. I came from an imperative programming world and had to learn a new way of writing code, basically it was like learning a new programming language, a new instruction set. Of course, I had to embrace this shift, there were a lot of benefits by using the framework. Angular became popular and clients wanted to adopt a standardized framework, and it was for an understandable reason: other developers that would work on the project had to follow a specific set of rules and it would be easier for them to adapt, without too much training from existing developers with more experience in the project. &lt;/p&gt;

&lt;p&gt;More than this, there were times when a certain thing could be implemented in different ways. So, which one to choose? That was another uncertainty. Personally, I wanted to have one way of doing something and stick to it. But I wasn’t so lucky. I didn’t know if I implemented it in a good or bad way. I’ve always preferred to understand the reasoning behind the rules and practices, not just blindly follow them.&lt;/p&gt;

&lt;p&gt;Taming Angular sometimes felt like a constant struggle, especially with the module system. Why should I declare so many imports and exports in a module? Why do I even need a module? I couldn’t find the answers to these questions until Angular introduced the standalone components in v14, which brought much needed relief! &lt;/p&gt;

&lt;h4&gt;
  
  
  Change detection
&lt;/h4&gt;

&lt;p&gt;The interpolation syntax in the template looked similar to the Mustache template engine I had used before. If a property changed after a certain event, it was automatically updated in the view, without any manual intervention, a great feature we all appreciated. However, this came with a hidden cost: the entire application view was re-rendered, and if there were any unrelated functions anywhere in the template, they were executed again, even if we didn’t ask for that. For example, when I defined a click handler on a button, every time I clicked it, the entire view was recomputed, even if my button belonged to a small component. The initial excitement seemed like it faded away; why were other parts of the view executing when they had nothing to do with my button click? Things started to be more clear when I read and experimented with how the change detection mechanism worked, about the OnPush detection strategy and the ChangeDetectorRef provider with its methods detectChanges and markForCheck.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dependency Injection
&lt;/h4&gt;

&lt;p&gt;Initially this mechanism seemed very abstract and hard to grasp. When I needed a service, I had to declare a parameter with the type of that service class in the constructor of the component, and then I could have access to the service instance. It looked simple to use, but how were the things built under the hood? Later I realized that every component had an injector which could provide different services. These injectors were organized in a similar manner like the component tree, in a hierarchical way. When a service was requested, the injector started to search on the component equivalent node, then it went way up in the hierarchy. This meant that different components could request the same provider (basically the same provider token), but they could get different service instances. Evrika! A similar concept exists in React, but they declare the providers directly in the views. Compared to the change detection, I find the dependency injection mechanism a very clever one and I give all the credits to the ones that designed it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Routing
&lt;/h4&gt;

&lt;p&gt;The library I had built included the concept of pages, and only one page could be visible at a time. Opening a page was done by calling a globally accessible method, specifying the name of the page and optionally a set of parameters. Comparing my basic routing mechanism to Angular’s one, the similarities are the name of the page (or of the route), the component associated with the route, and passing optional parameters. Angular introduced more advanced features, like updating the URL in the browser when the route changes, listening to URL changes, lazy loading a route, and many more. Again, it mostly uses declarative programming, hiding the implementation details from the developer.&lt;/p&gt;

&lt;p&gt;One of the unexpected things I encountered was that Angular destroys the components after navigating to another route. That was really strange for me in the beginning, my own implementation of pages simply hid the components in the DOM, and then showed them back; but Angular had a different approach. It was annoying to see that the previously fetched data was gone after returning to the earlier route. This opened the gate to understanding state management solutions and how to persist data in a component after it is destroyed. This also implied extracting data handling logic from the components and putting it into separate services, and later the component would just retrieve the data from those services. This approach is what’s called separation of concerns, which is considered a good programming practice. &lt;/p&gt;

&lt;h4&gt;
  
  
  Reactive forms
&lt;/h4&gt;

&lt;p&gt;When I started working with Angular Reactive Forms I noticed some high-level similarities with the form handling from the PHP Zend Framework: associating validators to a field or multiple fields, or reading and displaying errors. I was pleased with the fact that form fields in Angular were conceptualized as abstract objects.&lt;/p&gt;

&lt;p&gt;A challenge I faced was to efficiently display the errors. For that I created a custom directive to which I could pass the field errors object. The reason I implemented such a directive was to write less code in the templates, knowing that the traditional way implied many lines for displaying different field errors. I have always been tempted to find a solution when I see repeating code or code that follows a specific pattern.&lt;/p&gt;

&lt;p&gt;A very useful feature of forms and form fields has been the representation of change events as observables, which makes total sense and adheres to RxJs concepts.&lt;/p&gt;

&lt;h4&gt;
  
  
  RxJs
&lt;/h4&gt;

&lt;p&gt;The reactive programming introduced by RxJs can bring both satisfaction and headaches. I have to admit it can drastically shorten the amount of written code, but on the other side the learning curve has been very steep. Coming from a traditional imperative way of programming, where I had control of almost the entire code and I could easily debug and fix errors, when it came to using RxJs operators I really found myself a bit lost. It was again like learning a completely new and different language, with a new set of instructions. And indeed, these instructions are basically the operators and the building blocks of RxJs. What was a stream, how to visualize it, when to use it, these were questions that I struggled to find practical answers to. &lt;/p&gt;

&lt;p&gt;It was another challenge I had to deal with. I don’t know how it happened, but when I stopped struggling to understand the RxJs concepts, I had a moment when I visualized the observable streams as conveyor belts with different items, and that those items could jump to other conveyor belts, creating a connection and dependency between them. This visualization proved to be very useful and thus helped me to create observables from other ones and to understand what flattening operators do. &lt;/p&gt;

&lt;p&gt;The way I was initially performing an HTTP request when a button was clicked was to apply the traditional imperative way, that is, to define a handler for the click event and inside that method to subscribe to the HttpClient and then to assign the result to a component property. With the new approach I had, I visualized 3 connected streams: the click events, the HTTP requests, and the responses. So, the trigger of the final stream was the click event and the output was the data returned by the requests. This way I could also make use of the async pipe in the templates, without worrying about unsubscribing.&lt;/p&gt;

&lt;p&gt;Even now when I write these memories I keep thinking about this mental transition from imperative to reactive programming. I really find it useful and couldn’t imagine how it would be to use the imperative approach again when dealing with multiple interconnected streams. I had a feeling of pride and joyfulness after grasping this new philosophy of programming.&lt;/p&gt;

&lt;h4&gt;
  
  
  Signals
&lt;/h4&gt;

&lt;p&gt;I was curious when I heard the Angular community talking about signals. I have been impressed by this feature and I consider it a huge improvement introduced by the Angular team. I could easily visualize a signal as a piece of information that automatically informs the consumers when it changes and that it could depend on other signals, creating something like a dependency graph. Their usage is straightforward, there are some rules to follow though, and I see them as a very powerful feature and I’m sure that they’ll bring a positive impact on the Angular framework popularity. All my credits, again, for the Angular team for such a nice work!&lt;/p&gt;

&lt;h4&gt;
  
  
  Project architecture
&lt;/h4&gt;

&lt;p&gt;From my perspective, simply learning how to use some features is not enough for building an application. One of the first aspects I consider is the folder structure, how to organize the files inside the project. I prefer having separate main folders for: a) the core of the application, that includes the main component, routing, guards, interceptors, and other things that are specific to the application itself; b) the main features (or pages) of the application; c) shared or reusable blocks that are used by other entities. I consider the folder structure a crucial aspect of the project architecture, I visualize it like cabinets with drawers and I want to easily pick the tools I need to build the project.&lt;/p&gt;

&lt;p&gt;Nonetheless, another practice I have been adopting is the loose coupling and high cohesion. This practice came mostly from my own experience, rather than reading about it. I’ve developed it naturally, based on my desire to create self-contained independent modules, that have less dependency on other ones, being able to communicate using an interface, unaware of any implementation details. When I build an entity (that is, component, service, directive, pipe, and so on), I ask myself: What are the consequences if I completely remove it from the app, or if I change something in it or add something to it? How much does it affect the rest of the app? If I see that there is a significant impact, then I’ll refactor or restructure the code. My philosophy is to build code that won’t be affected by any future updates, or, at least, few changes will be needed to implement.&lt;/p&gt;

&lt;p&gt;I want to add a few words here about how I deal with state management. Even before Angular I was aware of the benefits of separating the data handling from the components themselves. But how far do I go with this separation? The next question will help with an answer: Does a specific piece of information belong to a certain component only? Can that information exist without that component? I have been using NgRx for implementing the state management and I avoid seeing things as black or white only, but adapting based on specific scenarios. So, my approach is the following: if any data can live independently of a component, I consider it part of the global state; otherwise, it is strictly part of that component itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Software design practices
&lt;/h2&gt;

&lt;p&gt;Even since I started to work on my first SPA I was focused on working in an OOP style and visualizing the UI as being composed as a hierarchy of building blocks. When I have a wireframe or prototype to implement, I first analyze what are the top main components. For this I take into account what parts have related functionalities, how they interact with each other, what type of information could be passed between them. I try to abstract out these blocks by temporarily ignoring visual presentational details and extract what is the purpose of those blocks. I’ve developed this technique through personal practice and later I realized I wasn’t the only one who had such a vision. I can say that there is not only a single solution for a problem, everyone could have their own approaches. I have to admit that I worked with senior developers who mostly had a procedural way of implementing the skeleton of an app (this was before Angular) and I was a bit surprised, I expected a more abstract and advanced approach from them. I prefer to see things neither good nor bad, but to be aware of and weigh the implications and consequences of choosing or designing a specific solution. &lt;/p&gt;

&lt;p&gt;A real turning point in my career was the discovery of the book Design patterns, by The Gang of Four. It came at the right moment, when I was in a search of developing abstract solutions. Even though the book described a number of 23 design patterns, I was mostly interested not to learn each of them, but to understand the idea, the principles that lie behind developing a pattern. The two invaluable principles that I was attracted to were: "Program to an interface, not an implementation", "Favor object composition over class inheritance". I allow myself to compare these statements with Einstein's equation: E=mc2. They all look very simple, yet they are very powerful. Of course, the literature includes other principles as well, like SOLID, DRY, KISS, or YAGNI, and they are valuable too. Without being initially aware of them, I’ve been applying them naturally, as they came as a consequence of my continuous search of building modular software. &lt;/p&gt;

&lt;p&gt;Talking about how I actually start writing the code to solve something new, most of the time my approach is from top to bottom, starting to define the main classes or functions, then how they should communicate. I look for giving proper semantic names to the entities, so the code is self explanatory as much as possible. For example, when I have a not so obvious condition in an if statement, I prefer to create a function or a variable for that expression, naming it according to its purpose. A rule of thumb is to group related code into separate functions or methods that would have the name as described above.&lt;/p&gt;

&lt;p&gt;One of the first questions I ask myself when working on a task is: what do I have to do? Then I translate that answer into a simple code, without writing any implementation details. After that I answer: How can I actually implement what I had just written? It looks pretty simple. But in practice it is not really as it looks on paper. I know myself struggling to solve something, trying to find solutions to a lot of things, thinking in advance that maybe I would need other things, and so on. I was basically missing to define what was the direction I had to go, to define the WHAT. Many times I started right away to write code, then to write more code, then to realize that I had no proper direction. I saw this behavior at other experienced developers too, so it wasn’t just me who faced it. My best tool to solve a problem is a pen and a paper; I have been writing tens of pages with sketches and notes, and I’ll continue to do this because it helps me to visualize the problem and structure the solution. I draw boxes representing different entities, then draw arrows to illustrate how those boxes communicate with each other. One of the first things we were taught at informatics was the flowchart diagram to describe the operations of an algorithm, and I still use it today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Presentations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Design patterns
&lt;/h3&gt;

&lt;p&gt;One day my manager asked me if I wanted to hold some presentations in front of an audience of around 10 colleagues, about design patterns. Even though I didn’t know much about the topic, nor had I held presentations (except for those when graduating the faculty), I accepted it on the spot. I was aware that I had to learn and prepare. And it wasn’t just one presentation, there were multiple sessions with different groups of people and different design patterns. Fortunately the content to be presented was already prepared and I just had to deliver it. But it wasn’t just reading from the PowerPoint slides, I had to also understand how those patterns work. The main sections of a presentation contained the problem, the solution, an UML diagram, a Java code example, use cases, pros &amp;amp; cons. I found it very well structured, so I didn’t have to change any content, but to familiarize myself with the concepts. &lt;/p&gt;

&lt;p&gt;Having all eyes in the room pointed at me when I started the first presentation made me freeze for a moment and forget everything I had to deliver. I felt very uncomfortable, but I managed to take a deep breath, drink a sip of water, and start talking about the topic. At the end I was relieved that the presentation had finished and all the colleagues had left the room. But it wasn’t completely over, I had to continue delivering other design patterns too. There’s no better way to learn something than actually doing it; reading from books is useful, but not enough. The best way to learn to swim is to jump into the water; I have no knowledge about someone who became a champion only by reading instructions or by watching how others did it. &lt;/p&gt;

&lt;p&gt;I have no words to describe how valuable was that experience: it helped me to reduce my anxiety when speaking in front of people; it helped me to understand how to structure a presentation about a technical topic; it reduced the development time for the project I worked on, it increased to code quality, and reduced the number of major or critical bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workshops
&lt;/h3&gt;

&lt;p&gt;Along my career I encountered problems that could be solved in different ways. I initiated a series of interactive workshops and encouraged the audience (usually no more than 6 people) to come up with their own solutions. We then discussed the benefits, disadvantages, followed by a presentation of my proposed solution. The idea was to involve my colleagues in the discussion too. I had situations when someone had a solution better than mine, so I find it very useful to create a debate, to have a two-way communication, not just to unidirectional deliver information. One of my favorite topics was “composition vs inheritance”. In one such session the challenge was to find ways to implement a reusable filtering component that could have multiple filters. That presentation was before the Angular era, so the solutions weren’t as standardized as nowadays.&lt;/p&gt;

&lt;p&gt;The fact that there was an open discussion during the presentations put me in situations where I had to actively listen to what my colleagues said and try to understand their ideas. I was also put in various communication contexts. For example, one time a colleague started to talk over me, even though I didn’t finish my sentence. What I have learned from these interactions was to listen to any idea, to ask for clarifications or how their solution would cover specific cases. Another lesson learned was that I could be wrong too, I make mistakes, I’m not perfect, but I’m open to admit that when it’s the case.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript training
&lt;/h3&gt;

&lt;p&gt;Other presentation sessions I held were about the JavaScript language for a group of about 20 people having other specializations. So I had to start with the basics, which, contrary to expectations, is not easy at all. The more experienced we become, the more we rely on advanced concepts; however, explaining something simple to beginners could be a challenging task. I realized that those “simple” things are connected to other “simple” things that I had to explain, and so on, so the initial explanation became quite a complex one. My approach was thus to start with the very basic things and gradually add new ones, so the audience would be focused and be able to keep track of the presented information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hackathon
&lt;/h3&gt;

&lt;p&gt;I’m smiling when I recall the moments from that event. I was part of a team with a project manager (who owned the idea of the project), a backend developer, a tester, and myself. The company organized a hackathon and six teams took part in the challenge. We went in the mountains to a chalet surrounded by woods and a very fresh air. Our project was a scheduler for meeting rooms, so everyone could easily see when a room is available. We were awarded the second prize, and later the project was physically implemented in the company, so each meeting room had a tablet near the door that displayed its availability. I used React for the frontend part; it was new for me at that time, but I learned it pretty fast and I was able to complete the project. I spent the entire night working and when I got to bed I could still see the code in my mind when I closed my eyes. I was tired, but it paid the price, we were all satisfied by the work and the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mentoring juniors
&lt;/h2&gt;

&lt;p&gt;For the first 3 years of my web development career I had been working either alone or with people having a relatively similar or more experience. I was able to work independently and get the tasks done without any additional support, so I gained trust from my manager that I can conduct a project. It was a time when the company I worked for organized an internship training, and I was asked to supervise the interns. Some of them were very friendly and showed their willingness to learn, and I was happy to share my knowledge with them. &lt;/p&gt;

&lt;p&gt;After a few weeks my manager told me that the other colleague from the team will be moved to another project and that the team will have 4 new members that had passed the internship. I felt honored, appreciated and happy for having this new responsibility. But this came with challenges too, as each of them had different knowledge levels, so I had to adapt and be patient with each of them. I started with assigning them low difficulty tasks, and, depending on their productivity, I later gave them more complex work. The main challenge I faced was to explain to them some custom mechanisms I had already implemented in the project. They weren’t used to abstract things, which was normal for their experience level. I thought about what could be an easy way for them to understand something new and abstract, and I came up with making analogies with situations or objects from real life, so they could better visualize the context. I tried to do my best to share my knowledge with them, but more important was their eagerness to learn new things. &lt;/p&gt;

&lt;p&gt;After some months I could notice different levels of productivity from each of them and it came the time when I was asked to pick the best two to be officially part of the team. It was a hard choice to make, because I would pick three of them, they proved to have good results and we also built a “friendly” relationship. I spent almost 2 years mentoring them and I’m grateful to them that they were patient with me too, we helped each other grow.&lt;/p&gt;

&lt;p&gt;Another experience I had for about one year was with a junior colleague in whom I saw real potential. He absorbed knowledge like a sponge, I could see him improving week by week. He was eager to listen and learn from me, and I had a pleasure working with him. One of the principles I wanted him to acknowledge was to define what needs to be done, then how it needs to be implemented; another good practice I taught him was to group related operations into separate methods, so the code could be cleaner and more structured. During our collaboration he was promoted to a mid-level, then, after he moved to another project, reached the senior level. Besides a productive professional collaboration we remained good friends and when we occasionally met he reminded me that my advice from that time was valuable to him and he could see over time the benefits of the practices I had taught him. I enjoy hearing such nice words from him.&lt;/p&gt;

&lt;p&gt;I also had other opportunities to work with juniors, and I’ve realized that everyone is different. Some of them asked questions and told me when they didn’t understand what I had explained to them, others just pretended to understand, and others were hesitating to ask for clarification. In such a relationship it is paramount to be honest and openly say when something is unclear. Teaching is, in my opinion, a two-way communication from the same level, the teacher shouldn’t make the learner feel inferior just because of the different amount of knowledge. &lt;/p&gt;

&lt;h2&gt;
  
  
  Agile methodology
&lt;/h2&gt;

&lt;p&gt;Writing code is just one part of the entire product development life cycle, which includes: planning, defining requirements, design, development, testing, deployment and maintenance. Apart from the development phase, I was curious about the early stages. A developer is more productive when they know clearly what needs to be done and also when their focus remains in the same area or related ones. The testing team has to have a clear understanding of how the product should work too. This means that the first two phases of SDLC need to be done with careful analysis. As I mentioned at the beginning of my story, I had moments when I worked on something, but didn’t have a clear picture of what needed to be done, in order to get quicker to a final working product. &lt;/p&gt;

&lt;p&gt;In the early stages of my professional career I had to personally create tasks based on design prototypes. Those tasks were effectively statements about what needed to be done from the developer’s perspective, like: display the links in the application header; show a dialog popup when I click the Cancel button, create the login endpoint, and so on. Of course, each task could be broken into smaller ones. This approach proved to work, until the testing team came into play. They wanted to know how a feature works, not how it is implemented, and such details weren’t quite available in the task definition. So I had to update the way of working, by also describing the order of certain operations and the expected output. &lt;/p&gt;

&lt;p&gt;Later on, I familiarized myself with Agile methodologies and some core concepts, like functional requirements, user stories, acceptance criteria, or use cases. Functional requirements provide a high-level overview of a product feature. On the other hand, user stories describe how a user interacts with the application. The acceptance criteria define some conditions that have to be met for a feature to be considered complete. A use case details a specific workflow in order to achieve a desired result. I found these concepts very useful and practical: I could more clearly visualize what needed to be done, how the application should behave, and also the testers could easily do their job. Even today these methods prove to be very useful and popular.&lt;/p&gt;

&lt;p&gt;Most of the projects I worked on followed the Agile methodologies and the Scrum framework. Daily meetings, planning, refinement, estimations, retrospectives - all these were activities that didn’t require writing code, but they were necessary to define and get the tasks done. The teams I was part of started to have a Project Manager, a Product Owner, or a Scrum Master. I became able to analyze the validity of the requirements, to estimate the effort, to clarify details with the client. I was also in the position of writing complete user stories with acceptance criteria based on the design prototypes and I really enjoyed that work. Moreover, I identified inter-dependencies between user stories. For many years, Jira has been the solution for task management. I try to make sure that the logged issues are well written in order to avoid bugs or misunderstandings and to deliver what the client expects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client interactions
&lt;/h2&gt;

&lt;p&gt;A job, at its core, means offering a service that meets someone’s needs. This implies that we have to make sure we understand what the client wants. This means communication, and the more direct it takes places, the easier it is for us to deliver what the client expects. In most of my outsourcing projects I worked on I had the privilege of direct interaction with the clients. When I say “client” I’m referring to individuals from outsourcing partner companies who have a deep understanding of the business needs and product requirements. This could include a product owner, a lead developer, or a business analyst. While I could implement something that makes technical sense, it should be valuable for the client too.&lt;/p&gt;

&lt;p&gt;In the early first projects I had senior colleagues who told me what to do, based on their discussion with the clients. I felt a bit uncomfortable and isolated, and I wanted to be involved in the communication, even though I knew that I didn’t have enough experience. But an opportunity arose when my manager asked me to speak with the director of the company we outsourced for, to clarify some requirements of the project. While on the phone, alone and unprepared, I became anxious, without anyone to support me. It was the first time when I had a direct conversation on such a high-level. My English was not so good at that time and during the conversation I was mostly quiet. But I also felt a bit empowered that I had that opportunity and I knew it wasn’t the last. &lt;/p&gt;

&lt;p&gt;The main challenge I’ve faced when discussing with a client is to extract the relevant information about what they actually want. Some of them prefer talking, while others prefer to write. For me it is more efficient and clear when I read because the information is most of the time well structured in the written form. However, I do understand that this requires some effort from the client, and that’s why others prefer to talk, and they will expect us to do the main work and extract the essential information. In situations when the client explains something, I try to rephrase that with my own words to check if I understood correctly. This has proven to be a useful technique and the client also sees my genuine interest which helps in maintaining a good professional relationship. &lt;/p&gt;

&lt;p&gt;There were moments when things appeared to be clear at the beginning, but later we realized that we didn’t know what to do because of missing requirements. In such situations, if we had potential solutions, we presented them to the client and discussed the best approach. In my opinion, this approach is preferable to simply waiting for the client to propose a solution. However, this is not a rule of thumb, it depends on the client's expectations, some of them prefer to have a lot of control, while others prefer us to be proactive and suggest solutions. Sometimes neither the client knows exactly what they want or they expect us to provide both the requirements and solutions. From my experience I can say that the key here is to adopt an open communication and clearly express our expectations on both sides in order to avoid misunderstandings.&lt;/p&gt;

&lt;p&gt;One of the most valuable discussions are demo presentations, where we walk to the client through our work. Many times I was responsible for the presentations and my approach was to start with a summary of the features I wanted to showcase. I then went with a happy flow scenario, eventually followed by an unhappy flow, trying to use realistic values for input data to simulate what a normal user would do. Most of the time the stakeholders are non-technical or less technical and I adapt my language so they could all understand. They are more interested in seeing their product meeting the requirements than to tell them we used specific design patterns or libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Journey of Learning
&lt;/h2&gt;

&lt;p&gt;During my career as a software engineer I faced challenges that shaped the way I am now. Beyond the technical expertise I also worked on improving my communications skills, and this growth has been incredibly rewarding. I have always been dedicated to creating and delivering valuable software, as if I make it for myself. Moreover, I’ve discovered a passion in sharing my knowledge by writing online tutorials that describe how to build solutions starting from realistic use cases. This journey is far from over. The world of software development is full of possibilities and I believe anyone with dedication and a passion for problem-solving can be part of it.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>career</category>
      <category>personaljourney</category>
      <category>programming</category>
    </item>
    <item>
      <title>From Requirements to Code: Implementing the Angular E-commerce Product Listing Page</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Sun, 14 Jul 2024 16:25:12 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/from-requirements-to-code-implementing-the-angular-e-commerce-product-listing-page-23nn</link>
      <guid>https://dev.to/cezar-plescan/from-requirements-to-code-implementing-the-angular-e-commerce-product-listing-page-23nn</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Welcome back to my series on building a scalable Angular e-commerce application! In the &lt;a href="https://dev.to/cezar-plescan/building-an-angular-e-commerce-app-a-step-by-step-guide-to-understanding-and-refining-requirements-hoe"&gt;previous article&lt;/a&gt;, I embarked on the crucial journey of understanding and refining the requirements for our Minimum Viable Product (MVP). Through collaborative discussions with stakeholders and careful analysis, we crafted a clear user story and acceptance criteria for the first feature: displaying a list of products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;In this article, I'll take the next crucial step: translating those requirements into tangible code. I'll delve into the initial implementation of the product listing page, exploring the thought process behind key decisions and showcasing practical techniques for creating a responsive and visually appealing layout.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'll cover
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Component Design&lt;/strong&gt;: Identifying the core components needed for the product list and establishing their relationships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Modeling and Mock Data&lt;/strong&gt;: Creating a data structure to represent our products and utilizing mock data to simulate a real-world scenario.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular Material Integration&lt;/strong&gt;: Leveraging the power of Angular Material components to create a visually appealing and user-friendly interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Responsive Grid Layout&lt;/strong&gt;: Implementing a flexible grid layout that adapts to different screen sizes, ensuring optimal display across devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spacing and Layout Refinements&lt;/strong&gt;: Exploring different techniques for achieving the desired spacing and visual balance between product cards.&lt;/p&gt;

&lt;p&gt;By the end of this article, you'll have a solid understanding of how to transform user stories and acceptance criteria into working Angular code, setting a strong foundation for building out the rest of our e-commerce application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Motivation
&lt;/h3&gt;

&lt;p&gt;My aim is to provide a practical, step-by-step guide that goes beyond theoretical concepts. I want to share my thought process as a developer, highlighting the decisions I make and the challenges I encounter along the way. By following along, you'll gain valuable insights into how to approach frontend development in a real-world scenario.&lt;/p&gt;

&lt;p&gt;The goal of this article is not just to show you &lt;em&gt;how&lt;/em&gt; to build a product list, but &lt;em&gt;why&lt;/em&gt; certain decisions are made, cultivating a deeper understanding of the architectural considerations involved in creating a successful e-commerce application.&lt;/p&gt;

&lt;p&gt;Whether you're a beginner or an experienced Angular developer, this article will equip you with the knowledge and skills to build the foundation of a successful e-commerce application.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Quick Recap of the User Story and Acceptance Criteria
&lt;/h3&gt;

&lt;p&gt;Let's revisit the refined user story and acceptance criteria that will guide the implementation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;User Story&lt;/strong&gt;&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As a potential customer, I want to see a clear and visually appealing list of products with their images, names, and prices, so that I can quickly browse and compare items before making a purchase decision.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Acceptance Criteria&lt;/strong&gt;:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Layout&lt;/em&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The product listing page displays products in a grid layout.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Each row of the grid contains a maximum of 4 product items.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The layout is responsive and adapts to different screen sizes.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Product Card Content:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Each product card displays a clear product image.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The product name is prominently displayed below the image.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The price is displayed clearly, using the appropriate currency symbol.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Functionality:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The product list is initially loaded when the user navigates to the product listing page.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;MVP Considerations:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The product list does not include pagination or infinite scrolling in this initial version.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Error handling for data fetching issues will be addressed in a later iteration.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Technical Notes:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Data Source: Product data will be initially hardcoded on the frontend.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;UI Framework: The Angular Material library will be used for styling and components.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  An overview of the path from requirements to code
&lt;/h3&gt;

&lt;p&gt;Now, it's time to translate these requirements into actual code. While there's no one-size-fits-all process, I'll share the high-level approach I typically take:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Project setup&lt;/strong&gt;: Create the Angular project and any essential initial configurations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component identification and hierarchy&lt;/strong&gt;: Identify the visual components needed for the product listing page and define their hierarchical relationships. This involves deciding which components will contain other components and how they will interact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data retrieval&lt;/strong&gt;: Determine the data source and how to fetch and manage the product data within the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout and styling&lt;/strong&gt;: Create the visual layout of the product list and style the individual product cards to meet the design requirements. I'll discuss this in more detail later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While these steps provide a solid starting point, it's important to remember that the development process is often iterative. We might discover new requirements, encounter unexpected challenges, or need to make adjustments based on user feedback. Therefore, it's crucial to maintain flexibility in our planning and be prepared to adapt as we go.&lt;/p&gt;

&lt;p&gt;In the next section, I'll delve deeper into each step of the implementation, starting with the identification of components and their hierarchical structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the component structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the Angular project
&lt;/h3&gt;

&lt;p&gt;Before identifying the component structure I need to create a new  project with the Angular CLI command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="nt"&gt;--package&lt;/span&gt; @angular/cli@18 ng new Angular-Architecture-Guide &lt;span class="nt"&gt;--defaults&lt;/span&gt; &lt;span class="nt"&gt;--standalone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;If you don't have npx installed, you can run:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; npx


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can also find the code for this project on Github at &lt;a href="https://github.com/cezar-plescan/Angular-Architecture-Guide/" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/Angular-Architecture-Guide/&lt;/a&gt;. The &lt;code&gt;master&lt;/code&gt; branch contains the initial project setup.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating the main components
&lt;/h3&gt;

&lt;p&gt;Analyzing the user story, I identify two main visual elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;the product list container&lt;/strong&gt;, a single element which holds all visible products.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;product elements&lt;/strong&gt;, each containing product details within the list container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I immediately notice that there's a parent-child component relationship between the list and each product. Here are the two components I'll create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ProductListComponent&lt;/code&gt;&lt;/strong&gt; - the container for the entire product list&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ProductCardComponent&lt;/code&gt;&lt;/strong&gt; - a reusable component for displaying individual product information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create these components, I'll use the Angular CLI commands:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

ng generate component product-list
ng generate component product-card


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Building the initial component structure
&lt;/h3&gt;

&lt;p&gt;Now that I have a clear vision for the product listing page, I'll start sketching out the structure of the Angular components. The &lt;code&gt;ProductListComponent&lt;/code&gt; will act as an orchestrator, managing the display of multiple &lt;code&gt;ProductCardComponent&lt;/code&gt; instances, each representing a single product.&lt;/p&gt;

&lt;p&gt;To get a better sense of the data flow and component interaction, I'll create a basic template for the product list:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;This template outlines my intent: I'll iterate over a &lt;code&gt;products&lt;/code&gt; array and pass each &lt;code&gt;product&lt;/code&gt; object as an input to a &lt;code&gt;ProductCardComponent&lt;/code&gt;. However, there are some missing pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I haven't defined the &lt;code&gt;products&lt;/code&gt; array and how I'll fetch the product data.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ProductCardComponent&lt;/code&gt; needs a &lt;code&gt;product&lt;/code&gt; input property to receive the data from the parent component.&lt;/li&gt;
&lt;li&gt;I need a &lt;code&gt;Product&lt;/code&gt; interface to define the structure of each product so that both the list and the card components can work with the data consistently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address these missing pieces, I'll start by defining the &lt;code&gt;Product&lt;/code&gt; interface, as this is the foundation for working with product data in my components. Then, I'll create the input property in the &lt;code&gt;ProductCardComponent&lt;/code&gt; and, finally, provide some initial product data to populate the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the &lt;code&gt;Product&lt;/code&gt; interface
&lt;/h3&gt;

&lt;p&gt;Since both components will be working with product data, it makes sense to define a common structure for this data. To achieve this, I'll create an interface called &lt;code&gt;Product&lt;/code&gt;: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, the question is: &lt;em&gt;where should I place this interface file?&lt;/em&gt; Since it's not specific to either component but will be used by both, a shared location makes the most sense.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;src/app&lt;/code&gt; directory, I'll create a new folder named &lt;code&gt;shared&lt;/code&gt;. This will serve as a central repository for code that's used multiple times across the application. Inside &lt;code&gt;shared&lt;/code&gt;, I'll create another folder called &lt;code&gt;types&lt;/code&gt; to specifically house our data interfaces.&lt;/p&gt;

&lt;p&gt;Therefore, the &lt;code&gt;product.interface.ts&lt;/code&gt; file will reside in the &lt;code&gt;src/app/shared/types&lt;/code&gt; folder. This approach promotes reusability and maintainability, as it makes the &lt;code&gt;Product&lt;/code&gt; interface easily accessible from anywhere in our application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add an input property to the &lt;code&gt;ProductCardComponent&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now that I have the &lt;code&gt;Product&lt;/code&gt; interface, I need a way for the &lt;code&gt;ProductListComponent&lt;/code&gt; to pass the data for each individual product to the &lt;code&gt;ProductCardComponent&lt;/code&gt;. To achieve this, I'll add an input property to the &lt;code&gt;ProductCardComponent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'll call this input property &lt;code&gt;product&lt;/code&gt; since it will be used to hold the data for a single product. Here's the updated code for &lt;code&gt;product-card.component.ts&lt;/code&gt;: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Define the product catalog data
&lt;/h3&gt;

&lt;p&gt;Since we don't yet have a backend API to provide product information, I'll use mock data for our initial implementation. This allows me to focus on building the frontend components and their interactions without being blocked by the backend development.&lt;/p&gt;

&lt;p&gt;To keep my code organized, I'll create a separate file to store this mock data. I'll name it &lt;code&gt;product-data.ts&lt;/code&gt; and place it within the &lt;code&gt;product-list&lt;/code&gt; folder. This makes sense because the mock data is directly related to this specific feature and isn't currently needed elsewhere in the application.&lt;/p&gt;

&lt;p&gt;Here is what the file will contain: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now that I have the product mock data, I can load it into the &lt;code&gt;ProductListComponent&lt;/code&gt; to be displayed on the product listing page. Here's the relevant code: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Displaying the products
&lt;/h3&gt;

&lt;p&gt;Now that I've defined the data models and loaded the mock product data, I'll bring the product listing page to life.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rendering the product list
&lt;/h4&gt;

&lt;p&gt;First, I need to tell the main application component &lt;code&gt;AppComponent&lt;/code&gt; to display the &lt;code&gt;ProductListComponent&lt;/code&gt;. This is where the product list will actually be rendered. I can do this by updating the &lt;code&gt;app.component.html&lt;/code&gt; file: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Displaying individual products
&lt;/h4&gt;

&lt;p&gt;Next, I need to populate the &lt;code&gt;ProductCardComponent&lt;/code&gt; template to display the details of each individual product. For now, I'll keep it simple to verify that the data is loading correctly. Note that I'm using the &lt;code&gt;currency&lt;/code&gt; pipe to format the price in Euros. &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Using &lt;code&gt;mat-card&lt;/code&gt; from Angular Material for styling
&lt;/h4&gt;

&lt;p&gt;As mentioned in the acceptance criteria, I'll be using Angular Material to style the product cards. I'll install the package using the Angular CLI. This command will prompt you to choose a pre-built theme and set up additional configuration options:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

ng add @angular/material


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then I'll update the &lt;code&gt;ProductCardComponent&lt;/code&gt; template to use the &lt;code&gt;mat-card&lt;/code&gt; component for styling: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can find more details about using the &lt;code&gt;mat-card&lt;/code&gt; component in the official Angular Material documentation: &lt;a href="https://v18.material.angular.io/components/card/overview" rel="noopener noreferrer"&gt;https://v18.material.angular.io/components/card/overview&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, I run the app (using &lt;code&gt;ng serve&lt;/code&gt; or &lt;code&gt;npm run start&lt;/code&gt;) and see a list of basic product cards displayed on the page. However, the layout isn't quite right and the image is taking up the full width of the container, no matter the window size.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpa7uq5thx12aft915j3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpa7uq5thx12aft915j3n.png" alt="Basic product list"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can see the code at this stage in the Github repository at this specific revision: &lt;a href="https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/eda71069e44b6615267f289627f94d01a3e50d2b" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/eda71069e44b6615267f289627f94d01a3e50d2b&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I now need to refine the layout of the product list and style the product cards to make them visually appealing and responsive. I'll cover this in the next section.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparing the layout and styling the components
&lt;/h2&gt;

&lt;p&gt;When I approach the design and layout of a component, I find it helpful to abstract away the specific content initially and focus on the underlying structure. This can save time and effort later on. &lt;/p&gt;

&lt;p&gt;I think of it like an architect designing the blueprint of a house before deciding on the interior decorations. The blueprint establishes the foundation and framework, while the decorations can be added and modified later.&lt;/p&gt;

&lt;p&gt;In my case, I'll consider the product list as a container with multiple items. This abstract view allows me to create a flexible and adaptable layout that works well across different screen sizes.&lt;/p&gt;

&lt;p&gt;Here's the step-by-step approach I typically follow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Abstraction&lt;/strong&gt;: define the core structure of the component without focusing on specific content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose a Design Approach&lt;/strong&gt;: select a layout strategy based on the design goals and responsiveness requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt;: translate the chosen design approach into code, using CSS to create the layout structure and style the elements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This methodical process ensures that the resulting layout is robust, adaptable, and visually appealing, regardless of the specific content it will hold.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important Note&lt;/strong&gt;: In this initial implementation, I'm solely focused on creating the product list functionality. The examples and code snippets provided here only showcase the &lt;code&gt;ProductListComponent&lt;/code&gt; and its child &lt;code&gt;ProductCardComponent&lt;/code&gt;. In a real-world e-commerce application, we would typically have a more comprehensive layout that includes a header, navigation menu, page title, footer, and other elements. These elements would be incorporated into the overall application structure using additional components and routing. However, for the sake of clarity and to emphasize the core principles of building a responsive product list, I'm keeping the current implementation focused and minimal.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Power of Abstraction in Layout Design
&lt;/h3&gt;

&lt;p&gt;Here's why abstracting away content initially is a valuable approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Focus on Core Structure&lt;/strong&gt;: By ignoring the specific content initially, we can focus on the fundamental layout principles – how the items should be arranged, the spacing between them, and how the layout should respond to different screen sizes. This allows us to create a solid foundation for our design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexibility and Reusability&lt;/strong&gt;: Abstracting the layout makes it easier to reuse the same structure for different types of content. For example, the same grid layout we create for product cards could be used for blog posts, team member profiles, or other types of content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplified Testing&lt;/strong&gt;: With a basic layout structure in place, we can test the responsiveness and adaptability of the design without being distracted by the details of the content. This allows us to catch any layout issues early on and iterate on the design more effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaboration&lt;/strong&gt;:  An abstract layout is easier to communicate and discuss with designers and stakeholders. They can focus on the overall structure and flow of the content without getting bogged down in the specifics of each element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gradual Refinement&lt;/strong&gt;: Once the basic layout is established, we can then gradually add and style the specific content elements. This allows for a more iterative and focused approach to design and development.&lt;/p&gt;

&lt;p&gt;In our e-commerce application, I can abstract the product list as a container (&lt;code&gt;product-list&lt;/code&gt;) with multiple items (&lt;code&gt;product-card&lt;/code&gt;). My focus will be on creating a responsive layout that adapts to different screen sizes and ensures proper spacing between items. Once this foundation is established, I'll design and style the individual product cards, filling them with the relevant content (images, names, prices, descriptions).&lt;/p&gt;
&lt;h3&gt;
  
  
  Mobile-First Design: Why it matters
&lt;/h3&gt;

&lt;p&gt;Next, I need to choose a design approach. Knowing that I'm aiming for a responsive design that works well on various screen sizes, from mobile to desktop, I'll adopt the &lt;strong&gt;Mobile-First Design&lt;/strong&gt; approach, a strategy that prioritizes designing and building the mobile version of a website or application first, then progressively enhancing it for larger screens.&lt;/p&gt;

&lt;p&gt;Here is why Mobile-First Design is often the preferred approach in modern web development:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Progressive Enhancement&lt;/strong&gt;: With a mobile-first approach, we start with a simpler, more streamlined layout and then progressively add enhancements for larger screens. This is generally easier and more efficient than trying to strip down a complex desktop design to fit mobile constraints. It also allows for more flexibility and scalability as we can easily add new features or design elements for larger screens without compromising the mobile experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mobile Usage Dominance&lt;/strong&gt;: The majority of web traffic now originates from mobile devices. By starting with a mobile-first design, we prioritize the experience for the largest segment of our audience. This ensures that the majority of our users have a smooth and optimized experience right from the start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content Prioritization&lt;/strong&gt;: Mobile screens have limited real estate, forcing us to focus on the most essential content and interactions. This naturally leads to a cleaner and more streamlined design that translates well to larger screens. Desktop-first designs can sometimes feel cluttered or overwhelming when adapted to mobile, as they might include elements that are less relevant or usable on smaller screens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Optimization&lt;/strong&gt;: Mobile devices often have slower connections and less processing power than desktops. Designing for mobile first forces us to optimize performance from the beginning. This optimization benefits users on all devices, as even desktop users appreciate fast-loading pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEO Benefits&lt;/strong&gt;: Search engines like Google prioritize mobile-friendly websites in their rankings. Adopting a mobile-first approach can improve our website's SEO and increase its visibility in search results.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you're interested in learning more about Mobile-First Design, check out these resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://www.youtube.com/watch?v=W1dGYykSR-4:" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=W1dGYykSR-4:&lt;/a&gt; A video tutorial explaining the concepts and benefits of mobile-first design.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://www.manypixels.co/blog/web-design/mobile-first-design:" rel="noopener noreferrer"&gt;https://www.manypixels.co/blog/web-design/mobile-first-design:&lt;/a&gt; An article discussing the importance of mobile-first design and providing practical tips for implementation.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://www.sanity.io/glossary/mobile-first-design:" rel="noopener noreferrer"&gt;https://www.sanity.io/glossary/mobile-first-design:&lt;/a&gt; A concise definition and overview of mobile-first design principles.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this mobile-first mindset, my approach will be to initially design and style the product list for smaller screens (mobile devices). Then, as I move to larger viewports (tablets and desktops), I'll progressively enhance the layout to accommodate more columns and potentially additional content.&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing the layout for small screens
&lt;/h3&gt;

&lt;p&gt;With the mobile-first approach in mind, I'll start implementing the layout for smaller screens. Currently, the product cards are stacked vertically, which is the correct behavior for mobile. However, they are lacking spacing between them. &lt;/p&gt;
&lt;h4&gt;
  
  
  Determining where to apply spacing
&lt;/h4&gt;

&lt;p&gt;The question is: where should I add this spacing? Remembering the principle of separation of concerns, it makes sense to apply the spacing within the &lt;code&gt;ProductListComponent&lt;/code&gt;, as this component controls the overall layout of the product list. The &lt;code&gt;ProductCardComponent&lt;/code&gt; should focus on presenting the individual product details, not the spacing between cards.&lt;/p&gt;
&lt;h4&gt;
  
  
  Choosing a spacing strategy
&lt;/h4&gt;

&lt;p&gt;Now, I need to decide how to add this spacing. There are a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;margins on product cards&lt;/strong&gt;:  This involves adding a margin to the &lt;code&gt;product-card&lt;/code&gt; elements, creating space around each card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;padding on the container&lt;/strong&gt;: This involves adding padding to the &lt;code&gt;product-list&lt;/code&gt; container, creating space between the cards and the container's edges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;flexbox &lt;code&gt;gap&lt;/code&gt; property&lt;/strong&gt;: If I use Flexbox for the layout, I can leverage the &lt;code&gt;gap&lt;/code&gt; property to easily control the spacing between items.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll examine each approach to determine the best fit for the mobile-first layout.&lt;/p&gt;
&lt;h4&gt;
  
  
  Apply margins around list items
&lt;/h4&gt;

&lt;p&gt;In the product list template, I'll wrap each &lt;code&gt;ProductCardComponent&lt;/code&gt; element within a &lt;code&gt;div.product-card&lt;/code&gt;: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This provides a target for applying margins directly to the product card wrapper elements, as shown below: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;After reloading the app, I see a nice, even spacing around each product card. The vertical spacing between adjacent cards is exactly &lt;code&gt;1rem&lt;/code&gt;, as expected.&lt;/p&gt;

&lt;p&gt;Here are some screenshots:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the first one is the initial display, without any spacing&lt;/li&gt;
&lt;li&gt;the list after applying the margins&lt;/li&gt;
&lt;li&gt;the last card has no bottom margin
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fig9h2kgem8ngfytthlpo.png" alt="apply margins"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, there's one important issue: the last product card doesn't have a visible bottom margin!&lt;/p&gt;

&lt;h4&gt;
  
  
  Understanding margin collapsing
&lt;/h4&gt;

&lt;p&gt;This behavior is due to a CSS concept called &lt;strong&gt;margin collapsing&lt;/strong&gt;. In essence, when vertical margins (top and bottom) are applied to adjacent elements, they sometimes combine into a single margin rather than adding up. This happens when there's no padding, border, or inline content to separate the elements.&lt;/p&gt;

&lt;p&gt;In this case, each &lt;code&gt;div.product-card&lt;/code&gt; element has a bottom margin. However, the last product card's bottom margin collapses with the bottom margin of its parent container. This is why we don't see a visible margin below the last card.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: More details about margin collapsing can be found at:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing&lt;/a&gt; The MDN Web Docs article provides a comprehensive guide to understanding margin collapsing, its rules, and how to work with it effectively.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://www.joshwcomeau.com/css/rules-of-margin-collapse/" rel="noopener noreferrer"&gt;https://www.joshwcomeau.com/css/rules-of-margin-collapse/&lt;/a&gt; This interactive blog post by Josh Comeau offers a visual and engaging explanation of margin collapsing, making it easier to grasp the concept.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Fixing the margin collapsing issue
&lt;/h4&gt;

&lt;p&gt;One of the fixes is to apply a bottom padding to the &lt;code&gt;.product-list&lt;/code&gt; container element and remove the bottom margin from the last &lt;code&gt;.product-card&lt;/code&gt; child: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 &lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, with these changes applied, the spacing between all product cards appears as expected, with even margins all around.&lt;/p&gt;

&lt;p&gt;You can check out the code with these changes in the GitHub repository at this revision: &lt;a href="https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/24ed3e51e3c988c452060c2b1cdd412c4d3786c4" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/24ed3e51e3c988c452060c2b1cdd412c4d3786c4&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Apply padding around list items
&lt;/h4&gt;

&lt;p&gt;Another solution for creating spacing around the cards is to use paddings on their wrappers: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud9hpecrqu03zxbqh20k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fud9hpecrqu03zxbqh20k.png" alt="product card paddings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this simple change, each product card now has &lt;code&gt;1rem&lt;/code&gt; of padding on all sides, visually separating the content from the card's edges. &lt;/p&gt;

&lt;p&gt;However, as you can see in the screenshot, this approach leads to a new issue: The vertical spacing between adjacent cards is double the intended space because the bottom padding of one card is added to the top padding of the next. &lt;/p&gt;

&lt;p&gt;To resolve this double spacing issue, I can selectively remove the bottom padding from all cards except for the last one in the list. I'll achieve this using the &lt;code&gt;:not(:last-child)&lt;/code&gt; pseudo-selector: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You can see the code with these changes in the GitHub repository at this revision: &lt;a href="https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/1d9d60a6319dffee2453eaf4072f708c0d8212a6" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/1d9d60a6319dffee2453eaf4072f708c0d8212a6&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  The philosophy of spacing: Parent vs Child responsibilities
&lt;/h4&gt;

&lt;p&gt;In the previous solutions, spacing was defined within the &lt;code&gt;.product-card&lt;/code&gt; selector (the child elements). However, let's revisit the fundamental question: Who should be responsible for defining spacing in a parent-child layout relationship?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parent's Role: Outer Spacing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider an empty parent container. To create inner spacing, we intuitively apply padding to the parent. This is because padding defines space &lt;em&gt;within&lt;/em&gt; an element, pushing its content inwards. While we could achieve a similar visual effect with margins &lt;em&gt;outside&lt;/em&gt; the parent, it's not semantically accurate because margins create space &lt;em&gt;around&lt;/em&gt; an element, not within it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Child's Role: Inner Spacing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's add child elements to the parent. These children will naturally reside within the boundaries defined by the parent's padding.&lt;/p&gt;

&lt;p&gt;The question then becomes: Who controls the spacing &lt;em&gt;between&lt;/em&gt; these child elements? The answer lies in the concept of margin. Margins create empty space &lt;em&gt;around&lt;/em&gt; elements, so it's the responsibility of each child element to define its own margin to create separation from its siblings.&lt;/p&gt;

&lt;p&gt;In our product list, applying &lt;code&gt;margin-bottom&lt;/code&gt; to each product card (except the last) achieves this goal. If we used padding instead, we'd imply that the space between cards is part of the card itself, which is not semantically correct. &lt;/p&gt;

&lt;p&gt;Based on this reasoning, a more effective implementation would be: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The code with these changes can be found at this specific revision: &lt;a href="https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/e31fe48758da28321b65125033c9f3cdea151e24" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/e31fe48758da28321b65125033c9f3cdea151e24&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using Flexbox for spacing
&lt;/h4&gt;

&lt;p&gt;Another elegant approach to defining spacing is to leverage the power of CSS &lt;strong&gt;Flexbox&lt;/strong&gt;. This method aligns perfectly with the principle of separation of concerns, distinguishing between spacing around the list and the space between its items. Here's the implementation: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;gap&lt;/code&gt; property does the heavy lifting here, handling the spacing between the cards. In our current vertical layout, this translates to vertical spacing only. With this approach, we eliminate concerns about margin collapsing, simplifying our code and ensuring consistent spacing between cards.&lt;/p&gt;

&lt;p&gt;The simplicity of this CSS is a major advantage. Flexbox is a powerful layout tool, and in this case, it allows us to achieve the desired layout with minimal code.&lt;/p&gt;

&lt;p&gt;For now, I'll refrain from declaring a definitive "best" approach. Let's continue exploring how both margins and Flexbox adapt when we introduce styling for larger screens. This will give us a more comprehensive understanding of their strengths and weaknesses in different scenarios.&lt;/p&gt;

&lt;p&gt;You can see the changes up to this point in the GitHub repository: &lt;a href="https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/ca8ab55b9529049da501f7191062651900d98e2c" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/ca8ab55b9529049da501f7191062651900d98e2c&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the layout for larger screens
&lt;/h3&gt;

&lt;p&gt;As I transition to designing the layout for larger screens, a key question arises: What should determine the number of product cards per row? Should it be based on the device's screen size, or the available width of the product list container itself?&lt;/p&gt;

&lt;p&gt;While screen size is a factor, my experience has taught me that relying solely on it can be restrictive. Different devices have varying widths, and the width of our container might change due to the overall page layout or user preferences. Therefore, I believe it's crucial to base the number of cards per row on the available container width. This approach ensures a more flexible and adaptable layout.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why container width is key for Responsive Design
&lt;/h4&gt;

&lt;p&gt;Here's why prioritizing container width leads to a more adaptable and user-friendly layout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content containers can change&lt;/strong&gt;: The width of the product list container might change depending on the overall page layout, user preferences, or other design considerations. Basing the layout on the container width ensures it adapts to these changes dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximizing space utilization&lt;/strong&gt;: By considering the container width, we can maximize the use of available space, ensuring that as many products as possible are displayed without overcrowding or creating an overly sparse layout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation&lt;/strong&gt;: Users might resize their browser windows or use devices with non-standard screen sizes. Basing the layout on container width allows the design to gracefully adjust and avoid breaking or looking odd.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Choosing the right implementation
&lt;/h4&gt;

&lt;p&gt;Let's revisit the acceptance criteria to guide the implementation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The product listing page displays products in a grid layout.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Each row of the grid contains a maximum of 4 product items.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The layout is responsive and adapts to different screen sizes.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since I need to accommodate more cards per row on larger screens, CSS &lt;strong&gt;Grid&lt;/strong&gt; is the perfect tool for the job. It allows me to create a flexible, responsive grid layout that adapts to different screen sizes and content while maintaining a visually pleasing arrangement.&lt;/p&gt;

&lt;p&gt;To create a dynamic layout while adhering to these constraints, I'll set both minimum and maximum widths for the product cards. This ensures the cards have enough space to display their content comfortably but don't become overly large on wide screens.&lt;/p&gt;

&lt;p&gt;In the next section, I'll delve into the CSS code that brings this responsive grid layout to life.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the grid layout
&lt;/h3&gt;

&lt;p&gt;To achieve the desired design, I'll gradually build the styling, based on each requirement.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Apply the grid layout
&lt;/h4&gt;

&lt;p&gt;The first step is to establish the &lt;code&gt;.product-list&lt;/code&gt; as a CSS grid container:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="nc"&gt;.product-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;At this point, the product cards will simply stack vertically, as a single column is the default behavior for a grid without explicit column definitions.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Set the minimum card width
&lt;/h4&gt;

&lt;p&gt;Next, I'll define the minimum card width. A value that was agreed upon during the discussion with the PO was &lt;code&gt;240px&lt;/code&gt;. The CSS property for this scenario is &lt;code&gt;grid-template-columns&lt;/code&gt;, which controls the number and size of columns in the grid.&lt;/p&gt;

&lt;p&gt;What value can the property take? For this case when we have a variable number of columns, we can use the &lt;strong&gt;&lt;code&gt;repeat()&lt;/code&gt;&lt;/strong&gt; function with the &lt;strong&gt;&lt;code&gt;auto-fit&lt;/code&gt;&lt;/strong&gt; value for the track count. The &lt;code&gt;auto-fit&lt;/code&gt; keyword tells the grid to create as many columns as possible within the available space, respecting any minimum or maximum constraints.&lt;/p&gt;

&lt;p&gt;Then, I need to define the second argument of the &lt;code&gt;repeat()&lt;/code&gt; function, which specifies the set of tracks that will be repeated. I need to have equal columns and define a minimum width for them - this will get translated into &lt;code&gt;minmax(240px, 1fr)&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;240px&lt;/code&gt; is the minimum width each column can be. It ensures that even on smaller screens, where we might only have one or two columns, the product cards will still have enough space to display their content comfortably.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1fr&lt;/code&gt; is a fractional unit, meaning that any remaining space in the grid container, after allocating the minimum width to each column, will be distributed equally among the columns. This is what creates the equal-width behavior I want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the CSS for this: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;When reloading the app and adjusting the viewport size, I notice that the columns are dynamically created, have a minimum width, but there could be more than the expected 4 columns on larger screens.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F834qcgtxiu7p0xyxd5ye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F834qcgtxiu7p0xyxd5ye.png" alt="5 items per row"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  3. Set a maximum of 4 cards per row
&lt;/h4&gt;

&lt;p&gt;I need to address the issue of displaying more columns than expected on larger screens. For this I need to calculate the maximum width each column can be. For this I'll adjust the first argument of the &lt;code&gt;minmax()&lt;/code&gt; function to include the maximum value of the column width.&lt;/p&gt;

&lt;p&gt;Now I have to determine what this maximum width is, relative to its parent container. I could say that the max width is 100% (the container width) / 4 (maximum columns per row) = 25%, but this is wrong because I didn't consider the gaps between the columns. Here is how I can include them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculate the total width of the gaps: there are 3 gaps between 4 columns), and each gap is &lt;code&gt;1rem&lt;/code&gt;, so the result is &lt;strong&gt;&lt;code&gt;(4 - 1) * 1rem&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Subtract the total gap width from the container width: &lt;code&gt;100% - (4 - 1) * 1rem&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Divide the remaining width by the number of columns: &lt;code&gt;(100% - (4 - 1) * 1rem) / 4&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the maximum value of a column: &lt;code&gt;calc((100% - (4 - 1) * 1rem) / 4)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To make the code more flexible and maintainable, I'll use CSS variables instead of hardcoded values. Here is the updated CSS code: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  4. Define a maximum width for product cards
&lt;/h4&gt;

&lt;p&gt;When transitioning from 2 columns to one column layout, the product card gets very wide and looks a bit odd. I want to limit its width to, let's say, 380px. This will also prevent the cards from stretching excessively on very wide screens.&lt;/p&gt;

&lt;p&gt;But where exactly should I apply this constraint? On the &lt;code&gt;.product-card&lt;/code&gt; wrapper itself, or on its content?&lt;/p&gt;

&lt;p&gt;When I apply it directly on &lt;code&gt;.product-card&lt;/code&gt; elements, the columns in the grid layout could overlap at different container sizes.&lt;/p&gt;

&lt;p&gt;A proper solution for this case is to apply the max width to the actual content of the &lt;code&gt;.product-card&lt;/code&gt; wrapper using the flexbox display: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: To get a deeper understanding of CSS Grid layout and its properties, you can refer to the following resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout&lt;/a&gt; This MDN Web Docs article provides a comprehensive overview of the fundamental concepts of CSS Grid, including grid containers, items, tracks, and lines.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns&lt;/a&gt; This MDN Web Docs article focuses specifically on the &lt;code&gt;grid-template-columns&lt;/code&gt; property, explaining how it defines the columns in a grid layout.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://css-tricks.com/almanac/properties/g/grid-template-columns/" rel="noopener noreferrer"&gt;https://css-tricks.com/almanac/properties/g/grid-template-columns/&lt;/a&gt; This CSS-Tricks almanac entry offers a detailed explanation of grid-template-columns, including different ways to specify column sizes and examples of how to use it.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://gridbyexample.com/examples/" rel="noopener noreferrer"&gt;https://gridbyexample.com/examples/&lt;/a&gt; This website provides a collection of interactive examples demonstrating various CSS Grid techniques, including responsive layouts, grid areas, and more.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Checkout the code
&lt;/h4&gt;

&lt;p&gt;The changes I've made so far can be found in the &lt;code&gt;product-list&lt;/code&gt; branch of the Github repository: &lt;a href="https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/product-list" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/product-list&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  See it in action
&lt;/h3&gt;

&lt;p&gt;Here are some screenshots of different screen sizes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftegccdy4ynwjcln68i2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftegccdy4ynwjcln68i2i.png" alt="Two column per row"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6l7d2ewf7hz9a4scgz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6l7d2ewf7hz9a4scgz5.png" alt="Three columns per row"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhouiv0lhbjozm0tv25cn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhouiv0lhbjozm0tv25cn.png" alt="Four columns per row"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;With the current implementation, I've successfully met the acceptance criteria for the MVP product listing page. I've established a responsive grid layout, limited the maximum card and content widths, and created a visually appealing design using Angular Material. The product list now dynamically adjusts to different screen sizes, ensuring a user-friendly experience across devices.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key takeaways
&lt;/h4&gt;

&lt;p&gt;In this article, I've laid the groundwork for the e-commerce product listing page. Here's a recap of the key points I've covered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Topics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discussed the importance of abstracting layout design and choosing the right approach (CSS Grid in our case) for responsive design.&lt;/li&gt;
&lt;li&gt;Explored the nuances of &lt;code&gt;grid-template-columns&lt;/code&gt;, &lt;code&gt;minmax()&lt;/code&gt;, and other CSS properties for creating a flexible and visually appealing grid.&lt;/li&gt;
&lt;li&gt;Deliberated on how to handle spacing, considering both the container and individual product cards.&lt;/li&gt;
&lt;li&gt;Implemented a mobile-first design with a maximum of 4 cards per row, addressing potential issues like margin collapsing.&lt;/li&gt;
&lt;li&gt;Set up Angular Material to enhance styling and provide pre-built components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architectural and Technical Decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mobile-First Design: prioritized the mobile experience, starting with a single-column layout and planning to progressively enhance for larger screens.&lt;/li&gt;
&lt;li&gt;CSS Grid: chose CSS Grid over Flexbox for its more powerful grid layout capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;grid-template-columns&lt;/code&gt;: used &lt;code&gt;auto-fit&lt;/code&gt; and &lt;code&gt;minmax&lt;/code&gt; to create a responsive grid that adjusts the number of columns based on the available space.&lt;/li&gt;
&lt;li&gt;Spacing: defined padding on the container for outer spacing and margins on the card wrappers for inner spacing.&lt;/li&gt;
&lt;li&gt;Card styling: styled the product cards using Angular Material and added custom CSS to limit the maximum card and content width for a visually balanced layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Next steps
&lt;/h4&gt;

&lt;p&gt;The journey of building a robust e-commerce application doesn't end here. While the core functionality is in place, there are several areas for improvement that I'll address in future iterations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content Overflow: I need to handle cases where product names or descriptions might be too long and overflow the card's boundaries. I'll explore strategies like truncating text or adding "Read More" functionality.&lt;/li&gt;
&lt;li&gt;Image Optimization: Angular is currently warning us about potentially oversized images. I'll look into image optimization techniques to ensure fast loading times and a smooth user experience.&lt;/li&gt;
&lt;li&gt;Web Accessibility: To make the product list accessible to all users, I'll follow WCAG (Web Content Accessibility Guidelines) and implement features like alternative text for images and keyboard navigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next article of this series, I'll tackle these enhancements and dive deeper into the technical details of styling and refining the product list.&lt;/p&gt;




&lt;p&gt;Stay tuned for more insights and practical tips as I continue to build the e-commerce application step by step!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>grid</category>
      <category>tutorial</category>
      <category>responsivedesign</category>
    </item>
    <item>
      <title>Building an Angular E-commerce App: A Step-by-Step Guide to Understanding and Refining Requirements</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Mon, 08 Jul 2024 19:28:56 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/building-an-angular-e-commerce-app-a-step-by-step-guide-to-understanding-and-refining-requirements-hoe</link>
      <guid>https://dev.to/cezar-plescan/building-an-angular-e-commerce-app-a-step-by-step-guide-to-understanding-and-refining-requirements-hoe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Imagine you're tasked with building a web application. The project manager hands you a list of requirements, but some of them are a bit... fuzzy. What do you do? Panic? Start coding blindly? Or maybe, you take a step back and start asking questions. In this series of articles, we'll follow the journey of an Angular developer as they tackle the challenge of turning vague requirements into a real, working product. We'll explore the thought processes, the questions they ask, the decisions they make, and ultimately, how they create a successful e-commerce experience for their users.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Approach
&lt;/h3&gt;

&lt;p&gt;I'll take a pragmatic, hands-on approach, starting with a Minimum Viable Product (MVP) and gradually adding complexity and features. Along the way, I'll tackle real-world challenges, explore different architectural patterns, and share practical tips for building robust and scalable Angular applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you'll learn
&lt;/h3&gt;

&lt;p&gt;By the end of this series, you'll gain a deeper understanding of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to analyze and refine e-commerce requirements.&lt;/li&gt;
&lt;li&gt;The importance of user stories and acceptance criteria.&lt;/li&gt;
&lt;li&gt;The role of collaboration between developers and stakeholders.&lt;/li&gt;
&lt;li&gt;Architectural decisions for data flow, state management, and UI design.&lt;/li&gt;
&lt;li&gt;How to prioritize features and deliver value early and often.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, buckle up and join me on this journey as we unravel the complexities of building a real-world Angular application, one architectural decision at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Describing the application
&lt;/h2&gt;

&lt;p&gt;Let’s imagine I’m tasked with building an Angular e-commerce application from scratch. The product owner (PO) provides the following requirements for the Minimum Viable Product (MVP):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Display a list of products with their images, names, and prices&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allow users to view product details (description, images, specifications) on a separate page&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable users to find or filter products easily&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users should be able to add products to their cart from both the product listing and product details pages&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allow users to proceed to checkout from the cart&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the initial set of requirements, and more are expected to follow. The primary goal is to deliver a functional application as soon as possible.&lt;/p&gt;

&lt;h4&gt;
  
  
  Understanding the MVP
&lt;/h4&gt;

&lt;p&gt;In simple terms, an MVP is the most basic version of a product that can be released to users to gather feedback and validate the concept. It focuses on core features and functionality, allowing for rapid development and iterative improvement. You can learn more about MVPs here: &lt;a href="https://www.productplan.com/glossary/minimum-viable-product/" rel="noopener noreferrer"&gt;https://www.productplan.com/glossary/minimum-viable-product/&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  My plan of action
&lt;/h4&gt;

&lt;p&gt;With these initial MVP requirements in hand, my next steps are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Thorough analysis&lt;/strong&gt; - Carefully review each requirement to grasp the application's core purpose and intended functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarify and refine&lt;/strong&gt; - Identify any unclear, conflicting, or missing details in the requirements. If needed, I'll initiate discussions with stakeholders to resolve these ambiguities and ensure a shared understanding of the project scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task breakdown&lt;/strong&gt; - Analyze the requirements to identify the data involved, the data retrieval mechanisms, and the necessary user interactions. This will help me break down the work into actionable tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Effort estimation&lt;/strong&gt; - Estimate the time and effort required for each task and communicate this with stakeholders. This allows for prioritization and potential adjustments to the scope if necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt; - Once the requirements are clearly understood and the tasks are defined, I'll begin the actual development process.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this article, I'll focus on the first two steps: &lt;strong&gt;thorough analysis&lt;/strong&gt; and &lt;strong&gt;clarification of the requirements&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding and clarifying the requirements
&lt;/h2&gt;

&lt;p&gt;The purpose of the app is straightforward: to build a simple e-commerce website. &lt;/p&gt;

&lt;p&gt;I'll start by analyzing the first requirement: &lt;em&gt;"Display a list of products with their images, names, and prices."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This statement represents a &lt;strong&gt;functional requirement&lt;/strong&gt;. In essence, it defines a specific action or capability the system must perform to meet user needs and business objectives. It focuses on &lt;strong&gt;&lt;em&gt;what&lt;/em&gt;&lt;/strong&gt; the system does, &lt;strong&gt;&lt;em&gt;not how&lt;/em&gt;&lt;/strong&gt; it does it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: For a deeper dive into functional requirements, you can refer to this guide: &lt;a href="https://qat.com/guide-functional-requirements/" rel="noopener noreferrer"&gt;https://qat.com/guide-functional-requirements/&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In our case, the requirement clearly states the expected outcome: &lt;em&gt;displaying a product list&lt;/em&gt;. However, it leaves out crucial details about how the list should be presented and where the data will come from. To fully understand this requirement, I’ll need to clarify a few points with the PO.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clarification and decisions points
&lt;/h3&gt;

&lt;p&gt;While the requirement states the desired outcome, it raises several questions I need to address:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;visual presentation&lt;/strong&gt; - How should the list be displayed? Should it be a grid, a simple list, or something else?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;data source&lt;/strong&gt; - Where will I get the product data from? Is there an existing API, or do I need to use mock data for now?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;performance&lt;/strong&gt; - How many products are we dealing with? If it's a large dataset, should I implement features like pagination or infinite scrolling to optimize performance?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;responsiveness&lt;/strong&gt; - How should the list adapt to different screen sizes (desktop, mobile, etc.)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's delve into each of these questions and how we might approach them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Visual Presentation
&lt;/h4&gt;

&lt;p&gt;Since the requirement doesn't specify the layout, I'll consult with the PO. There are two likely scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: The PO has design guidelines&lt;/strong&gt;. Ideally, the PO might have already collaborated with a designer or have a clear vision for the product list's appearance. In this scenario, they would provide detailed specifications on the layout, styling, and any specific interactions. This makes my job easier as I can directly translate these guidelines into code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: The PO needs guidance&lt;/strong&gt;. More often, especially in early MVP stages, the PO may need guidance. My approach would be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Propose options: I'll suggest common layouts like grid and list, potentially with simple mockups to visualize them.&lt;/li&gt;
&lt;li&gt;User Experience (UX) Focus: We'll discuss the pros and cons of each layout regarding UX. For instance, grids are visually appealing but might be less space-efficient on mobile devices.&lt;/li&gt;
&lt;li&gt;Guided Decision: By presenting options and their implications, I'll help the PO make an informed decision.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this case, let's say the PO decides on a grid layout with a maximum of 4 items per row. We'll also leverage the Angular Material UI library for its pre-built components and consistent styling.&lt;/p&gt;

&lt;h4&gt;
  
  
  Data source
&lt;/h4&gt;

&lt;p&gt;Next, I need to figure out where the product data is coming from. The requirement doesn't mention where the product data comes from. Do we have a backend API ready, or should I use mock data for now? Also, what specific data fields (e.g., descriptions, categories) should be included? &lt;/p&gt;

&lt;p&gt;I'll inquire about the availability of a backend API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API exists&lt;/strong&gt;: If there's a ready-to-use API, I'll need to understand the endpoint, response format, and any authentication requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No API Yet&lt;/strong&gt;: If the backend is still under development, I'll propose using mock data for the initial implementation. This allows us to move forward with the frontend while the backend is being built.&lt;/p&gt;

&lt;p&gt;Since there's no backend API yet, we've decided to hardcode the products on the frontend.&lt;/p&gt;

&lt;h4&gt;
  
  
  Performance considerations
&lt;/h4&gt;

&lt;p&gt;The number of products can significantly impact how we display the list. I'll ask the PO about the expected product volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: Small List&lt;/strong&gt;. If we're dealing with a limited number of products (e.g., 10-20), a simple list or grid should suffice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: Large List&lt;/strong&gt;. If we anticipate hundreds or thousands of products, I'll need to consider performance optimization techniques like pagination, infinite scrolling, or virtual scrolling. Given that this is an MVP, I'll likely recommend starting with the simplest approach (no pagination/scrolling) to prioritize a quick launch. We can revisit this as the product catalog grows.&lt;/p&gt;

&lt;p&gt;To keep things simple for the MVP, we’ve decided to have no pagination or scrolling mechanisms for the product list at this stage.&lt;/p&gt;

&lt;h4&gt;
  
  
  Responsiveness
&lt;/h4&gt;

&lt;p&gt;Most e-commerce sites need to look good on various devices. I'll check if the PO has any specific requirements for mobile or tablet views.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: Responsiveness Required&lt;/strong&gt;. If so, I'll need to ensure the chosen layout (grid or list) is responsive and adapts gracefully to different screen sizes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: Desktop-First&lt;/strong&gt;. If the focus is on desktop for the MVP, I'll still design with responsiveness in mind, but we might postpone full mobile optimization for later iterations.&lt;/p&gt;

&lt;p&gt;The PO confirms they want a visually appealing product list on both mobile and desktop devices. Therefore, our chosen grid layout will be made responsive to adapt to various screen sizes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The importance of thorough requirement analysis
&lt;/h3&gt;

&lt;p&gt;The process I've just walked through highlights several crucial aspects of effective requirement analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Proactive communication&lt;/strong&gt;: Engaging in open and direct communication with the PO or stakeholders is key. By asking questions and seeking clarification, we avoid making assumptions and ensure the feature is built to meet the intended goals. This proactive communication not only clarifies the "&lt;strong&gt;what&lt;/strong&gt;" of the requirement but also uncovers the underlying "&lt;strong&gt;why&lt;/strong&gt;", allowing us to tailor solutions that truly address user needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balancing user needs and technical constraints&lt;/strong&gt;: Building software is a delicate balancing act between what users desire and what is technically feasible within given constraints (time, budget, resources). By carefully considering both perspectives, we can make informed decisions that prioritize delivering value to users while also ensuring a sustainable and maintainable implementation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterative development and the MVP mindset&lt;/strong&gt;: Recognizing that we are building an MVP (Minimum Viable Product) allows us to prioritize the most essential features and functionality. By starting with a simplified solution and iterating based on user feedback, we can quickly validate our assumptions, learn what works best, and make informed refinements to the product over time. This agile approach not only speeds up development but also ensures that the final product is more aligned with user expectations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This requirement analysis process demonstrates that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Effective communication is vital for successful project outcomes.&lt;/li&gt;
&lt;li&gt;Collaboration between developers and stakeholders is key to building the right thing.&lt;/li&gt;
&lt;li&gt;Prioritization and iteration are fundamental principles of agile development, allowing us to deliver value quickly and adapt to changing needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By embracing these principles, we can create a solid foundation for our e-commerce application, ensuring that it not only meets the initial requirements but also evolves to satisfy the evolving needs of our users and business goals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing user stories and acceptance criteria
&lt;/h3&gt;

&lt;p&gt;The clarifications I've gathered are a solid foundation for development, but they primarily focus on the technical &lt;strong&gt;&lt;em&gt;what&lt;/em&gt;&lt;/strong&gt; – what the system needs to do. However, behind every technical requirement, there's a crucial &lt;strong&gt;&lt;em&gt;why&lt;/em&gt;&lt;/strong&gt; – the user's motivations and goals.&lt;/p&gt;

&lt;h4&gt;
  
  
  Personal reflection: the importance of "&lt;em&gt;why&lt;/em&gt;"
&lt;/h4&gt;

&lt;p&gt;In my own experience, I've found that simply following instructions without understanding the underlying purpose can be demotivating and unproductive. Whether it was homework in school, theoretical problems in university, or tasks at previous jobs, knowing the "why" behind my work always made a significant difference. It gave my efforts meaning and helped me see the bigger picture.&lt;/p&gt;

&lt;p&gt;That's why, when working on personal projects, my biggest challenge was always to clearly define the problem I wanted to solve. Understanding the "why" gave my work direction and ensured I was building something truly valuable.&lt;/p&gt;

&lt;p&gt;As developers, it's easy to get caught up in the technical details and forget the bigger picture. We may have been trained to focus on completing tasks and implementing features without always understanding the underlying reasons behind them. This "just do it" mindset can be demotivating and lead to solutions that miss the mark.&lt;/p&gt;

&lt;p&gt;In real-world projects, it's essential to grasp the "why" behind our work. What problems are we solving for the users? How will our implementation bring value to them and the business? By answering these questions, we can build features that are not only technically sound but also meaningful and impactful.&lt;/p&gt;

&lt;h4&gt;
  
  
  The pitfalls of a purely technical focus
&lt;/h4&gt;

&lt;p&gt;In the context of our e-commerce application, focusing solely on the technical what can lead to several pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Misaligned solutions&lt;/strong&gt;: If we don't understand the user's underlying motivations, we might build a technically impressive feature that doesn't actually solve their problem or address their needs. This leads to wasted effort and resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missed opportunities&lt;/strong&gt;: You might miss opportunities to enhance the user experience if you don't explicitly consider the user's goals and motivations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misaligned priorities&lt;/strong&gt;: Without a clear understanding of the user's needs, it's harder to prioritize features effectively. You might end up spending time on technical details that don't significantly impact the user experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication barriers&lt;/strong&gt;: Technical jargon can create communication barriers between stakeholders and developers, leading to misunderstandings and delays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disengaged developers&lt;/strong&gt;: When developers don't understand the "why" behind their work, they can become disengaged and less invested in the project's success. This can negatively impact their motivation and productivity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Shifting to a user-centric perspective with user stories
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;User stories&lt;/strong&gt; help us bridge this gap. They shift the focus from the technical implementation to the user's perspective. Let's see how we can transform our first requirement into a user story:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;As a potential customer, I want to see a list of products with their images, names, and prices so that I can quickly browse and compare products before making a purchase decision.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This user story clearly articulates the user's goal (browsing and comparing products) and the reason behind it (making an informed purchase decision). By understanding this "why," we can make better design and implementation choices that truly cater to the user's needs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can find more information about user stories at &lt;a href="https://www.visual-paradigm.com/guide/agile-software-development/what-is-user-story/" rel="noopener noreferrer"&gt;https://www.visual-paradigm.com/guide/agile-software-development/what-is-user-story/&lt;/a&gt; or &lt;a href="https://www.atlassian.com/agile/project-management/user-stories" rel="noopener noreferrer"&gt;https://www.atlassian.com/agile/project-management/user-stories&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding clarity with Acceptance Criteria
&lt;/h3&gt;

&lt;p&gt;In addition to this part, we have to define a set of conditions that must be met for the user story to be considered complete and accepted by the product owner or stakeholders. This is what is called &lt;strong&gt;Acceptance Criteria&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is how they can be composed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The product listing page displays a grid of products.&lt;/li&gt;
&lt;li&gt;Each product card shows the product image, name, and price.&lt;/li&gt;
&lt;li&gt;The product list has a maximum of 4 items per row.&lt;/li&gt;
&lt;li&gt;The list does not have pagination or infinite scrolling functionality (for the MVP).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can learn more about acceptance criteria at &lt;a href="https://www.productplan.com/glossary/acceptance-criteria/" rel="noopener noreferrer"&gt;https://www.productplan.com/glossary/acceptance-criteria/&lt;/a&gt; or &lt;a href="https://www.altexsoft.com/blog/acceptance-criteria-purposes-formats-and-best-practices/" rel="noopener noreferrer"&gt;https://www.altexsoft.com/blog/acceptance-criteria-purposes-formats-and-best-practices/&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  How to compose effective Acceptance Criteria
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Focus on user outcomes&lt;/strong&gt;: Instead of specifying technical details, focus on what the user should be able to achieve or experience when the feature is implemented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use clear and concise language&lt;/strong&gt;: Write in plain language that everyone, including non-technical stakeholders, can understand. Avoid vague terms like "&lt;em&gt;user-friendly&lt;/em&gt;" or "&lt;em&gt;intuitive&lt;/em&gt;." Be specific about what the user should see, do, or experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each criterion should be verifiable&lt;/strong&gt;: Write criteria that can be objectively tested to determine if they have been met. Use specific values or conditions whenever possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaborate with stakeholders&lt;/strong&gt;: Work with the product owner, designers, and other stakeholders to ensure the acceptance criteria accurately reflect their expectations. Share your draft acceptance criteria with stakeholders early on to gather feedback and make necessary adjustments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of using User Stories and Acceptance Criteria
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;User-Centric Focus&lt;/strong&gt;: The core principle of user stories is to shift the perspective from a purely functional description to understanding the user's needs and motivations. By framing the requirement as "As a potential customer, I want to see a list of products... so that I can quickly browse and compare products," you immediately highlight the value the feature brings to the user. This helps the team focus on delivering solutions that directly address user needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared understanding&lt;/strong&gt;: User stories create a common language between stakeholders (including the PM, designers, and developers). The simple "&lt;em&gt;As a... I want... so that...&lt;/em&gt;" format is easy to understand and avoids technical jargon. This ensures everyone is aligned on the purpose of the feature and what it needs to accomplish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clearer priorities&lt;/strong&gt;: User stories are easier to prioritize than purely functional requirements. By understanding the "why" behind the feature, stakeholders can assess its importance relative to other features and prioritize development efforts accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexibility and adaptability&lt;/strong&gt;: User stories are inherently flexible. They are not rigid specifications but rather invitations for conversation and collaboration. As the team learns more about user needs and technical constraints, the user story can be refined and adapted without requiring a complete rewrite of the requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testable outcomes&lt;/strong&gt;: User stories naturally lead to the definition of acceptance criteria, which are specific, measurable conditions that must be met for the story to be considered complete. This creates a clear definition of "done" and facilitates testing and quality assurance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documenting the discussions
&lt;/h3&gt;

&lt;p&gt;Once I've thoroughly discussed and clarified the requirements, it's crucial to formally document them in a shared location accessible to the entire team. This documentation serves as a single source of truth, ensuring everyone is aligned and preventing misunderstandings.&lt;/p&gt;

&lt;p&gt;Here is how I can translate the first requirement into a user story:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;User Story&lt;/strong&gt;&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As a potential customer, I want to see a clear and visually appealing list of products with their images, names, and prices, so that I can quickly browse and compare items before making a purchase decision.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Acceptance Criteria&lt;/strong&gt;:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Layout&lt;/em&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The product listing page displays products in a grid layout.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Each row of the grid contains a maximum of 4 product items.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The layout is responsive and adapts to different screen sizes.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Product Card Content:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Each product card displays a clear product image.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The product name is prominently displayed below the image.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The price is displayed clearly, using the appropriate currency symbol.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Functionality:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The product list is initially loaded when the user navigates to the product listing page.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;MVP Considerations:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The product list does not include pagination or infinite scrolling in this initial version.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Error handling for data fetching issues will be addressed in a later iteration.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Technical Notes:&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Data Source: Product data will be initially hardcoded on the frontend.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;UI Framework: The Angular Material library will be used for styling and components.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this e-commerce example, I've translated the first functional requirement into a single user story. However, this is not always the case. A single functional requirement can often give rise to multiple user stories, each representing different perspectives and needs of various users.&lt;/p&gt;

&lt;p&gt;In an Agile environment, the responsibility of writing user stories is often shared. While the Product Owner is ultimately accountable for the product backlog, developers can contribute by creating user stories and validating them with the PO to ensure they are clear, concise, and actionable.&lt;/p&gt;

&lt;h3&gt;
  
  
  The importance of clarifying and refining requirements
&lt;/h3&gt;

&lt;p&gt;As you've seen, even a seemingly simple requirement like "&lt;em&gt;Display a list of products with their images, names, and prices&lt;/em&gt;" can raise numerous questions. Before a single line of code is written, it's crucial to address these questions through collaborative discussions with the Product Owner and other stakeholders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not assume? The value of shared understanding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While it might be tempting to make assumptions based on experience or best practices (e.g., assuming a grid layout for the product list), it's essential to resist that urge. Explicitly discussing design choices and technical considerations with stakeholders ensures everyone is on the same page.&lt;/p&gt;

&lt;p&gt;For instance, while a grid layout might seem like the obvious choice, the PO might have a different vision in mind or specific design constraints to consider. By collaborating early on, we avoid misunderstandings, reduce the risk of rework, and build a product that aligns with everyone's expectations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business value first: Balancing innovation and practicality&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As developers, we're often eager to implement cutting-edge features or solve complex technical challenges. However, it's crucial to remember that our primary goal is to deliver business value. This means prioritizing features that directly address user needs and contribute to the product's success.&lt;/p&gt;

&lt;p&gt;In the context of an MVP (Minimum Viable Product), the focus should be on getting the core functionality working quickly and efficiently. We can always add more sophisticated features and optimizations in later iterations, based on user feedback and evolving business needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Agile mindset: Embracing iteration and feedback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This approach aligns with the core principles of Agile development methodologies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Incremental Delivery&lt;/strong&gt;: Start with a basic version of the product and gradually add features and refinements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Feedback&lt;/strong&gt;: Regularly gather feedback from users and stakeholders to validate assumptions and guide development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptability&lt;/strong&gt;: Be prepared to adjust plans and priorities based on feedback and changing requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember, we're not just building software; we're building a product that solves real problems for real people. By fostering open communication, clarifying assumptions, and prioritizing user needs, we set ourselves up for success, creating a product that delights users and drives business growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recap of the clarification process
&lt;/h3&gt;

&lt;p&gt;As I delved into the first requirement, "Display a list of products with their images, names, and prices," several questions and decisions emerged. This seemingly simple requirement opened up a wealth of considerations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Visual Presentation: We recognized the need to clarify the desired layout of the product list. By proposing options and mockups, we ensured a shared understanding of the visual presentation with stakeholders.&lt;/li&gt;
&lt;li&gt;Data Source: We proactively addressed the absence of a backend API by suggesting mock data for the initial development phase. This keeps the project moving forward while the backend is being developed.&lt;/li&gt;
&lt;li&gt;Performance and Scalability: We considered the potential for the product list to grow over time. While simpler solutions were prioritized for the MVP, we acknowledged the need for future optimizations like pagination or infinite scrolling if the list becomes extensive.&lt;/li&gt;
&lt;li&gt;Responsiveness: We discussed the importance of responsiveness across different devices, but agreed to focus on the desktop version for the MVP, prioritizing timely delivery.&lt;/li&gt;
&lt;li&gt;Documentation: We emphasized the importance of documenting these decisions and assumptions, creating a clear reference point for the team and stakeholders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This clarification process revealed a crucial shift in perspective. Initially, we focused on the technical &lt;strong&gt;&lt;em&gt;what&lt;/em&gt;&lt;/strong&gt; - the specific steps to implement the product list. However, by engaging in discussions and exploring different options, we naturally began to consider the &lt;strong&gt;&lt;em&gt;why&lt;/em&gt;&lt;/strong&gt; - the user's needs and the value this feature would bring to them.&lt;/p&gt;

&lt;p&gt;This shift in focus leads us to the next logical step: framing the requirement as a user story and defining clear acceptance criteria. User stories provide a powerful tool for encapsulating the user's perspective and goals, while acceptance criteria serve as a checklist to ensure we meet those expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summarize and looking ahead
&lt;/h2&gt;

&lt;p&gt;In this article, I've explored the initial steps of building an Angular e-commerce application. By carefully analyzing and clarifying even a seemingly simple requirement like "&lt;em&gt;Display a list of products&lt;/em&gt;", I've demonstrated the importance of understanding the underlying user needs and business goals. I've also highlighted the value of collaborative discussions with stakeholders, documenting decisions, and prioritizing features for the MVP.&lt;/p&gt;

&lt;p&gt;In the next article, I'll dive deeper into the &lt;strong&gt;technical design&lt;/strong&gt; and &lt;strong&gt;implementation&lt;/strong&gt; of the product listing page. We'll explore how to structure our Angular components, fetch data from a mock source, and leverage Angular Material to create a visually appealing and user-friendly interface. Stay tuned for the next article in the series, where I'll continue the journey towards building a successful e-commerce platform&lt;/p&gt;




&lt;p&gt;What are your thoughts on the importance of user stories and acceptance criteria? Have you encountered similar challenges in your own projects? Share your experiences and insights in the comments below!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Angular Form Architecture: Achieving Separation of Concerns with a FormHandlerService</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Fri, 28 Jun 2024 13:28:45 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/angular-form-architecture-achieving-separation-of-concerns-with-a-formhandlerservice-5a7n</link>
      <guid>https://dev.to/cezar-plescan/angular-form-architecture-achieving-separation-of-concerns-with-a-formhandlerservice-5a7n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my previous articles, I've focused on managing the data flow in our Angular user profile form, separating concerns between components and services. We successfully extracted the data access logic into a &lt;code&gt;UserService&lt;/code&gt; and created a &lt;code&gt;HttpHelperService&lt;/code&gt; to handle form data preparation. While this was a great step towards a cleaner architecture, we can further refine our form handling to enhance maintainability and reusability.&lt;/p&gt;

&lt;p&gt;In this article, I'll tackle the challenge of managing form related logic within our Angular component. I'll focus on decoupling this logic from the component core responsibility of UI interactions, ensuring that our code adheres to the &lt;strong&gt;Single Responsibility Principle (SRP)&lt;/strong&gt; and is more adaptable to future changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  What I'll cover here
&lt;/h4&gt;

&lt;p&gt;I'll guide you through the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identifying form handling logic&lt;/strong&gt; - pinpoint specific methods and properties within the &lt;code&gt;UserProfileComponent&lt;/code&gt; that relate to form management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating the &lt;code&gt;FormHandlerService&lt;/code&gt;&lt;/strong&gt; - a new service to house the extracted form handling logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementing reusable methods&lt;/strong&gt; - craft generic and reusable methods in the service to handle tasks like form updates, validation, and state management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrating the service with the component&lt;/strong&gt; - modify the &lt;code&gt;UserProfileComponent&lt;/code&gt; to delegate form related operations to the &lt;code&gt;FormHandlerService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Considering alternatives and best practices&lt;/strong&gt; - explore different approaches for organizing and sharing form handling logic, discussing their pros and cons.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this article, you'll have a deeper understanding of how to apply the Single Responsibility Principle to Angular forms, create reusable services for form logic, and build more maintainable and flexible frontend applications.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;A quick note&lt;/em&gt;:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;This article builds upon the concepts and code developed in previous articles in this series. If you're new here, I highly recommend catching up on the earlier articles to make the most of this one&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;You can find the starting point for the code I'll be working with in the &lt;code&gt;17.error-interceptor&lt;/code&gt; branch of the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/17.error-interceptor"&gt;repository&lt;/a&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dealing with the form logic in the component
&lt;/h2&gt;

&lt;p&gt;As I examine the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/blob/17.error-interceptor/src/app/user-profile/user-profile.component.ts"&gt;user-profile.component.ts&lt;/a&gt; file, I see a lot of code related to form handling. This includes things like updating the form values, displaying error messages, or figuring out whether the form has been changed.&lt;/p&gt;

&lt;p&gt;I want to clean things up and make my code more organized. So, I'm going to follow the &lt;strong&gt;separation of concerns principle&lt;/strong&gt; and move some of this form handling logic out of the component and into a new service. This way, the component can focus on its main job: managing how the form looks and feels for the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding the right pieces to extract
&lt;/h3&gt;

&lt;p&gt;I've spotted two methods that seem like good candidates for moving to a separate service: &lt;code&gt;updateForm()&lt;/code&gt; and &lt;code&gt;setFormErrors()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;updateForm()&lt;/code&gt; method updates the form values, and it doesn't really depend on anything else in the component. It's a pretty general function that could be used with any form.&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The second one, &lt;code&gt;setFormErrors()&lt;/code&gt;, handles setting error messages on the form based on a response from the server. It's also not specific to this component; it could be used with other forms too. &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;There are other methods that work with the form too, like &lt;code&gt;restoreForm&lt;/code&gt;, &lt;code&gt;isFormPristine&lt;/code&gt;, &lt;code&gt;isSaveButtonDisabled&lt;/code&gt;, or &lt;code&gt;isResetButtonDisabled&lt;/code&gt;, but I'll tackle those later. For now, I want to simplify the component and extract the logic from &lt;code&gt;updateForm()&lt;/code&gt; and &lt;code&gt;setFormErrors()&lt;/code&gt; methods into a new service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the &lt;code&gt;FormHandlerService&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I'll use the Angular CLI to create a new service in the &lt;code&gt;src/app/services&lt;/code&gt; folder: &lt;code&gt;ng generate service form-handler&lt;/code&gt;. Then, I'll move the &lt;code&gt;updateForm()&lt;/code&gt; and &lt;code&gt;setFormErrors()&lt;/code&gt; methods into this new service. Here's what the &lt;code&gt;FormHandlerService&lt;/code&gt; looks like after moving the code: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this moment the app is broken because the component doesn't have those methods anymore, and the service doesn't know which form to work on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing the service code
&lt;/h3&gt;

&lt;p&gt;I'll update the two methods to accept the form instance as a parameter: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Additionally, I'll create a separate file &lt;code&gt;src/app/shared/types/form.type.ts&lt;/code&gt; containing the definition of the &lt;code&gt;FormModel&lt;/code&gt; interface: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I've also made the service more flexible by using generic types &lt;code&gt;&amp;lt;FormType extends FormModel, DataType extends FormType&amp;gt;&lt;/code&gt; so that it can work with different kinds of forms and data structures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the &lt;code&gt;UserProfileComponent&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now, let's update our component to use this new service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remove the original &lt;code&gt;updateForm()&lt;/code&gt; and &lt;code&gt;setFormErrors()&lt;/code&gt; methods from the component.&lt;/li&gt;
&lt;li&gt;inject the &lt;code&gt;FormHandlerService&lt;/code&gt; into the component.&lt;/li&gt;
&lt;li&gt;call the service methods where needed, passing in the &lt;code&gt;form&lt;/code&gt; object as an argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the updated component code:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  The power of reusability
&lt;/h3&gt;

&lt;p&gt;With these changes, we've successfully extracted some of the form handling logic into a separate service. This makes the component cleaner, easier to understand, and more focused on its job of managing the UI. The &lt;code&gt;FormHandlerService&lt;/code&gt; is now reusable; we can use it with other forms in our application, too.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;isFormPristine()&lt;/code&gt; method
&lt;/h3&gt;

&lt;p&gt;Next I'll examine the logic in the &lt;code&gt;isFormPristine()&lt;/code&gt; method. It checks if the form value matches the last saved data from the backend. This method relies on two things: the original user data and the current value of the form.&lt;/p&gt;

&lt;p&gt;The logic inside &lt;code&gt;isFormPristine()&lt;/code&gt; isn't specific to our component, so it's a good candidate for moving to the &lt;code&gt;FormHandlerService&lt;/code&gt;. This way, we can reuse it with other forms throughout the application. To make this happen, I'll pass both the form instance and the last saved data as parameters to the method: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Then, I'll update the &lt;code&gt;UserProfileComponent&lt;/code&gt;: remove the &lt;code&gt;isFormPristine()&lt;/code&gt; method and replace it with &lt;code&gt;this.formHandlerService.isFormPristine()&lt;/code&gt; in the two methods where it's used:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Form button logic
&lt;/h3&gt;

&lt;p&gt;But wait, there's more! The logic in the &lt;code&gt;isSaveButtonDisabled()&lt;/code&gt; and &lt;code&gt;isResetButtonDisabled()&lt;/code&gt; methods also looks pretty reusable.  These methods determine when to disable the "Save" and "Reset" buttons, depending on whether the form is valid, pristine, or if a save operation is in progress.&lt;/p&gt;

&lt;p&gt;Let's refactor these too and move them into our service:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And now update the &lt;code&gt;UserProfileComponent&lt;/code&gt;, by simply delegating the calls to these new service methods:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You might be thinking, &lt;em&gt;"Whoa, this is a lot of arguments to pass to the service!"&lt;/em&gt; And you're right, it does look like more code at first. But trust me, this will really pay off when you reuse the &lt;code&gt;FormHandlerService&lt;/code&gt; in other components later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;restoreForm()&lt;/code&gt; method
&lt;/h3&gt;

&lt;p&gt;Taking a closer look at the &lt;code&gt;restoreForm()&lt;/code&gt; method, I see that it's responsible for resetting the form to its initial or pristine state. This logic is directly related to the form and its values, making it another candidate for extraction into our &lt;code&gt;FormHandlerService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By moving this method to the service, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;consolidate the form logic, keeping all form related operations centralized within the &lt;code&gt;FormHandlerService&lt;/code&gt;, adhering to the Single Responsibility Principle (SRP).&lt;/li&gt;
&lt;li&gt;enhance reusability, making the &lt;code&gt;restoreForm&lt;/code&gt; logic available to other components that might need it.&lt;/li&gt;
&lt;li&gt;simplify the component, removing unnecessary code from the &lt;code&gt;UserProfileComponent&lt;/code&gt;, allowing it to focus more on its core UI responsibilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see the refactored code of both the service and the component:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Separate service instance for each component
&lt;/h2&gt;

&lt;p&gt;You might have noticed that all of the service methods currently require the form object as an argument. This can lead to a bit of extra work when calling these methods repeatedly. What if we could pass the form only once when the service is first injected into the component?&lt;/p&gt;

&lt;p&gt;This would involve storing the form object within the service instance itself. To achieve this, we'll need to have multiple instances of the &lt;code&gt;FormHandlerService&lt;/code&gt;, one for each component that injects it. Fortunately, Angular's dependency injection system allows us to easily provide services at the component level, ensuring each component gets its own dedicated instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modifying the &lt;code&gt;FormHandlerService&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Here's the updated service class:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The key changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I've removed &lt;code&gt;{providedIn: 'root'}&lt;/code&gt;. This ensures the service is no longer a singleton shared across the entire application.&lt;/li&gt;
&lt;li&gt;The service now contains the &lt;code&gt;form&lt;/code&gt; property to store the form instance.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;initForm&lt;/code&gt; method allows the component to initialize the service with its specific form instance.&lt;/li&gt;
&lt;li&gt;I've removed the &lt;code&gt;form&lt;/code&gt; parameter from all the other methods and instead used the local reference &lt;code&gt;this.form&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Providing the service at the component level
&lt;/h3&gt;

&lt;p&gt;Here's how to provide the service in the &lt;code&gt;UserProfileComponent&lt;/code&gt;:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Remember to remove the &lt;code&gt;this.form&lt;/code&gt; argument from all the service methods within the component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits and considerations
&lt;/h3&gt;

&lt;p&gt;By creating a separate service instance for each component, we achieve the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reduced overhead - avoid passing the form object to every method call.&lt;/li&gt;
&lt;li&gt;clearer intent - the component interaction with the service is more explicit, as it initializes the service with the form upon creation.&lt;/li&gt;
&lt;li&gt;isolated forms - each component manages its own form independently, preventing conflicts between different forms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  There's a tradeoff
&lt;/h4&gt;

&lt;p&gt;Having multiple instances of a service can &lt;strong&gt;consume more memory&lt;/strong&gt;, especially if we have many components with forms.&lt;/p&gt;

&lt;h4&gt;
  
  
  Balancing flexibility and performance
&lt;/h4&gt;

&lt;p&gt;While this approach offers &lt;strong&gt;isolation&lt;/strong&gt; and &lt;strong&gt;convenience&lt;/strong&gt;, we should consider the potential memory impact in larger applications with numerous forms.&lt;/p&gt;

&lt;p&gt;The best approach will depend on the specific requirements and scale of our application. In many cases, the benefits of isolation and cleaner code outweigh the minor performance overhead of multiple service instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;In this article, I've focused on improving the &lt;code&gt;UserProfileComponent&lt;/code&gt; by moving its form related logic to a dedicated &lt;code&gt;FormHandlerService&lt;/code&gt;. We've accomplished the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recognized that the component was violating the Single Responsibility Principle by handling too many unrelated concerns.&lt;/li&gt;
&lt;li&gt;extracted form related methods like &lt;code&gt;updateForm&lt;/code&gt;, &lt;code&gt;setFormErrors&lt;/code&gt;, &lt;code&gt;isFormPristine&lt;/code&gt;, and button state logic into this new service.&lt;/li&gt;
&lt;li&gt;used TypeScript generics to make the service reusable with different form types and data structures.&lt;/li&gt;
&lt;li&gt;provided the service at the component level to ensure each form has its own isolated instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By separating the form handling logic into a dedicated service, we've made our &lt;code&gt;UserProfileComponent&lt;/code&gt; cleaner, more focused, and easier to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to find the code
&lt;/h3&gt;

&lt;p&gt;The complete code for this refactoring can be found in the &lt;code&gt;18.form-service&lt;/code&gt; branch of the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/18.form-service"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to explore, experiment, and adapt this approach to your own Angular forms. This is just another step in the ongoing journey of refactoring and improving our application architecture.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you have any questions, suggestions, or experiences you'd like to share, please leave a comment below! Let's continue learning and building better Angular applications together.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>forms</category>
      <category>service</category>
    </item>
    <item>
      <title>Error Handling with Angular Interceptors</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Tue, 25 Jun 2024 13:20:45 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/error-handling-with-angular-interceptors-2548</link>
      <guid>https://dev.to/cezar-plescan/error-handling-with-angular-interceptors-2548</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article I'll tackle the challenge of building a robust error handling in our user profile form application. I'll look beyond simple validation errors and dive into a wider array of issues that can arise during the HTTP communication with the backend server. What if there's no network connection, or the server sends us an unexpected response? To ensure a smooth user experience, we need to anticipate these errors and provide clear feedback or recovery options.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'll cover
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The need for comprehensive error handling&lt;/strong&gt; - look beyond validation errors and uncover into the different types of issues that can occur during HTTP communication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The power of interceptors&lt;/strong&gt; - discover how interceptors can act as a central point for managing errors, validating responses, and enhancing security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating and registering an interceptor&lt;/strong&gt; - the process of setting up an Angular interceptor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validating successful responses&lt;/strong&gt; - implement the logic to ensure that the server's 200 OK responses match our expected format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling network errors&lt;/strong&gt; - learn how to detect and manage scenarios where the user loses internet connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tackling other errors&lt;/strong&gt; - explore strategies for handling server-side errors and unexpected issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;A quick note&lt;/em&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;This article builds upon the concepts and code I've developed in previous articles in this series. If you're just joining us, I highly recommend catching up on the earlier articles to make the most of this one.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;You can find the code I'll be working with in the &lt;code&gt;16.user-service&lt;/code&gt; branch of the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/16.user-service"&gt;repository&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Identifying the current issues
&lt;/h2&gt;

&lt;p&gt;So far, I've focused on handling form validation errors within the &lt;code&gt;tapValidationErrors&lt;/code&gt; operator - those 400 Bad Request responses from the server when the form data isn't quite right. However, there are other types of errors that can crop up, and we need a way to deal with them too. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;network errors&lt;/strong&gt; - the "no internet connection" scenario.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;invalid response formats&lt;/strong&gt; - even if the server responds with a 200 status code, the data might not be in the format we expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;unexpected errors&lt;/strong&gt; - the server could return various error codes, such as 4xx or 5xx, but other than the 400 Bad Request, which I already handled in the &lt;code&gt;tapValidationErrors&lt;/code&gt; RxJS operator.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Current error handling limitations
&lt;/h4&gt;

&lt;p&gt;Currently, error handling is primarily managed by the &lt;code&gt;UserProfileComponent&lt;/code&gt;, using the &lt;code&gt;tapError&lt;/code&gt; operator to set an error flag or display a popup message. Additionally, the &lt;code&gt;tapResponseData&lt;/code&gt; operator assumes the response will always be in the expected successful format. We need to expand our error-handling capabilities to cover unexpected scenarios and responses with invalid formats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Angular interceptors
&lt;/h2&gt;

&lt;p&gt;That's where Angular HTTP interceptors come into play. These handy tools let us intercept and handle HTTP requests and responses, giving us greater control over how our application communicates with the backend. &lt;/p&gt;

&lt;p&gt;They allow us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Catch errors globally&lt;/strong&gt; - Instead of handling errors in every component, we can catch them in one place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate response formats&lt;/strong&gt; - We can verify that server responses match our agreed-upon structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle specific error types&lt;/strong&gt; - We can differentiate between various error scenarios (e.g., network errors, authorization errors, server errors) and respond appropriately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhance security&lt;/strong&gt; - We can add headers, tokens, or other security measures to requests and responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating and registering an interceptor
&lt;/h3&gt;

&lt;p&gt;To get started, I'll create an interceptor in the &lt;code&gt;src/app/core/interceptors&lt;/code&gt; folder using the Angular CLI command &lt;code&gt;ng generate interceptor server-error&lt;/code&gt;. This will generate a file named &lt;code&gt;server-error.interceptor.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Next, I need to tell Angular to use this interceptor whenever we make HTTP requests with the &lt;code&gt;HttpClient&lt;/code&gt; service. More specifically, I need to update the &lt;code&gt;app.config.ts&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this point, our interceptor doesn't do anything yet. My first task will be to validate the format of successful responses, which have the HTTP "&lt;strong&gt;200 OK&lt;/strong&gt;" status code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating successful response format
&lt;/h2&gt;

&lt;p&gt;Recall that in the &lt;code&gt;tapResponseData&lt;/code&gt; operator definition I've assumed that successful responses from the server follow a specific format.&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let's recap that format defined within the &lt;code&gt;server.ts&lt;/code&gt; file. It's an object of this type &lt;code&gt;{status: 'ok', data: any}&lt;/code&gt;:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;It's important to note that there is no single, universal standard for RESTful API response formats. Each application can have its own conventions. However, once a format is established, the client (our Angular app) should verify if the server's response complies with it. This helps catch unexpected errors or inconsistencies on the backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the response format validation
&lt;/h3&gt;

&lt;p&gt;Here's the updated interceptor, with the check for the format of 200 OK responses:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;check200ResponseBodyFormat&lt;/code&gt; function verifies if a response matches the expected format. The interceptor taps into the HTTP response stream, checking if the response is a &lt;code&gt;200 OK&lt;/code&gt; and if it fails the format check. If so, it displays an error notification using &lt;code&gt;MatSnackBar&lt;/code&gt; and throws a custom error.&lt;/p&gt;

&lt;p&gt;To see this in action, you can intentionally modify the &lt;code&gt;server.ts&lt;/code&gt; file to return a malformed response with a 200 status code (e.g., change &lt;code&gt;status: 'ok'&lt;/code&gt; to &lt;code&gt;status: 'bad format'&lt;/code&gt;). Then, restart the server and reload the application. The interceptor should detect this error and display the notification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check out the updated code
&lt;/h3&gt;

&lt;p&gt;The updated code incorporating the changes made so far can be found in the repository at &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/faf6536062cca0b4d84afc984dc8e4ce4e47ca9f"&gt;this specific revision&lt;/a&gt;. Feel free to explore the repository to see the full implementation details of the interceptor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling network errors
&lt;/h2&gt;

&lt;p&gt;What happens when the user loses internet connection? This is a common scenario that we need to handle gracefully to provide a good user experience. To catch network errors, I'll leverage the &lt;code&gt;catchError&lt;/code&gt; operator within the interceptor. This operator allows us to intercept errors in the HTTP request/response pipeline and take appropriate action. &lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Here's how I'll modify the interceptor:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Remember that network error detection can be tricky, and this implementation is just one approach. Depending on your application's specific needs, you might need to adjust or expand this logic further.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;catchError&lt;/code&gt; operator intercepts any errors that happen during the request.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;checkNoNetworkConnection&lt;/code&gt; function checks if the error looks like a network issue. This function examines the error object for missing headers, a zero status code, and other clues.&lt;/li&gt;
&lt;li&gt;If it's a network error:

&lt;ul&gt;
&lt;li&gt;Show a friendly message to the user ("No network connection").&lt;/li&gt;
&lt;li&gt;Log the error so we know it happened (for debugging).&lt;/li&gt;
&lt;li&gt;Set a flag &lt;code&gt;wasCaught&lt;/code&gt; on the error to remember that the interceptor already handled it.&lt;/li&gt;
&lt;li&gt;Re-throw the error. This is important! It lets other parts of the app know about the problem. For example, the &lt;code&gt;tapError&lt;/code&gt; operator I created earlier can now use that &lt;code&gt;wasCaught&lt;/code&gt; flag to avoid showing the same message twice.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If it's not a network error, I just re-throw it, letting other parts of the app deal with it in their own way.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Updating the &lt;code&gt;tapError&lt;/code&gt; operator
&lt;/h3&gt;

&lt;p&gt;To ensure that we don't display multiple error notifications for the same error, I'll update the &lt;code&gt;tapError&lt;/code&gt; operator to check the flag &lt;code&gt;wasCaught&lt;/code&gt; on the error object. This flag is set by the interceptor when it catches a network error.&lt;/p&gt;

&lt;p&gt;Here is the updated operator:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Then, in the &lt;code&gt;UserProfileComponent&lt;/code&gt;, I have to update the request stream pipeline in the &lt;code&gt;saveUserData()&lt;/code&gt; method where the operator is used:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Check out the updated code
&lt;/h3&gt;

&lt;p&gt;The updated code with these changes can be found in the repository at &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/15f5d1c17933ea73d48efa91d417747009508060"&gt;this specific revision&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling other error types
&lt;/h2&gt;

&lt;p&gt;While the interceptor manages invalid 200 responses and network issues, other error scenarios can still arise during server communication. For a robust user experience, I need to address these remaining errors as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;I'll enhance the existing interceptor code to handle these additional errors:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;While I won't cover authentication-specific errors (401, 403) in detail here (as these are typically handled by dedicated interceptors), it's important to have a strategy for dealing with unexpected server errors or other potential HTTP issues.&lt;/p&gt;

&lt;p&gt;You might wonder, &lt;strong&gt;why re-throw the error&lt;/strong&gt; after I've handled it in the interceptor? Here's the reasoning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;flexibility - re-throwing the error allows for additional error handling at higher levels of our application; for instance, we might have a global error handler that logs errors or sends them to an error tracking service.&lt;/li&gt;
&lt;li&gt;component specific handling - our individual components might need to take specific actions based on the error; for example, our &lt;code&gt;UserProfileComponent&lt;/code&gt; might want to display a more tailored error message in certain cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Skipping validation errors
&lt;/h3&gt;

&lt;p&gt;One important thing to note is that I'm not going to handle validation errors in the interceptor. Why? Because I already have the &lt;code&gt;tapValidationErrors&lt;/code&gt; operator taking care of those. This operator is designed to catch errors that are related to the data we send to the server. The interceptor will let &lt;code&gt;tapValidationErrors&lt;/code&gt; do its thing and focus on other types of errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing &lt;code&gt;tapError&lt;/code&gt; from the component
&lt;/h3&gt;

&lt;p&gt;Remember the &lt;code&gt;tapError&lt;/code&gt; operator I used in the &lt;code&gt;saveUserData&lt;/code&gt; method? We don't need it anymore. Since we're catching all errors in the interceptor and showing the appropriate messages, there's no need for the component to worry about error handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking Out the Updated Code
&lt;/h3&gt;

&lt;p&gt;You can find the updated code incorporating these error-handling enhancements at the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/200f2f05eb2d089f8836e8b281e5b14ed9b99884"&gt;following revision&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to explore the repository, experiment with the code, and see how these changes improve your Angular application robustness in handling a wider range of HTTP errors!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further resources on Angular interceptors
&lt;/h2&gt;

&lt;p&gt;While I've covered the fundamentals of error handling with Angular interceptors, there's always more to learn. If you're eager to dive deeper into this powerful tool, here are some resources that will help you level up your skills:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@mohsinogen/angular-17-http-interceptors-guide-417e7c8ffada"&gt;Angular 17 HTTP interceptors: A complete guide&lt;/a&gt;: This guide covers everything you need to know about Angular interceptors, from the basics to advanced use cases and best practices.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@santosant/angular-functional-interceptors-3a2a2e71cdef"&gt;Angular functional interceptors&lt;/a&gt;: Explore a modern, functional approach to creating interceptors, leveraging RxJS operators for clean and concise code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/bytebantz/angulars-17-interceptors-complete-tutorial-220k"&gt;Angular's 17 interceptors: Complete tutorial&lt;/a&gt;: This tutorial provides a step-by-step walkthrough of building and using interceptors, with practical examples and explanations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.scaler.com/topics/angular/angular-interceptor/"&gt;What is an Angular interceptor and how to implement it?&lt;/a&gt; This article provides a beginner-friendly introduction to Angular interceptors, explaining their purpose and demonstrating how to create and use them in your applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this article, we've leveled up our Angular user profile form by introducing an error-handling mechanism using an HTTP interceptor. I've tackled common challenges like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invalid response format - making sure the data we get from the server is what we expect, even when the status code is 200 OK.&lt;/li&gt;
&lt;li&gt;network errors - handling those "no internet" moments and giving the user helpful feedback.&lt;/li&gt;
&lt;li&gt;other server errors - catching and displaying messages for those unexpected server hiccups.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Check Out the Code
&lt;/h4&gt;

&lt;p&gt;Ready to see it all in action? The complete code for this error-handling interceptor, along with the custom error classes and helper functions, can be found in the &lt;code&gt;17.error-interceptor&lt;/code&gt; branch of the GitHub &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/17.error-interceptor"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to explore, experiment, and adapt it to your own Angular applications.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading, and happy coding!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>interceptor</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>Isolating user data logic with a UserService</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Thu, 20 Jun 2024 12:08:44 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/refactoring-user-data-service-nop</link>
      <guid>https://dev.to/cezar-plescan/refactoring-user-data-service-nop</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I'll shift the focus to refactor the &lt;strong&gt;user data management&lt;/strong&gt;. Currently, the logic for fetching and saving user data is handled within the component. To create a cleaner, more maintainable architecture, I'll extract this logic into a dedicated service, named &lt;code&gt;UserService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;A quick note&lt;/strong&gt;:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Before we begin, please note that this article builds on concepts and code introduced in previous articles of this series. If you're new here, I highly recommend that you check out those articles first to get up to speed.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The starting point for the code I'll be working with in this article can be found in the &lt;code&gt;15.http-rxjs-operators&lt;/code&gt; branch of the repository &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/15.http-rxjs-operators"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/15.http-rxjs-operators&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  What I'll cover
&lt;/h4&gt;

&lt;p&gt;In this article, I'll guide you through a step-by-step refactoring process to create a dedicated &lt;code&gt;UserService&lt;/code&gt; and enhance our Angular application architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Analyzing the &lt;code&gt;UserProfileComponent&lt;/code&gt;&lt;/strong&gt;: Pinpoint specific areas where data access and manipulation logic can be separated from UI concerns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating the &lt;code&gt;UserService&lt;/code&gt;&lt;/strong&gt;: Generate a dedicated service to handle all interactions with the user data API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling form data&lt;/strong&gt;: Introduce a helper service, &lt;code&gt;HttpHelperService&lt;/code&gt;, to convert raw form data into the format required for API requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eliminating hardcoded URLs&lt;/strong&gt;: Replace hardcoded URLs with dynamic configuration using Angular environment files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managing API paths&lt;/strong&gt;: Establish a clear pattern for organizing and maintaining API paths within the &lt;code&gt;UserService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Addressing the User ID challenge&lt;/strong&gt;: Explore strategies for dynamically determining the user ID without hardcoding it in the service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this article, you'll have a deeper understanding of how to structure your Angular applications to achieve better separation of concerns, improved maintainability, and more reusable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the problem
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/cezar-plescan/user-profile-editor/blob/15.http-rxjs-operators/src/app/user-profile/user-profile.component.ts"&gt;component&lt;/a&gt; handles a wide range of tasks, including performing HTTP requests, processing responses, and managing UI interactions. This approach violates the Single Responsibility Principle (SRP), a fundamental principle of software design that states that a class should have only one reason to change. In our case, the component is doing too much! &lt;/p&gt;

&lt;p&gt;Let's take a closer look at the HTTP related methods in the component class:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;loadUserData()&lt;/code&gt; - fetches user data from the server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getUserData$()&lt;/code&gt; - defines the observable stream for fetching user data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;saveUserData()&lt;/code&gt; - saves updated user data to the server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;saveUserData$()&lt;/code&gt; - defines the observable stream for saving user data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;getUserData$()&lt;/code&gt; and &lt;code&gt;saveUserData$()&lt;/code&gt; methods use the Angular &lt;code&gt;HttpClient&lt;/code&gt; service to interact with the backend API:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;These methods not only return observables but also contain hardcoded endpoint URLs. Maintaining these URLs within the component could become a headache if they change in the future. Therefore, these methods are prime candidates for extraction into a separate service class.&lt;/p&gt;

&lt;p&gt;But what about &lt;code&gt;loadUserData()&lt;/code&gt; and &lt;code&gt;saveUserData()&lt;/code&gt;? They contain even more logic.&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Should we move them into the service too? The answer is no, because they simply respond to requests, describing how the component UI elements will interact with the received data. These methods are tightly coupled to the component's UI. They handle aspects like: setting UI flags, updating form values, or displaying error messages. These tasks are directly related to the presentation layer and user interaction, which should ideally remain within the component's responsibilities.&lt;/p&gt;

&lt;p&gt;I'll extract the reusable, data-centric logic, that is, HTTP requests, into a separate service, while keeping the UI-specific actions within the component. This adheres to the SRP, making the codebase more maintainable and easier to reason about.&lt;/p&gt;

&lt;p&gt;In the next section, I'll create the &lt;code&gt;UserService&lt;/code&gt; class to house the HTTP request logic, leaving the &lt;code&gt;loadUserData()&lt;/code&gt; and &lt;code&gt;saveUserData()&lt;/code&gt; methods in the component to manage the UI interactions based on the service's responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the &lt;code&gt;UserService&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I'll create a service file &lt;code&gt;user.service.ts&lt;/code&gt; in the &lt;code&gt;src/app/services&lt;/code&gt; folder using the Angular CLI command &lt;code&gt;ng generate service user&lt;/code&gt;, then move &lt;code&gt;getUserData$()&lt;/code&gt; and &lt;code&gt;saveUserData$()&lt;/code&gt; methods into it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial content of the service class
&lt;/h3&gt;

&lt;p&gt;Here is the content of the &lt;code&gt;UserService&lt;/code&gt; class, after simply moving the code from the component:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;There are 2 errors here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;TS2304: Cannot find name 'UserDataResponse'&lt;/code&gt; - this is because the type &lt;code&gt;UserDataResponse&lt;/code&gt; was defined within the component; I'll address this by creating a shared file for user data types.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TS2339: Property 'form' does not exist on type 'UserService'&lt;/code&gt; - this error arises because the &lt;code&gt;saveUserData$()&lt;/code&gt; method references the &lt;code&gt;form&lt;/code&gt; property, which belongs to the component; I'll resolve this by carefully considering where the responsibility for preparing form data should reside.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's see how they can be addressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating shared user data types
&lt;/h3&gt;

&lt;p&gt;To resolve the first error, I'll create a dedicated file for user related type definitions. I'll name this file &lt;code&gt;user.type.ts&lt;/code&gt; and place it in the &lt;code&gt;src/app/shared/types/&lt;/code&gt; folder:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Then, in the &lt;code&gt;user.service.ts&lt;/code&gt; file I'll import the &lt;code&gt;UserDataResponse&lt;/code&gt; type:&lt;br&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserDataResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../shared/types/user.type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This solves the first error. Let's tackle the second one now.&lt;/p&gt;
&lt;h3&gt;
  
  
  Delegating form data preparation
&lt;/h3&gt;

&lt;p&gt;The second error is because the &lt;code&gt;form&lt;/code&gt; property doesn't exist in the new service. This makes sense, as the form is created and managed by the component. But how do we get the form data to the service for the HTTP request?&lt;/p&gt;

&lt;p&gt;This situation raises an important design question: &lt;strong&gt;where should the responsibility of processing the form data lie?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My goal is to adhere to the Single Responsibility Principle (SRP) by grouping related functionalities and extracting them into separate entities, allowing the component to focus on its core responsibility: managing the view, as intended by the Angular team. I'll show you my approach for this situation.&lt;/p&gt;

&lt;p&gt;I'll start by analyzing the different players involved in data manipulation and determine where this data processing should occur. I can identify 4 main actors in this process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;component&lt;/em&gt; - it creates and manages the raw form values.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;raw form values&lt;/em&gt; - these are the data entered by the user into the form fields.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;service&lt;/em&gt; - it's responsible for making the HTTP requests to save the user data.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;processed form data&lt;/em&gt; - this is the user data transformed into a &lt;code&gt;FormData&lt;/code&gt; object, suitable for sending in an HTTP request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The data flow can be visualized like this: &lt;em&gt;Component&lt;/em&gt; ==&amp;gt; &lt;em&gt;Form Values&lt;/em&gt; ==&amp;gt; &lt;em&gt;Processed Form Data&lt;/em&gt; ==&amp;gt; &lt;em&gt;Service&lt;/em&gt; ==&amp;gt; &lt;em&gt;HTTP Request&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The raw form values originate within the component and are manipulated by the user through the view. Logically, I can consider these values as an integral part of the component itself.&lt;/p&gt;

&lt;p&gt;The processed form data, however, is necessary for the &lt;code&gt;UserService&lt;/code&gt; to create the HTTP request. Where should the transformation from raw values to &lt;code&gt;FormData&lt;/code&gt; object take place? Let's consider the options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;within the component&lt;/strong&gt;: This would mean the component would be responsible for both managing the view and preparing the data for the request. This would violate the SRP and make the component less focused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;within the service&lt;/strong&gt;: This seems more appropriate. The &lt;code&gt;UserService&lt;/code&gt; would receive the raw form values from the component, process them into a &lt;code&gt;FormData&lt;/code&gt; object, and then use it for the HTTP request. However, if we ever need another service that requires a similar data transformation, we'd have duplicate code, which isn't ideal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;a separate entity&lt;/strong&gt;: This approach is the most flexible and reusable. I could create a helper function or separate service dedicated to generating the &lt;code&gt;FormData&lt;/code&gt; object. This would allow us to reuse this logic in different parts of our application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The remaining question is who should invoke the helper function that generates the &lt;code&gt;FormData&lt;/code&gt; object: the component or the service?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;the component&lt;/em&gt;: If the component calls the function, it would mean it still has some knowledge about how the data is prepared for the request. This could lead to tight coupling and make the component less reusable.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;the service&lt;/em&gt;: Having the service invoke the helper function is a better approach. This way, the component can simply provide the raw form values, and the service takes care of the rest, including data transformation and sending the request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the spirit of &lt;strong&gt;separation of concerns&lt;/strong&gt;, it's more appropriate for the &lt;strong&gt;service&lt;/strong&gt; to invoke the helper function. Here's why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;data ownership&lt;/strong&gt;: the service is responsible for communicating with the backend, and the format of the request data is closely tied to this responsibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;encapsulation&lt;/strong&gt;: keeping the data processing logic within the service encapsulates it, preventing the component from being burdened with unnecessary details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;testability&lt;/strong&gt;: having the service handle data processing makes it easier to write unit tests for both the component and the service independently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;flexibility&lt;/strong&gt;: this approach allows us to potentially reuse the helper function in other scenarios where we need to prepare the &lt;code&gt;FormData&lt;/code&gt; for HTTP requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By delegating the data processing responsibility to another entity, I create a cleaner architecture where each entity focuses on its core function. The component handles the view and user interactions, while the services take care of data preparation and communication with the backend.&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementation of the helper service
&lt;/h3&gt;

&lt;p&gt;Let's see this in practice. I'll create a new service dedicated for HTTP related operations, named &lt;code&gt;HttpHelperService&lt;/code&gt;, in the &lt;code&gt;src/app/services&lt;/code&gt; folder, using the Angular CLI: &lt;code&gt;ng generate service http-helper&lt;/code&gt;. Here is its content:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h3&gt;
  
  
  Update the &lt;code&gt;UserService&lt;/code&gt; class
&lt;/h3&gt;

&lt;p&gt;Here is the updated &lt;code&gt;UserService&lt;/code&gt; class that uses &lt;code&gt;HttpHelperService&lt;/code&gt;:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Update the component class
&lt;/h3&gt;

&lt;p&gt;There are a couple of changes to be made in the &lt;code&gt;user-profile.component.ts&lt;/code&gt; file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remove the type definitions at the beginning of the file&lt;/li&gt;
&lt;li&gt;inject &lt;code&gt;UserService&lt;/code&gt;: &lt;code&gt;private userService = inject(UserService);&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;invoke the two methods of the service&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;this.form.value&lt;/code&gt; as the argument of the &lt;code&gt;this.userService.saveUserData$&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;remove &lt;code&gt;getUserData$()&lt;/code&gt;, &lt;code&gt;saveUserData$()&lt;/code&gt;, and &lt;code&gt;getFormData()&lt;/code&gt; methods if not already done&lt;/li&gt;
&lt;li&gt;import the necessary types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the updated component file:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Check out the refactored code
&lt;/h3&gt;

&lt;p&gt;The updated code incorporating the changes made so far can be found in the repository at this specific revision: &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/58be812de4c5c04d914c12b600cfc9c27f3cee4a"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/58be812de4c5c04d914c12b600cfc9c27f3cee4a&lt;/a&gt;. Feel free to explore the repository to see the full implementation details and how the refactored component and services interact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dealing with hardcoded URLs
&lt;/h2&gt;

&lt;p&gt;As I've discussed earlier, the hardcoded URLs for the API endpoints within the &lt;code&gt;UserService&lt;/code&gt; are less than ideal due to their lack of flexibility, maintainability challenges, and potential security risks. I'll delve deeper into how to solve these issues using &lt;strong&gt;Angular's environment files&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why hardcoded URLs are problematic
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;environment specificity&lt;/strong&gt;: The hardcoded URL ties our service to a specific environment (e.g., &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt;). If we deploy our application to a different server or domain, the URL would be incorrect, and our service would break.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;configuration changes&lt;/strong&gt;: If the base URL for our API changes (e.g., due to server migration or a change in the API version), we'll need to manually update it in every service where it's used, which is error-prone and tedious.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scattered configuration&lt;/strong&gt;: Having URLs scattered throughout our services makes it harder to manage and update them consistently. It becomes a challenge to keep track of all the places where the URL is used, increasing the risk of errors when changes are needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;code duplication&lt;/strong&gt;: If multiple services use the same base URL, we'll likely have duplicate code, violating the DRY (Don't Repeat Yourself) principle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;testing challenges&lt;/strong&gt;: When testing the service, we'll need to mock the API endpoints. Hardcoded URLs can make it more difficult to replace the real API with a mock during testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To tackle these issues, let's take a closer look at the structure of our URLs and discuss who should be responsible for managing them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the URL structure and responsibilities
&lt;/h3&gt;

&lt;p&gt;It's important to understand how our URLs are put together. Let's break down the URL we're using, &lt;code&gt;http://localhost:3000/users/1&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Base URL&lt;/strong&gt;: This is the main address of our API (in this case, &lt;code&gt;http://localhost:3000&lt;/code&gt;). This can vary depending on where the app is running (our computer, a testing server, or the live server). We shouldn't hardcode this in our service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Path&lt;/strong&gt;: This is the part of the URL that tells the server we want to work with users (&lt;code&gt;/users&lt;/code&gt;). This should be the service's responsibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User ID&lt;/strong&gt;: This is the specific user we're dealing with (in this case, user number &lt;code&gt;1&lt;/code&gt;). The user ID might be a &lt;em&gt;fixed value&lt;/em&gt; (e.g., when an admin updates a user's data) or represent the &lt;em&gt;currently logged-in user&lt;/em&gt;. In the latter case, another service, like an &lt;code&gt;AuthService&lt;/code&gt;, should be responsible for managing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding this structure helps us determine who should build the complete URL and where each part should be defined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing the base URL
&lt;/h3&gt;

&lt;p&gt;The best way to manage the base URL is to use &lt;strong&gt;environment files&lt;/strong&gt;. We'll have different files for different environments (development, testing, production). This way, we can easily change the base URL without touching our service code.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating the environment files
&lt;/h4&gt;

&lt;p&gt;Angular's environment files are a powerful mechanism for managing configuration settings across different environments. They allow us to define variables specific to each environment, keeping our codebase adaptable and maintainable.&lt;/p&gt;

&lt;p&gt;Angular comes with a dedicated command for creating these files: &lt;code&gt;ng generate environments&lt;/code&gt;. This will create two files in the &lt;code&gt;src/environments&lt;/code&gt; folder: &lt;code&gt;environment.ts&lt;/code&gt; and &lt;code&gt;environment.development.ts&lt;/code&gt;. At their core, environment files are simply TypeScript files that export a constant object containing our configuration variables.&lt;/p&gt;

&lt;p&gt;Add the following content to the &lt;code&gt;environment.development.ts&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Since we have only one environment right now, I won't use the &lt;code&gt;environment.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;To gain a deeper understanding of how environment files work and how to leverage them effectively in your Angular projects, you can explore the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://youtu.be/o8wCvlj3IHg"&gt;Angular Environment Variables (YouTube)&lt;/a&gt; - This video tutorial provides a step-by-step walkthrough of setting up and using environment files in Angular.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.digitalocean.com/community/tutorials/angular-environment-variables"&gt;Angular Environment Variables&lt;/a&gt; - This DigitalOcean tutorial offers a comprehensive guide on configuring and using environment variables in Angular projects.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.telerik.com/blogs/angular-basics-using-environmental-variables-organize-build-configurations"&gt;Angular Basics: Using Environmental Variables to Organize Build Configurations&lt;/a&gt; - This Telerik blog post explores the fundamentals of environment variables in Angular and how they can help organize your build configurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Usage in the UserService
&lt;/h4&gt;

&lt;p&gt;To access the environment variables in our service, we simply need to import the &lt;code&gt;environment.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../environments/environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But earlier I've said that this file is not used! Under the hood, Angular replaces the content of this file with &lt;code&gt;environment.development.ts&lt;/code&gt; file. This happens because &lt;code&gt;angular.json&lt;/code&gt; file was automatically updated with an additional configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"optimization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"extractLicenses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"fileReplacements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"replace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/environments/environment.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"with"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/environments/environment.development.ts"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in the service class, I'll replace the hardcoded URLs with&lt;br&gt;
&lt;code&gt;${environment.apiBaseUrl}/users/1&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The &lt;code&gt;/users&lt;/code&gt; path
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/users&lt;/code&gt; part of the URL is specific to our API and how it's set up. Since the &lt;code&gt;UserService&lt;/code&gt; talks to the API, it makes sense for this path to live there. I'll define it as a constant within the service, so it's easy to update if needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USERS_API_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;USERS_API_PATH&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/1`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling the User ID
&lt;/h3&gt;

&lt;p&gt;There are primarily two main approaches to providing the user ID to the &lt;code&gt;UserService&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;pass as argument: the component, considering it's aware of the current user context, explicitly passes the user ID as an argument when calling service methods like &lt;code&gt;getUserData$&lt;/code&gt; or &lt;code&gt;saveUserData$&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;service retrieval: the &lt;code&gt;UserService&lt;/code&gt; itself fetches the user ID from another source; this source could be route parameters, an authentication service, or even a state management system like NgRx.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's important to note that, regardless of the method, fetching the user ID isn't the &lt;code&gt;UserService&lt;/code&gt;'s primary responsibility.&lt;/p&gt;

&lt;p&gt;The best method depends on how the application is structured and how complex it is. In this tutorial, to keep things simple, I'll leave the user ID hardcoded for now, as there's no real context to determine which option is the most suitable. However, in a real-world project, you'd choose the option that best fits your specific needs and keeps your code clean and easy to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  See the code in action
&lt;/h3&gt;

&lt;p&gt;To see all of these changes in action and get a better understanding of how the pieces fit together, check out this specific revision in the repository &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/308208159bd28d6ff3a5f4959ba6d867c1ae632a"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/308208159bd28d6ff3a5f4959ba6d867c1ae632a&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we successfully refactored our &lt;code&gt;UserProfileComponent&lt;/code&gt;, tackling the challenge of tightly coupled responsibilities. By extracting the data access and form data preparation logic into dedicated services – &lt;code&gt;UserService&lt;/code&gt; and &lt;code&gt;HttpHelperService&lt;/code&gt; – we've achieved a cleaner and more maintainable codebase.&lt;/p&gt;

&lt;p&gt;Here's what we've accomplished:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved separation of concerns&lt;/strong&gt;: Our &lt;code&gt;UserProfileComponent&lt;/code&gt; is now focused on its core responsibility: managing the user interface and interactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced reusability&lt;/strong&gt;: The extracted services can be easily reused in other parts of our application, saving us time and effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better code organization&lt;/strong&gt;: Our project structure is more modular and easier to navigate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This refactoring effort not only simplifies our current code but also paves the way for future enhancements. With a more streamlined component and reusable services, we can easily add new features or modify existing ones without worrying about unintended side effects.&lt;/p&gt;

&lt;p&gt;Feel free to explore and experiment with the code from this article, available in the &lt;code&gt;16.user-service&lt;/code&gt; branch of the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/16.user-service"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I hope this article has shown you the power of refactoring in improving Angular code quality and maintainability. Please leave your thoughts, questions, or suggestions in the comments below!  Let's keep learning and building better Angular applications together.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>refactoring</category>
      <category>services</category>
    </item>
    <item>
      <title>Building Custom RxJS Operators for HTTP Requests</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Fri, 14 Jun 2024 11:34:17 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/refactoring-rxjs-operators-for-http-streams-25kh</link>
      <guid>https://dev.to/cezar-plescan/refactoring-rxjs-operators-for-http-streams-25kh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article I'll focus on how to efficiently &lt;strong&gt;structure the logic in the HTTP request stream pipelines&lt;/strong&gt; for the loading and saving of the user data. Currently, the entire logic is handled within the &lt;code&gt;UserProfileComponent&lt;/code&gt; class. I'll refactor this to achieve a more declarative and reusable approach.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;A quick note&lt;/strong&gt;:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Before we begin, please note that this article builds on concepts and code introduced in previous articles of this series. If you're new here, I highly recommend that you check out those articles first to get up to speed.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The starting point for the code I'll be working with in this article can be found in the &lt;code&gt;14.image-form-control&lt;/code&gt; branch of the repository &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/14.image-form-control"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/14.image-form-control&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  In this article I'll guide you through:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;How to break down complex logic into smaller, reusable RxJS operators.&lt;/li&gt;
&lt;li&gt;The role of the &lt;code&gt;catchError&lt;/code&gt; operator in error handling.&lt;/li&gt;
&lt;li&gt;Strategies for handling validation errors, upload progress, and successful responses within observable pipelines.&lt;/li&gt;
&lt;li&gt;The benefits of using custom operators for cleaner, more maintainable code.&lt;/li&gt;
&lt;li&gt;How to create custom operators like &lt;code&gt;tapValidationErrors&lt;/code&gt;, &lt;code&gt;tapUploadProgress&lt;/code&gt;, &lt;code&gt;tapResponseBody&lt;/code&gt;, and &lt;code&gt;tapError&lt;/code&gt; to make the code more streamlined and easier to manage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this article, you'll have a deeper understanding of how to leverage custom RxJS operators to manage the intricacies of HTTP requests and responses in your Angular applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the current issues
&lt;/h2&gt;

&lt;p&gt;The code of the &lt;code&gt;saveUserData()&lt;/code&gt; method of the &lt;code&gt;UserProfileComponent&lt;/code&gt; class in the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/blob/14.image-form-control/src/app/user-profile/user-profile.component.ts#L141-L186"&gt;user-profile.component.ts&lt;/a&gt; file looks like this:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The method is quite lengthy and handles multiple tasks, such as detecting response error types and computing upload progress. This violates the Single Responsibility Principle, making the code less maintainable. Ideally, the component should only be concerned with receiving errors, user data, and upload progress, not the intricacies of error handling or progress calculation.&lt;/p&gt;

&lt;p&gt;To address all these, I'll create custom RxJS operators to handle different aspects of the HTTP request stream. This approach will lead to a more declarative and reusable code structure.&lt;/p&gt;

&lt;p&gt;I'll start by implementing an operator for validation error handling, which I'll name &lt;code&gt;tapValidationErrors&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling validation errors: the &lt;code&gt;tapValidationErrors&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;The core idea behind this operator is to apply the &lt;strong&gt;extract method&lt;/strong&gt; refactoring technique. I'll move the validation error handling code from the component into a separate, reusable function that can be used in the observable pipe chain.&lt;/p&gt;

&lt;p&gt;The reason behind naming this operator is that the &lt;em&gt;tap&lt;/em&gt; prefix aligns with the RxJS convention of using it for operators that perform side effects (like logging or triggering actions) without modifying the values in the stream. While this operator doesn't directly modify values, it does perform the side effect of invoking the callback function for validation errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of this approach
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separation of concerns&lt;/strong&gt;: The error handling logic is decoupled from the main subscription logic, improving code organization and readability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: The operator can be easily reused across different observables and components that need to handle validation errors in a similar way, promoting a DRY (Don't Repeat Yourself) approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: The operator provides a clear way to customize the error handling behavior for validation errors without affecting the handling of other types of errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation and usage
&lt;/h3&gt;

&lt;p&gt;Let's create a new file, &lt;code&gt;tap-validation-errors.ts&lt;/code&gt;, in the &lt;code&gt;src/app/shared/rxjs-operators&lt;/code&gt; folder with the following content:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I've also updated the &lt;code&gt;saveUserData()&lt;/code&gt; method in the &lt;code&gt;user-profile.component.ts&lt;/code&gt; file:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Let's break down what I've done here. I'll first examine the method in the component. 
&lt;h3&gt;
  
  
  Simplifying the &lt;code&gt;saveUserData()&lt;/code&gt; method
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;catchError&lt;/code&gt; operator is now responsible for only handling global errors. &lt;/p&gt;

&lt;p&gt;I've introduced the new operator &lt;code&gt;tapValidationErrors&lt;/code&gt; before it. This order is crucial, as placing &lt;code&gt;catchError&lt;/code&gt; before &lt;code&gt;tapValidationErrors&lt;/code&gt; would cause it to catch and handle all errors, including validation errors, preventing &lt;code&gt;tapValidationErrors&lt;/code&gt; from specifically addressing those validation errors. By placing &lt;code&gt;tapValidationErrors&lt;/code&gt; first, I ensure that validation errors are identified and processed &lt;em&gt;before&lt;/em&gt; any other error handling logic.&lt;/p&gt;
&lt;h3&gt;
  
  
  How the operator works
&lt;/h3&gt;

&lt;p&gt;Now let's talk about the logic inside the &lt;code&gt;tapValidationErrors&lt;/code&gt; operator.&lt;/p&gt;

&lt;p&gt;After extracting the validation error handling code from the &lt;code&gt;catchError&lt;/code&gt; block from the &lt;code&gt;saveUserData()&lt;/code&gt; method, I could have simply used two separate &lt;code&gt;catchError&lt;/code&gt; operators in the pipeline: one dedicated to validation errors and the other for general errors. While this would separate the logic, it wouldn't necessarily improve reusability or code organization.&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Instead, I can adopt a more elegant solution by encapsulating the extracted logic into a reusable custom operator. This leverages the power of RxJS operator composition, where we can combine existing operators to create new ones with specialized behavior.&lt;/p&gt;

&lt;p&gt;In this case, the &lt;code&gt;tapValidationErrors&lt;/code&gt; operator is essentially a higher-order function that takes a callback function as an argument and returns a new RxJS operator based on &lt;code&gt;catchError&lt;/code&gt;. This custom operator handles validation errors in a controlled and informative manner, allowing us to perform specific actions like displaying error messages while leaving other error types to be handled elsewhere in the pipeline.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;callback&lt;/code&gt; parameter in the operator definition will be invoked when these errors are detected. In the component method &lt;code&gt;saveUserData()&lt;/code&gt; I specify the exact action to take when validation errors are received from the server, which in this instance is to display the errors in the form.&lt;/p&gt;

&lt;h4&gt;
  
  
  Error handling strategies in &lt;code&gt;catchError&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;There's one crucial aspect in the implementation I want to discuss: the use of &lt;code&gt;return EMPTY&lt;/code&gt; and &lt;code&gt;throw error&lt;/code&gt;. The &lt;code&gt;catchError&lt;/code&gt; operator requires that its inner callback either returns an observable or throws an exception.&lt;/p&gt;

&lt;p&gt;By returning &lt;code&gt;EMPTY&lt;/code&gt;, I explicitly indicate that this error in the stream has been handled within this operator. This prevents the error from propagating further down the observable chain and triggering another error handler, which is not what I expect to happen. &lt;code&gt;EMPTY&lt;/code&gt; is a special observable that immediately completes without emitting any values. By returning this observable, we effectively terminate the current observable stream. This is important because, in the context of a form submission, we don't want to continue processing the response if the server indicates validation errors.&lt;/p&gt;

&lt;p&gt;On the other hand, by using &lt;code&gt;throw error&lt;/code&gt;, I'm explicitly re-throwing errors that are not validation errors. This allows these errors to be caught and handled by a higher-level &lt;code&gt;catchError&lt;/code&gt; operator in our RxJS pipeline or by a global error handler in the application (like the &lt;code&gt;ErrorHandler&lt;/code&gt; injection token or an HTTP interceptor).&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking upload progress: the &lt;code&gt;tapUploadProgress&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;I'll continue to refine the file upload handling by addressing the calculation of upload progress. Currently, this logic resides in the observer in the &lt;code&gt;saveUserData()&lt;/code&gt; method, which, as I discussed earlier, isn't ideal. My goal is to create a separate operator that handles this task exclusively. Building upon the approach I've taken with the &lt;code&gt;tapValidationErrors&lt;/code&gt; operator, I'll create a new file &lt;code&gt;tap-upload-progress.ts&lt;/code&gt; in the same folder. I'll name the operator &lt;code&gt;tapUploadProgress&lt;/code&gt;. Here is the content of the file:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I've removed the extracted code from the &lt;code&gt;saveUserData()&lt;/code&gt; method, and added the new operator:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;By extracting the progress calculation into the &lt;code&gt;tapUploadProgress&lt;/code&gt; operator, I've decluttered the &lt;code&gt;saveUserData()&lt;/code&gt; method and made it more focused on its core responsibilities. This enhances code readability and maintainability while promoting reusability of the progress-tracking logic across other parts of our application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring upload progress tracking
&lt;/h3&gt;

&lt;p&gt;In order to access upload progress information, we need to configure the HTTP request with two specific options: &lt;code&gt;reportProgress: true&lt;/code&gt; and &lt;code&gt;observe: 'events'&lt;/code&gt;: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In &lt;code&gt;HttpClient&lt;/code&gt;, methods like &lt;code&gt;put&lt;/code&gt;, &lt;code&gt;post&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, etc., accept an optional third argument called &lt;code&gt;options&lt;/code&gt;. This argument is an object that allows us to configure various aspects of the HTTP request and how the response is handled. &lt;/p&gt;

&lt;p&gt;With both &lt;code&gt;reportProgress: true&lt;/code&gt; and &lt;code&gt;observe: 'events'&lt;/code&gt;, we get an observable that emits a stream of &lt;code&gt;HttpEvent&lt;/code&gt; objects. Each event represents a different stage of the HTTP request/response lifecycle.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;reportProgress: true&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This option tells &lt;code&gt;HttpClient&lt;/code&gt; to track the progress of the HTTP request, particularly relevant for uploads and downloads.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;reportProgress&lt;/code&gt; is true, &lt;code&gt;HttpClient&lt;/code&gt; emits &lt;code&gt;HttpEventType.UploadProgress&lt;/code&gt; (for uploads) or &lt;code&gt;HttpEventType.DownloadProgress&lt;/code&gt; (for downloads) events as part of the observable stream. These events contain information about the progress, such as loaded bytes and total bytes.&lt;/p&gt;

&lt;p&gt;In the context of our image upload component, this allows us to track the upload progress and provide feedback to the user through the progress bar indicator.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: Even without this option, the code will still work fine, but we'll have no progress indicator. The &lt;code&gt;if&lt;/code&gt; condition in the &lt;code&gt;tapUploadProgress&lt;/code&gt; operator won't be satisfied, and the callback for updating the progress won't be invoked.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;observe: 'events'&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;This option instructs &lt;code&gt;HttpClient&lt;/code&gt; to emit the full sequence of HTTP events instead of just the final response body.&lt;/p&gt;

&lt;p&gt;The emitted events can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HttpEventType.Sent&lt;/code&gt;: The request has been sent to the server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpEventType.UploadProgress&lt;/code&gt; (for uploads): Provides progress information.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HttpEventType.Response&lt;/code&gt;: The response has been received from the server (this contains the data we usually work with).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By observing events, we gain access to more granular information about the HTTP request lifecycle. In our case, we're using it to access the UploadProgress events to track progress and the Response event to get the final server response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extracting the response body with the &lt;code&gt;tapResponseBody&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;Let's make our code even better by introducing another handy tool: the &lt;code&gt;tapResponseBody&lt;/code&gt; operator. This operator helps us grab the data we want from successful responses to our HTTP requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  The code
&lt;/h3&gt;

&lt;p&gt;Here is the content of the &lt;code&gt;tap-response-body.ts&lt;/code&gt; file: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
And the updated &lt;code&gt;saveUserData()&lt;/code&gt; method: &lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Why Use &lt;code&gt;tapResponseBody&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Right now, the &lt;code&gt;saveUserData&lt;/code&gt; method handles successful responses directly inside the &lt;code&gt;subscribe&lt;/code&gt; block. This works, but it can get messy as our form gets more complex. The &lt;code&gt;tapResponseBody&lt;/code&gt; operator cleans things up by separating this logic into a reusable piece.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;tapResponseBody&lt;/code&gt;, we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate response handling: Keep the code that deals with the response data away from the main part of the &lt;code&gt;saveUserData&lt;/code&gt; method. This makes our code tidier and easier to read.&lt;/li&gt;
&lt;li&gt;Reuse the logic: Use the same response handling code for other parts of our app where we need to get data from successful responses.&lt;/li&gt;
&lt;li&gt;Focus on the big picture: Keep the &lt;code&gt;subscribe&lt;/code&gt; part simple and focused on the main actions, while the &lt;code&gt;tapResponseBody&lt;/code&gt; operator handles the fine details of dealing with the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Updating the &lt;code&gt;loadUserData()&lt;/code&gt; method
&lt;/h3&gt;

&lt;p&gt;Now that we've successfully applied the &lt;code&gt;tapResponseBody&lt;/code&gt; operator for the &lt;code&gt;saveUserData()&lt;/code&gt; method, let's see how we can apply it for the &lt;code&gt;loadUserData()&lt;/code&gt; method. I'll take a similar approach and move the code from the &lt;code&gt;subscribe&lt;/code&gt; method into our operator inner callback:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
However, when compiling it, we get a few TypeScript errors:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Understanding the challenge
&lt;/h4&gt;

&lt;p&gt;Why doesn't this work smoothly? The problem lies in how our HTTP requests are set up. Let's take a closer look at the two methods:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
There is a key difference. The save request uses &lt;code&gt;observe: 'events'&lt;/code&gt;, meaning it gives a stream of &lt;code&gt;HttpEvent&amp;lt;UserDataResponse&amp;gt;&lt;/code&gt; objects. But &lt;code&gt;getUserData$()&lt;/code&gt; doesn't speficy this option, so it defaults to &lt;code&gt;observe: 'body'&lt;/code&gt;, which gives us the response data directly. Notice the &lt;code&gt;map&lt;/code&gt; operator which receives values of type &lt;code&gt;UserDataResponse&lt;/code&gt;, but it converts them to &lt;code&gt;UserProfile&lt;/code&gt;.

&lt;p&gt;Our &lt;code&gt;tapResponseBody&lt;/code&gt; operator is designed to work with &lt;code&gt;HttpEvent&lt;/code&gt; objects. This mismatch is causing these TypeScript errors.&lt;/p&gt;

&lt;p&gt;At a high level, I see primarily two ways to address this issue:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;to modify &lt;strong&gt;&lt;code&gt;getUserData$()&lt;/code&gt;&lt;/strong&gt;, or,&lt;/li&gt;
&lt;li&gt;to adapt the &lt;strong&gt;&lt;code&gt;tapResponseBody&lt;/code&gt;&lt;/strong&gt; operator.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Modify the &lt;code&gt;getUserData$()&lt;/code&gt; stream
&lt;/h4&gt;

&lt;p&gt;I could change the GET request to also use &lt;code&gt;observe: 'events'&lt;/code&gt;, just like &lt;code&gt;saveUserData$()&lt;/code&gt;. This would make both requests consistent, and &lt;code&gt;tapResponseBody&lt;/code&gt; would work as is. However, this would also make our operator less flexible; it would only work with observables that emit &lt;code&gt;HttpEvent&lt;/code&gt; objects.&lt;/p&gt;

&lt;p&gt;Here is how this solution could be implemented:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This might be simpler if we only have a few places where we need to handle both &lt;code&gt;HttpEvent&lt;/code&gt; and response body types. On the other hand, we might need to repeat code if we use this pattern in multiple places, and the &lt;code&gt;tapResponseBody&lt;/code&gt; operator would be less reusable.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adapt the &lt;code&gt;tapResponseBody&lt;/code&gt; operator
&lt;/h4&gt;

&lt;p&gt;This solution implies the operator to work with requests no matter the value of the &lt;code&gt;observe&lt;/code&gt; option, which could be one of: &lt;code&gt;"body", "response", "events"&lt;/code&gt;. This would make the operator more versatile and adaptable to different HTTP request configurations. It also offers more flexibility and reusability, especially if we have multiple observables that emit either event or response body types. It also encapsulates the type-handling logic within the operator itself. Of course, the tradeoff is that it requires more development work for adapting the logic inside the operator. &lt;/p&gt;

&lt;p&gt;I'll choose the more flexible route and enhance the operator to handle both scenarios. For improved clarity, I'll rename it to &lt;code&gt;tapResponseData&lt;/code&gt;. Here's the updated code:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Take a moment to read the comments in the code – they explain how I've improved the operator to handle different types of responses.

&lt;p&gt;The generic type &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt; in the operator now represents the specific type of data we expect from the response, &lt;code&gt;UserDataResponse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, I've created new files with different data types and interfaces:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here is the updated component where the operator is used:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Since &lt;code&gt;tapResponseData&lt;/code&gt; now handles different kinds of responses, the other two operators &lt;code&gt;tapValidationErrors&lt;/code&gt; and &lt;code&gt;tapUploadProgress&lt;/code&gt; need to be updated too. The fix is to specify the new type &lt;code&gt;HttpClientResponse&amp;lt;T&amp;gt;&lt;/code&gt;, instead of the old one&lt;code&gt;HttpEvent&amp;lt;T&amp;gt;&lt;/code&gt;, which was available only when the &lt;code&gt;observe&lt;/code&gt; option was set to &lt;code&gt;'events'&lt;/code&gt;. Here are their updated code:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You can test the code with different settings for the &lt;code&gt;observe&lt;/code&gt; option in both load and save requests. The code should successfully handle all scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling made easy: the &lt;code&gt;tapError&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;To improve the error handling and make the code more compact, I'll introduce a new custom RxJS operator called &lt;code&gt;tapError&lt;/code&gt;. This operator will serve as a dedicated mechanism for handling errors in HTTP request streams, like replacing the &lt;code&gt;catchError&lt;/code&gt; block in the &lt;code&gt;loadUserData()&lt;/code&gt; or &lt;code&gt;saveUserData()&lt;/code&gt; methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  The purpose of &lt;code&gt;tapError&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The primary goal of the &lt;code&gt;tapError&lt;/code&gt; operator is to execute specific actions when an error occurs within an observable stream. In our case, the &lt;code&gt;loadUserData()&lt;/code&gt; method needs to be notified when an HTTP error happens so we can set an error flag in the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Here's the implementation of the operator:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
The usage in the component is straightforward:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;One important distinction: the &lt;code&gt;tapError&lt;/code&gt; operator is designed for single use within a stream. Why? Because the stream will terminate immediately after the operator handles the error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error handling strategies in RxJS
&lt;/h3&gt;

&lt;p&gt;There are several ways to respond to errors in RxJS streams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;catchError&lt;/code&gt; operator: this is the most common and flexible way to handle errors. It allows us to catch errors and decide how to proceed, either by returning a new observable, emitting a fallback value, or throwing the error again.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tap&lt;/code&gt; operator with &lt;code&gt;error&lt;/code&gt; callback: executes a side effect when an error occurs but allows the error to propagate further.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subscribe&lt;/code&gt; method's &lt;code&gt;error&lt;/code&gt; callback: Handles the error at the end of the observable chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;catchError&lt;/code&gt; is the right tool
&lt;/h3&gt;

&lt;p&gt;Here is an alternative implementation of the &lt;code&gt;tapError&lt;/code&gt; with the &lt;code&gt;tap&lt;/code&gt; operator and the &lt;code&gt;error&lt;/code&gt; callback: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
If you use this implementation, the behavior of the &lt;code&gt;loadUserData()&lt;/code&gt; would remain the same.

&lt;p&gt;In our scenario, I'm interested in both handling the error (setting the &lt;code&gt;hasLoadingError&lt;/code&gt; flag) and stopping the error from propagating. This aligns perfectly with the purpose of &lt;code&gt;catchError&lt;/code&gt;. Here's why &lt;code&gt;tap&lt;/code&gt; with the &lt;code&gt;error&lt;/code&gt; callback wouldn't be ideal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uncontrolled error propagation: The &lt;code&gt;tap&lt;/code&gt; operator doesn't stop errors from continuing down the stream. This means the error would still reach the &lt;code&gt;subscribe&lt;/code&gt; block &lt;code&gt;error&lt;/code&gt; callback, potentially causing duplicate error handling and unexpected behavior.&lt;/li&gt;
&lt;li&gt;limited control: While &lt;code&gt;tap&lt;/code&gt; allows us to perform actions in response to errors, it doesn't let us change the stream's behavior fundamentally. In our case, we want to stop the stream after an error, which &lt;code&gt;tap&lt;/code&gt; can't do.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using &lt;code&gt;catchError&lt;/code&gt; with &lt;code&gt;return EMPTY&lt;/code&gt;, we achieve clear error handling and explicit stream termination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: A cleaner, more maintainable approach
&lt;/h2&gt;

&lt;p&gt;In this article, I've shown you how custom RxJS operators can make the Angular code much cleaner and easier to work with. I created special operators like &lt;code&gt;tapValidationErrors&lt;/code&gt;, &lt;code&gt;tapUploadProgress&lt;/code&gt;, &lt;code&gt;tapResponseData&lt;/code&gt; and &lt;code&gt;tapError&lt;/code&gt; to handle different parts of HTTP requests.&lt;/p&gt;

&lt;p&gt;By using these custom operators, we've made our code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easier to understand - each operator does one specific job, making it simpler to read and follow the logic.&lt;/li&gt;
&lt;li&gt;reusable - we can use these operators in other parts of the project, saving us time and effort.&lt;/li&gt;
&lt;li&gt;more flexible - we can now easily change how we handle errors or responses without affecting other parts of the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to explore and experiment with the code from this article, available in the &lt;code&gt;15.http-rxjs-operators&lt;/code&gt; branch of the repository: &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/15.http-rxjs-operators"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/15.http-rxjs-operators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope this article helps you see how awesome custom RxJS operators are. Feel free to use these ideas in your own Angular projects and let me know if you have any questions or comments. Let's keep learning and improving together as Angular developers.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>rxjs</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>Enhancing Angular Reactive Forms with a Custom Image Uploader FormControl</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Mon, 10 Jun 2024 17:14:18 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-3-refactoring-image-uploading-3ndi</link>
      <guid>https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-3-refactoring-image-uploading-3ndi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, I'll guide you through building a special component that makes &lt;strong&gt;uploading images&lt;/strong&gt; in forms much easier. This component won't just make the form templates look cleaner, it'll also work seamlessly with the existing reactive forms setup, letting you treat image uploads just like any other form field.&lt;/p&gt;

&lt;p&gt;Throughout this article, I'll walk you through the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creating the component&lt;/strong&gt;: Generate the new component and migrate the existing code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Displaying the saved image&lt;/strong&gt;: Implement the logic to display the currently saved image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selecting a new image&lt;/strong&gt;: Add functionality to allow users to choose and preview a new image before uploading it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form integration&lt;/strong&gt;: Integrate the custom component with Angular reactive forms system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removing hardcoded paths&lt;/strong&gt;: Refactor the code to avoid hardcoding image URLs and instead fetch them from the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom upload button&lt;/strong&gt;: Create a custom upload button for a consistent look across browsers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;A quick note&lt;/strong&gt;: Before we begin, I'd like to remind you that this article builds on concepts and code introduced in previous articles of this series. If you're new here, I highly recommend that you check out those articles first to get up to speed. You can find the starting point for the code I'll be working with in the &lt;code&gt;13.validation-error-directive&lt;/code&gt; branch of the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/13.validation-error-directive" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/13.validation-error-directive&lt;/a&gt; repository.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the current issues
&lt;/h2&gt;

&lt;p&gt;Let's take a look at our form in the &lt;code&gt;user-profile.component.ts&lt;/code&gt; file, which includes controls for name, email, address and avatar:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
When I examine the template in &lt;code&gt;user-profile.component.html&lt;/code&gt; I can easily identify the &lt;code&gt;input&lt;/code&gt; elements associated with the first 3 controls, thanks to the &lt;code&gt;formControlName&lt;/code&gt; directive provided by Angular's Reactive Forms Module.&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
These &lt;code&gt;input&lt;/code&gt; elements provide both the display and modification of the form control value. However, when it comes to images, my current approach separates these functionalities.&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I rely on an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag to display the existing image, but for uploading a new image, I use a separate &lt;code&gt;&amp;lt;input type="file"/&amp;gt;&lt;/code&gt; element. This fragmented approach makes the code less maintainable and creates an unintuitive user experience. From my point of view, users should have a clear and consistent way to interact with an image in a form, whether they're viewing the current image or selecting a new one for upload.

&lt;p&gt;To address this, I'll create a dedicated Angular component to manage both image display and upload. I want this component to be able to receive the &lt;code&gt;formControlName&lt;/code&gt; directive, just like any other form control.&lt;/p&gt;

&lt;p&gt;But why do I create a component instead of a directive, like I did for the validation errors in the &lt;a href="https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-2-refactoring-validation-errors-4mnk"&gt;previous article&lt;/a&gt;? While directives are excellent for manipulating existing elements, a component provides both a view (for displaying the image and selecting a new one for uploading) and its associated logic.&lt;/p&gt;

&lt;p&gt;By extracting this functionality into a reusable component, we'll achieve some advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified templates&lt;/strong&gt;: our form templates become cleaner and more focused on the overall structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Responsibility Principle&lt;/strong&gt;: the component adheres to SRP, as it encapsulates all the logic and behavior related to image handling, keeping the parent component concerns separate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: we can easily use this component in any form throughout our application. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's explore the steps involved in creating this dedicated image form control component.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing the new component
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create the new component files
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Generate the component: I begin by generating a component named &lt;strong&gt;&lt;code&gt;image-form-control&lt;/code&gt;&lt;/strong&gt; within the &lt;code&gt;src/app/shared/components&lt;/code&gt; folder, using the Angular CLI: &lt;code&gt;ng generate component image-form-control&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Migrate Existing Code: Next, I'll transfer the relevant code from the form template &lt;code&gt;user-profile.component.html&lt;/code&gt; into &lt;code&gt;image-form-control.component.html&lt;/code&gt;. Similarly, I'll move the associated methods (&lt;code&gt;getAvatarFullUrl&lt;/code&gt; and &lt;code&gt;onImageSelected&lt;/code&gt;) from the &lt;code&gt;user-profile.component.ts&lt;/code&gt; component into &lt;code&gt;image-form-control.component.ts&lt;/code&gt;. Remember to remove these methods from the &lt;code&gt;UserProfileComponent&lt;/code&gt; class.&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Integrate the component: Now, I'll update the form template in &lt;code&gt;user-profile.component.html&lt;/code&gt; to use the &lt;code&gt;ImageFormControlComponent&lt;/code&gt;:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this stage the application is broken. We still have some work to do to make the new component fully functional. &lt;/p&gt;

&lt;p&gt;The first visible error is that the &lt;code&gt;form&lt;/code&gt; property doesn't exist in the &lt;code&gt;ImageFormControlComponent&lt;/code&gt;. Additionally, we need to instruct this component to interact with the reactive form through the &lt;code&gt;formControlName&lt;/code&gt; directive. In the following sections I'll describe how to resolve these challenges and make the component a fully functional form control.&lt;/p&gt;

&lt;p&gt;I'll break down the implementation into two main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;displaying the saved image&lt;/li&gt;
&lt;li&gt;selecting a new image to upload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: This new component is essentially a &lt;strong&gt;custom form control&lt;/strong&gt;. If you're new to this topic, I highly recommend you to refer to this &lt;a href="https://blog.angular-university.io/angular-custom-form-controls/" rel="noopener noreferrer"&gt;comprehensive guide&lt;/a&gt; from the Angular University blog. My implementation will simply follow the steps from this guide.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Displaying the image
&lt;/h3&gt;

&lt;p&gt;Let's take a closer look at how our component is used in the form template:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
We notice that it only has one input property, &lt;code&gt;formControlName="avatar"&lt;/code&gt;, without any direct reference to the form or the avatar form control itself. Stay with me to explore how to connect our component to the Reactive Forms setup.

&lt;p&gt;For now, I'll just use the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag in our template and suppose that the image source comes from an &lt;code&gt;imgSrc&lt;/code&gt; property:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Now I need to define how the &lt;code&gt;imgSrc&lt;/code&gt; property is set in the &lt;code&gt;ImageFormControlComponent&lt;/code&gt; class. Following the &lt;a href="https://blog.angular-university.io/angular-custom-form-controls/" rel="noopener noreferrer"&gt;Angular custom form controls guide&lt;/a&gt;, I need to perform several steps:

&lt;ol&gt;
&lt;li&gt;declare the &lt;code&gt;imgSrc&lt;/code&gt; property in the component class&lt;/li&gt;
&lt;li&gt;the component class should implement the &lt;code&gt;ControlValueAccessor&lt;/code&gt; &lt;a href="https://angular.dev/api/forms/ControlValueAccessor" rel="noopener noreferrer"&gt;interface&lt;/a&gt;; this helps to communicate with the reactive forms system&lt;/li&gt;
&lt;li&gt;implement the &lt;code&gt;writeValue&lt;/code&gt; method of the interface (I'll ignore the other methods for now); this method will be called by Angular to set the initial image source when the form loads&lt;/li&gt;
&lt;li&gt;register the component in the dependency Injection system; this tells Angular that this component should act like a form control&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Now, our component can read the avatar value from the form and display the image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The &lt;code&gt;writeValue&lt;/code&gt; method contains a hardcoded string for the images path. This is not ideal and I'll address it in a later section.&lt;/p&gt;

&lt;p&gt;To understand how our custom form control integrates with Angular reactive forms, let's dive a bit deeper into the internals.&lt;/p&gt;
&lt;h4&gt;
  
  
  NG_VALUE_ACCESSOR injection token
&lt;/h4&gt;

&lt;p&gt;Let's see why we need this token and how it is used internally. I'll jump straight into the &lt;code&gt;formControlName&lt;/code&gt; directive &lt;a href="https://github.com/angular/angular/blob/17.3.3/packages/forms/src/directives/reactive_directives/form_control_name.ts#L133" rel="noopener noreferrer"&gt;source code&lt;/a&gt;. The relevant line is in the constructor definition:&lt;br&gt;
&lt;code&gt;@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What does this actually mean?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;NG_VALUE_ACCESSOR&lt;/code&gt;&lt;/strong&gt; is an injection token provided by Angular. Its purpose is to act as a lookup key for finding the appropriate &lt;code&gt;ControlValueAccessor&lt;/code&gt; implementation for a given form control. In the component &lt;code&gt;providers&lt;/code&gt; array, we're essentially saying, "Hey Angular, when you encounter a form control that needs a &lt;code&gt;ControlValueAccessor&lt;/code&gt;, use my &lt;code&gt;ImageFormControlComponent&lt;/code&gt; as the implementation." When Angular processes the &lt;code&gt;formControlName&lt;/code&gt; directive, it looks for a &lt;code&gt;NG_VALUE_ACCESSOR&lt;/code&gt; provider in the injector hierarchy. Since we've registered our component as the provider, Angular knows how to use the component methods (writeValue, registerOnChange, etc.) to interact with the form control.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;formControlName&lt;/code&gt; directive, when applied to an element (in our case, the &lt;code&gt;app-image-form-control&lt;/code&gt; component), uses the Angular dependency injection system to look for providers of the &lt;code&gt;NG_VALUE_ACCESSOR&lt;/code&gt; token on that specific element itself. It's not looking for a global provider or one in a parent component. This is indicated by the &lt;code&gt;@Self()&lt;/code&gt; decorator on the injection site in the directive's constructor.&lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;formControlName&lt;/code&gt; directive is applied to our component, it finds this provider and uses it to get an instance of the &lt;code&gt;ControlValueAccessor&lt;/code&gt;. Since we've provided our component itself, the directive gets a direct reference to it.&lt;/p&gt;
&lt;h4&gt;
  
  
  Executing the &lt;code&gt;writeValue&lt;/code&gt; method
&lt;/h4&gt;

&lt;p&gt;Here is a simplified process of how the method is called internally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Form Control Setup: When we create a &lt;code&gt;FormControl&lt;/code&gt; in our component (either directly or through &lt;code&gt;FormBuilder&lt;/code&gt;) and bind it to your custom form control using &lt;code&gt;formControlName&lt;/code&gt; in the template, Angular establishes a connection between them.&lt;/li&gt;
&lt;li&gt;Initial Value Setting: If we've provided an initial value to the &lt;code&gt;FormControl&lt;/code&gt; (e.g., through the &lt;code&gt;value&lt;/code&gt; property or &lt;code&gt;setValue&lt;/code&gt; method), Angular will call the &lt;code&gt;writeValue&lt;/code&gt; method on our custom form control (the &lt;code&gt;ImageFormControlComponent&lt;/code&gt;) to set that initial value.&lt;/li&gt;
&lt;li&gt;Value Changes: Whenever the value of the &lt;code&gt;FormControl&lt;/code&gt; changes (e.g., through user input, &lt;code&gt;setValue&lt;/code&gt;, or &lt;code&gt;patchValue&lt;/code&gt;), Angular will again call &lt;code&gt;writeValue&lt;/code&gt; on our component to update it with the new value. This ensures that the UI element stays in sync with the form control's value.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Selecting an image to upload
&lt;/h3&gt;

&lt;p&gt;The current template contains only the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag. For selecting an image file to upload I have to add the &lt;code&gt;&amp;lt;input type="file"/&amp;gt;&lt;/code&gt; element back into the template:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I've also included the handler &lt;code&gt;onImageSelected&lt;/code&gt; for the &lt;code&gt;change&lt;/code&gt; event, that will be triggered when the user selects a file. This is necessary to display the selected image in place of the original one, before uploading it to the server. Here is its implementation:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I want to upload the image to the server only when the user explicitly submits the form. Why do I do this? Because the user could change their mind after selecting an image and I don't want to immediately send the image to the server, but only when the user is ready to save the form.
&lt;h4&gt;
  
  
  Serving files locally
&lt;/h4&gt;

&lt;p&gt;The selected image file doesn't exist on the server yet and you might wonder where it is served from. The answer lies in the &lt;code&gt;URL.createObjectURL(file)&lt;/code&gt; method of the browser's API. This method takes a &lt;code&gt;File&lt;/code&gt; object (or a &lt;code&gt;Blob&lt;/code&gt;) and generates a URL string. This URL doesn't point to a physical file on our server; instead, it references the file data directly in the browser's memory.&lt;/p&gt;

&lt;p&gt;The generated URL is a special type called a blob URL (e.g., &lt;code&gt;blob:http://localhost:4200/d9856eeb-2405-4388-8894-064e56c254a8&lt;/code&gt;). We can use this URL as the &lt;code&gt;src&lt;/code&gt; attribute of an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag to display the selected image in the browser without needing to upload it to a server first. You can verify this by inspecting the &lt;a href="" class="article-body-image-wrapper"&gt;&lt;img&gt;&lt;/a&gt; element in DevTools after selecting an image file; you'll notice the &lt;code&gt;src&lt;/code&gt; attribute value has a format similar to the example above.&lt;/p&gt;
&lt;h4&gt;
  
  
  Revoking blob URLs
&lt;/h4&gt;

&lt;p&gt;Blob URLs are temporary. They only exist as long as the document in which they were created remains open. Once the document is closed or navigated away from, the blob URL is automatically revoked by the browser. It's a good practice to release blob URLs when they are no longer needed using &lt;code&gt;URL.revokeObjectURL()&lt;/code&gt;. This helps avoid potential memory leaks, especially if you're dealing with large image files. Let's see how to incorporate this method in our component:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
To verify that the blob URLs are indeed being discarded, follow these steps:

&lt;ul&gt;
&lt;li&gt;select an image file&lt;/li&gt;
&lt;li&gt;go to DevTools and open the image source URL in a new tab&lt;/li&gt;
&lt;li&gt;select another image from the form&lt;/li&gt;
&lt;li&gt;go to the previously opened tab and refresh it&lt;/li&gt;
&lt;li&gt;you should no longer be able to view the previous image, confirming that the blob URL has been revoked&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Notify the form about the new image selection
&lt;/h4&gt;

&lt;p&gt;At this stage, you might have noticed that the Save and Reset buttons don't become active after selecting a new image. This isn't what we expect. When typing into the text input fields, the buttons become active, and we expect the same behavior when selecting a new image. This is because the form doesn't yet know that we've changed the image. &lt;/p&gt;

&lt;p&gt;Let's see how to address this. The reactive forms system provides a way to notify the form control that its value has changed by calling a function that Angular registers with our custom form control. The &lt;a href="https://blog.angular-university.io/angular-custom-form-controls/" rel="noopener noreferrer"&gt;Angular custom form controls guide&lt;/a&gt; provides a detailed explanation of this process. &lt;/p&gt;

&lt;p&gt;Here is the updated &lt;code&gt;image-form-control.component.ts&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I've also added a template reference variable for the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element, which is used in the component as a &lt;code&gt;ViewChild&lt;/code&gt;:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
In the &lt;code&gt;user-profile.component.ts&lt;/code&gt; file I've removed all references to &lt;code&gt;fileInput&lt;/code&gt; property, as it's now managed entirely by the &lt;code&gt;ImageFormControlComponent&lt;/code&gt;; two methods were affected:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now I'll explain the key changes step by step:&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;registerOnChange&lt;/code&gt; method
&lt;/h5&gt;

&lt;p&gt;Angular calls the &lt;code&gt;registerOnChange&lt;/code&gt; method, part of the &lt;code&gt;ControlValueAccessor&lt;/code&gt; interface, internally. The only action required here is to store a reference to the internal function that will be used to notify the form control when its value changes:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Now, we need to call this stored function whenever the user selects a new image. In the &lt;code&gt;onImageSelected&lt;/code&gt; method I've added &lt;code&gt;this.onChange?.(file)&lt;/code&gt; which will internally notify the form control about the change. This results in the form buttons becoming enabled after selecting an image.

&lt;h5&gt;
  
  
  Clear the image filename after upload
&lt;/h5&gt;

&lt;p&gt;There's one refinement to make here. Currently, after the form is submitted with a new image, the filename remains displayed in the file input element. To fix this, we need to clear the file input value after the form is submitted and the image is uploaded. To achieve this, we have to modify the &lt;code&gt;writeValue&lt;/code&gt; method to also clear the file input. &lt;/p&gt;

&lt;p&gt;First, we need access to the file input element in the template. I've attached a template reference variable to the element &lt;code&gt;&amp;lt;input **#fileInput** ... /&amp;gt;&lt;/code&gt;. Then, to access it in the component I've used the &lt;code&gt;@ViewChild&lt;/code&gt; decorator &lt;code&gt;@ViewChild('fileInput', {static: true}) protected fileInput!: ElementRef&amp;lt;HTMLInputElement&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, to clear the filename, I've simply set its value to an empty string within the &lt;code&gt;writeValue&lt;/code&gt; method: &lt;code&gt;this.fileInput.nativeElement.value = ''&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With these changes, the image upload component is now integrated with the reactive form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements
&lt;/h2&gt;

&lt;p&gt;In this section I'll explore some enhancements we can make to our image form control component, taking its functionality to a new level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom upload button
&lt;/h3&gt;

&lt;p&gt;The current implementation of selecting an image to upload is rendered differently across browsers. There's no way of consistently style a plain input of type file. A solution is to hide the default input element and create a custom button that triggers it behind the scenes.&lt;/p&gt;

&lt;p&gt;Here is the updated template:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I've added some styling to the component elements:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Don't forget to import the necessary Angular Material modules &lt;code&gt;MatButtonModule&lt;/code&gt; and &lt;code&gt;MatIconModule&lt;/code&gt; into the component.

&lt;p&gt;The trick is that the custom button delegates the click event to the hidden file input. With this approach we can fully customize the appearance of the upload button. If necessary, the filename of the selected image can be extracted and displayed, using additional logic in the template and the component, but I've omitted that for simplicity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove hardcoded path for image URLs
&lt;/h3&gt;

&lt;p&gt;In the previous implementation, I've hardcoded the path for image URLs within the component. However, it's not ideal to have the client-side responsible for constructing URLs. Ideally, the server should provide the complete URL for each image.&lt;/p&gt;

&lt;p&gt;There are several advantages of this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment Flexibility&lt;/strong&gt;: By dynamically generating URLs, our application becomes more adaptable to different environments (development, staging, production) without requiring manual changes to hardcoded paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Maintainability&lt;/strong&gt;: Centralizing URL generation on the server makes it easier to manage and update image paths if needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address this, I've updated the &lt;code&gt;server.ts&lt;/code&gt; file. When the server sends a response containing the user data, it will include the full URL for the avatar image, while keeping only the filename stored in the &lt;code&gt;db.json&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
With this change, the &lt;code&gt;writeValue&lt;/code&gt; method in the component becomes simpler:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Remember to restart the server to see these changes.

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;p&gt;Here are some additional resources that will help you dive deeper into the concepts covered in this article.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom form control
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.angular-university.io/angular-custom-form-controls/" rel="noopener noreferrer"&gt;Angular Custom Form Controls - Complete Guide&lt;/a&gt;: A comprehensive guide to creating custom form controls in Angular, covering the ControlValueAccessor interface, validation, and more.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://angularindepth.com/posts/1055/never-again-be-confused-when-implementing-controlvalueaccessor-in-angular-forms" rel="noopener noreferrer"&gt;Never Again Be Confused When Implementing ControlValueAccessor in Angular Forms&lt;/a&gt;: An in-depth article explaining the intricacies of the ControlValueAccessor interface and how to use it effectively.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@oluwaetosin/custom-form-controls-in-angular-d5a63d1a1d60" rel="noopener noreferrer"&gt;Custom Form Controls in Angular&lt;/a&gt;: A practical guide with examples on building various types of custom form controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  File uploading
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.angular-university.io/angular-file-upload/" rel="noopener noreferrer"&gt;Angular file upload&lt;/a&gt;: A detailed tutorial on file uploads in Angular, including how to create custom upload buttons.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://imagekit.io/blog/how-to-upload-files-in-html/" rel="noopener noreferrer"&gt;How to upload files in HTML?&lt;/a&gt;: A comprehensive guide to file uploads in HTML, covering the basics of file input elements, JavaScript interactions, and best practices.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications" rel="noopener noreferrer"&gt;A comprehensive overview of the File API and its capabilities for file selection and reading&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Custom upload button
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/faddalibrahim/how-to-create-a-custom-file-upload-button-using-html-css-and-javascript-1c03"&gt;How to create a custom file upload button using HTML, CSS, and JavaScript&lt;/a&gt;: A step-by-step guide with code examples on building a custom upload button.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/572768/styling-an-input-type-file-button" rel="noopener noreferrer"&gt;Styling an input type="file" button&lt;/a&gt;: A Stack Overflow discussion on different ways to style the file input button.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: Building powerful forms with custom controls
&lt;/h2&gt;

&lt;p&gt;In this article, we've taken a significant step towards mastering Angular forms by creating a reusable &lt;code&gt;ImageFormControlComponent&lt;/code&gt;. We've seen how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transform a basic image upload element into a full-fledged form control.&lt;/li&gt;
&lt;li&gt;Integrate it seamlessly with Angular reactive forms system.&lt;/li&gt;
&lt;li&gt;Handle image display, selection, and preview.&lt;/li&gt;
&lt;li&gt;Ensure efficient memory management with blob URLs.&lt;/li&gt;
&lt;li&gt;Address common challenges like cross-browser styling inconsistencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This custom component not only improves our code but also enhances the user experience by providing a clear and consistent way to manage image uploads within your forms.&lt;/p&gt;

&lt;p&gt;I encourage you to experiment with the code from this article and explore the possibilities for further enhancements. You can find the code for this project at: &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/14.image-form-control" rel="noopener noreferrer"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/14.image-form-control&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions, suggestions, or experiences you'd like to share, please leave a comment below! Let's continue the conversation and learn together.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>fileupload</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>Simplifying Error Handling in Angular Forms with a Custom Directive</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Wed, 05 Jun 2024 08:24:27 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-2-refactoring-validation-errors-4mnk</link>
      <guid>https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-2-refactoring-validation-errors-4mnk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my previous &lt;a href="https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-1-4m1c"&gt;article&lt;/a&gt;, I walked you through building an Angular user profile editor. While the initial implementation got the job done, there's always room for improvement. Today, I'm taking the next step in optimizing our form by refactoring the way the validation error messages are displayed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;A quick note&lt;/strong&gt;: If you're new to this series, you might find it helpful to catch up on the previous article. It'll provide you with the context and foundation you need to fully grasp the concepts I'll cover here&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you're new to the concept, &lt;em&gt;refactoring&lt;/em&gt; is the process of restructuring existing code without changing its external behavior. So, why bother with refactoring? &lt;/p&gt;

&lt;p&gt;The benefits are significant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced readability&lt;/strong&gt;: Refactored code is often more concise and easier to understand, making it simpler for us to maintain and extend the codebase in the future.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved maintainability&lt;/strong&gt;: By identifying and eliminating duplicate code, clarifying responsibilities, and simplifying complex logic, refactoring makes our code less prone to bugs and easier to update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increased reusability&lt;/strong&gt;: Refactoring often involves identifying common patterns and extracting them into reusable components or functions. This saves us time and effort in the long run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better performance&lt;/strong&gt;: In some cases, refactoring can lead to performance optimizations by eliminating redundancies or improving algorithmic efficiency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I'll show you exactly how to improve the way error messages are displayed in our form. I'll use step-by-step explanations to make these changes easy to understand. By the end, you'll see how these small tweaks can make the code much better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the current issues
&lt;/h2&gt;

&lt;p&gt;I'll start by examining &lt;code&gt;user-profile.component.html&lt;/code&gt;. What catches my attention is the amount of logic within the first &lt;code&gt;&amp;lt;mat-error&amp;gt;&lt;/code&gt; element associated with the &lt;em&gt;Name&lt;/em&gt; input field&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I prefer a more declarative approach. The current code within the element is procedural, with an &lt;code&gt;if&lt;/code&gt; statement and repeated calls to &lt;code&gt;form.get('name')&lt;/code&gt;. Additionally, the error messages are explicitly associated with the error codes, making the template less flexible.

&lt;p&gt;I'd like to move this logic outside the template so that the view's main responsibility is simply rendering the content. Any additional logic should be handled elsewhere. This aligns with the software design principle of &lt;strong&gt;Separation of concerns&lt;/strong&gt;, allowing the template to focus on presentation and leaving error handling to other components.&lt;/p&gt;

&lt;p&gt;My approach is to create an abstraction for the code inside &lt;code&gt;&amp;lt;mat-error&amp;gt;&lt;/code&gt;, similar to a function, with an input (the form control, &lt;code&gt;form.get('name')&lt;/code&gt;) and an output (the error message). The question is: how can I generate the appropriate content inside the element based on the form control's state? In Angular, we have two main options: &lt;strong&gt;pipes&lt;/strong&gt; and &lt;strong&gt;directives&lt;/strong&gt;. Let's explore each implementation, discuss their tradeoffs, and then choose the best option for our context.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implement a pipe for displaying form field errors
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Usage of the pipe
&lt;/h3&gt;

&lt;p&gt;I'll start by sketching out how the view could use this pipe, and then I'll work on its implementation. I'll name this pipe &lt;strong&gt;&lt;code&gt;validationErrorMessage&lt;/code&gt;&lt;/strong&gt;, and it will take the form control, &lt;code&gt;form.get('name')&lt;/code&gt;, as input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mat-error&amp;gt;
  {{ form.get('name') | validationErrorMessage }}
&amp;lt;/mat-error&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This code passes the &lt;code&gt;form.get('name')&lt;/code&gt; form control object into the &lt;code&gt;validationErrorMessage&lt;/code&gt; pipe. The pipe will process this input and return the appropriate error message, which will then be displayed within the &lt;code&gt;&amp;lt;mat-error&amp;gt;&lt;/code&gt; element. I also need to make sure the pipe handles cases where &lt;code&gt;form.get('name')&lt;/code&gt; is invalid or has no errors, so it doesn't display anything in those situations.&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementing the pipe
&lt;/h3&gt;

&lt;p&gt;This pipe should return the validation error messages of the specified form control. I expect the pipe to update the message when the error changes or clear it when the field becomes valid. &lt;/p&gt;

&lt;p&gt;But when is the pipe (re)evaluated? It depends whether the pipe is pure or not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if the pipe is &lt;strong&gt;pure&lt;/strong&gt;, it is evaluated at every change detection cycle but only when its input changes, which implies a performance optimization;&lt;/li&gt;
&lt;li&gt;if the pipe is &lt;strong&gt;impure&lt;/strong&gt;, it is evaluated at every change detection cycle, regardless of its input changes.
Our pipe should run only when the field errors change, so I'll create a pure pipe. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the code example above, I noticed the pipe input is the form control itself. Since the form control is a single reference and doesn't change along with the errors, I'll pass the form control errors object to the pipe instead:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mat-error&amp;gt;
  {{ form.get('name')?.errors | validationErrorMessage }}
&amp;lt;/mat-error&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note&lt;/em&gt;&lt;/strong&gt;: The optional chaining operator &lt;code&gt;?.&lt;/code&gt; is necessary because the Angular compiler would complain that &lt;code&gt;Object is possibly 'null'&lt;/code&gt;. This is because there's no guarantee that the &lt;code&gt;get&lt;/code&gt; method will always return a form control object due to its input parameter, which could theoretically be any string.&lt;/p&gt;

&lt;p&gt;Here is the implementation of this pipe:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;and the usage in the template:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
&lt;br&gt;
  &lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;The &lt;code&gt;errors&lt;/code&gt; parameter in the pipe's &lt;code&gt;transform&lt;/code&gt; method is an object generated by the Angular forms, where the keys represent the error codes. The values could be &lt;code&gt;true&lt;/code&gt; (generated internally by Angular validators) or the message itself (in our app, generated explicitly by the server). The &lt;code&gt;Object.entries&lt;/code&gt; method converts the errors object into an array of key-value pairs, allowing us to easily iterate over each error type and its corresponding value.&lt;/p&gt;

&lt;p&gt;If the value is a string, the pipe will return it. Otherwise, it will read the error message from the &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; dictionary. Moreover, if there are multiple error messages (even though in our application this won't be the case), they will be concatenated.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; dictionary is meant to contain messages for certain error codes and isn't associated with any form fields. These codes are usually generated by validators, like &lt;code&gt;Validators.required&lt;/code&gt; or &lt;code&gt;Validators.email&lt;/code&gt; (you can have a look at the code where the &lt;code&gt;form&lt;/code&gt; object is defined in the component class). Such validators don't generate the error messages themselves; they only indicate the presence of validation errors.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important Note&lt;/strong&gt;: This implementation, while effective for displaying error messages in out specific scenario, is not a  general solution applicable to all projects. There are scenarios that my solution doesn't cover. I want to highlight that displaying form field errors is a very complex topic if we try to cover every possible scenario. My intention is to improve code quality by presenting this concrete example and applying software design principles when we have to deal with repeated code or some logic that should be moved from its original location.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reusing the pipe
&lt;/h3&gt;

&lt;p&gt;Now that the pipe is working for the &lt;code&gt;name&lt;/code&gt; field, I want to explore how can it be reused for other fields. Since the pipe is designed to be generic, I can easily apply it to the &lt;code&gt;email&lt;/code&gt; field as well: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
To accomodate validation errors for emails, I've updated the &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; dictionary to include the error for invalid emails: &lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
While this approach works well for simple scenarios, as the form becomes more complex with additional fields and validation rules, we might need a more structured way to manage the error messages. I'll delve into potential solutions for organizing and customizing error messages in the following sections.
&lt;h3&gt;
  
  
  Extending and customizing error messages
&lt;/h3&gt;

&lt;p&gt;There could be situations where we want to display custom error messages for specific fields, instead of the generic ones, or when we have additional form fields. For example, I want to display the message &lt;em&gt;"Please fill in the name"&lt;/em&gt;, instead of the generic &lt;em&gt;"This field is required"&lt;/em&gt; when the name field is empty, and similarly for the email field, &lt;em&gt;"Please fill in the email"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; dictionary contains pairs of error codes and messages but isn't aware of the fields the errors apply to. While I could theoretically change the dictionary to include the field names, this would tightly couple the pipe to my form fields. If I had another component with different form fields, the dictionary would have to be updated with specific messages for those fields,  creating chaos in the code: whenever new forms will be created, the dictionary would need to be updated too, and this reduces the code maintainability, extensibility and scalability. &lt;/p&gt;

&lt;p&gt;I definitely want to maintain the pipe's independence from its consumers. How can I do this? I'll pass an additional argument to the pipe with custom error messages for a specific field. And where can I define these messages? To answer this, I first need to address: to which elements do the messages relate? They relate to the form fields. And where are the form fields defined? They are defined in the component. So, I'll create the error messages where the form fields are defined.&lt;/p&gt;

&lt;p&gt;Here is the updated template where I pass an argument to the &lt;code&gt;validationError&lt;/code&gt; pipe:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
I've also added a second parameter to the pipe method for the custom error messages:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
The custom error messages used in the template are defined in the component class: &lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
In addition, I've defined two new types that are used in the pipe and in the component: &lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
This approach allows us to easily extend and customize error messages for different fields without cluttering the pipe code or sacrificing its reusability. As our application grows, we can add new fields and their corresponding error messages to the &lt;code&gt;errorMessages&lt;/code&gt; object in the component, keeping the validation logic organized and maintainable.
&lt;h3&gt;
  
  
  Checking out the code
&lt;/h3&gt;

&lt;p&gt;The code I've described here can be found in the &lt;code&gt;12.validation-error-pipe&lt;/code&gt; branch of the repository &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/12.validation-error-pipe"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/12.validation-error-pipe&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to explore the repository and experiment with the code yourself!&lt;/p&gt;
&lt;h2&gt;
  
  
  Implement a directive for displaying form field errors
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Usage of the directive
&lt;/h3&gt;

&lt;p&gt;Similar to the pipe implementation, I'll start by defining how I can use a directive to generate error messages within &lt;code&gt;&amp;lt;mat-error&amp;gt;&lt;/code&gt; elements. This directive, which I'll name &lt;code&gt;appValidationError&lt;/code&gt;, will be applied directly to the &lt;code&gt;&amp;lt;mat-error&amp;gt;&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;mat-error [appValidationError]="form.get('name')?.errors" [customMessages]="errorMessages?.['name']"&amp;gt;&amp;lt;/mat-error&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The directive will have 2 inputs: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;appValidationError&lt;/code&gt;: the errors object obtained from the form control.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;customMessages&lt;/code&gt; (optional): custom messages specific to that field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The directive will then be responsible for dynamically generating and displaying the appropriate error message based on the input values. It will also handle cases where there are no errors or the form control is invalid.&lt;/p&gt;

&lt;p&gt;Let's now dive into the implementation details of this &lt;code&gt;appFieldErrors&lt;/code&gt; directive.&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementation of the directive
&lt;/h3&gt;

&lt;p&gt;When using a pipe, we explicitly control where its output is rendered using the interpolation syntax. However, with a directive, we adopt a more declarative approach. The directive takes full responsibility for generating and displaying the error messages within the element it's applied to.&lt;/p&gt;

&lt;p&gt;To manipulate the content of the element, we have to inject the &lt;code&gt;ElementRef&lt;/code&gt; provider, which gives us access to underlying DOM element. We then use this reference to update the element's content with the generated error messages.&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;The logic for generating the error messages within the directive is almost identical to that of the pipe. However, there's one key difference: we need to explicitly tell Angular to update the error messages when any of the input properties change. We achieve this by implementing the &lt;code&gt;ngOnChanges&lt;/code&gt; lifecycle hook, which detects changes to input properties and triggers the &lt;code&gt;updateErrorMessage&lt;/code&gt; method to refresh the displayed message.&lt;/p&gt;

&lt;p&gt;This differs from the pipe, which is typically pure and re-executes automatically when its inputs change. In contrast, the directive needs the &lt;code&gt;ngOnChanges&lt;/code&gt; hook to actively respond to updates in its input properties.&lt;/p&gt;

&lt;p&gt;The choice between a pipe and a directive often depends on the desired level of control and flexibility. While pipes are simpler and more reusable for basic data transformations, directives offer direct DOM manipulation and fine-grained control over how and where error messages are displayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  ERROR_MESSAGES provider
&lt;/h3&gt;

&lt;p&gt;Another major change is how I handled the default error messages. Initially, they were defined in the pipe file, but this posed several drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separation of Concerns&lt;/strong&gt;: The pipe should primarily focus on error message display logic, not on storing the actual messages. Separating the messages into a dedicated location improves code organization and adheres to the Single Responsibility Principle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability&lt;/strong&gt;: When error messages are embedded, it becomes harder to unit test the pipe in isolation. We can't easily test it with different sets of error messages without modifying the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder to Manage&lt;/strong&gt;: As our application grows and the number of error messages increases, managing them within the pipe file can become cumbersome and clutter the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Customization&lt;/strong&gt;: If we want to modify the error messages for a particular component or use case, we'd have to duplicate the pipe and modify it, leading to code redundancy and potential inconsistencies. Additionally, having the error messages tightly coupled to the pipe makes it difficult to use the pipe in different parts of the application with varying error message requirements. If multiple components or directives define their own sets of error messages, we could end up with conflicting definitions for the same error codes, leading to unpredictable behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address these issues, we can create an injection token named &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; and define a provider for it. This allows us to provide different error message dictionaries at different levels of our application, offering greater flexibility and customization. The dictionary itself is defined in a separate file for better organization. I've also created a provider function, &lt;code&gt;provideValidationErrorMessages()&lt;/code&gt;, for the &lt;code&gt;providers&lt;/code&gt; array of the application configuration.&lt;/p&gt;

&lt;p&gt;This file defines the &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; injection token and creates a provider function to be registered at the root level of the application:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
In the &lt;code&gt;app.config.ts&lt;/code&gt; file I've added the previously defined provider function &lt;code&gt;provideValidationErrorMessages()&lt;/code&gt; to register the error message provider at the root level of the application:&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
The default error messages are now defined in a separate file: &lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
This might seem like a lot of code to address the issues mentioned earlier, but it showcases the power and flexibility of Angular's &lt;strong&gt;Dependency Injection&lt;/strong&gt; system. This approach allows us to easily manage and customize dependencies throughout our application, making our code more modular, maintainable, and testable.

&lt;h3&gt;
  
  
  Overriding default error messages
&lt;/h3&gt;

&lt;p&gt;If we need to define a different dictionary for the default error messages in a specific component, we'll simply provide another value for the &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; token within that component's &lt;code&gt;providers&lt;/code&gt; array: &lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
By doing this, we effectively override the default messages provided at the application level. This allows us to customize the error messages specifically for this component. Angular's dependency injection system will then inject this new set of error messages into the &lt;code&gt;ValidationErrorDirective&lt;/code&gt; whenever it's used within this component or its children.

&lt;h3&gt;
  
  
  Checking out the code
&lt;/h3&gt;

&lt;p&gt;The code I've described so far can be found in the &lt;code&gt;13.validation-error-directive&lt;/code&gt; branch of the repository &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/13.validation-error-directive/src/app"&gt;https://github.com/cezar-plescan/user-profile-editor/tree/13.validation-error-directive/src/app&lt;/a&gt;. Feel free to explore the repository and experiment with the directive yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison with the pipe
&lt;/h3&gt;

&lt;p&gt;I want to analyze the directive and pipe approaches to determine which is better suited for our application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Directive

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strengths&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DOM Manipulation&lt;/strong&gt;: Directives directly manipulate the DOM, allowing us to modify the appearance, positioning, and content of the element displaying the error message. This offers greater flexibility for complex error visualizations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precise Targeting&lt;/strong&gt;: We can apply the directive to specific form controls, providing fine-grained control over which elements display error messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Handling&lt;/strong&gt;: Directives can easily react to events like focus, blur, or value changes, allowing us to update error messages dynamically as the user interacts with the form.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weaknesses&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learning Curve&lt;/strong&gt;: Directives can be slightly more complex to understand and implement than pipes, especially for developers new to Angular.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tight Coupling (Potentially)&lt;/strong&gt;: If not designed carefully, directives can become tightly coupled to the specific DOM structure of our form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: Unit testing directives can be more involved than testing pure pipes due to their interaction with the DOM.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pipe

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strengths&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: Pipes are generally easier to understand and use than directives, especially for simple data transformations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt;: Pipes are highly reusable and can be used in various templates and contexts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability&lt;/strong&gt;: Pure pipes are easier to unit test than directives, as their logic is isolated from DOM manipulations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Angular's Optimization&lt;/strong&gt;: Angular's change detection mechanism is designed to optimize the use of pure pipes, potentially offering better performance in some cases.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weaknesses&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited DOM Manipulation&lt;/strong&gt;: Pipes cannot directly manipulate the DOM, so we'll need additional elements (like ) and potentially some conditional logic (e.g., *ngIf) in our template to display the error messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less Precise Targeting&lt;/strong&gt;: While we can conditionally apply the pipe based on error conditions, it might not be as precise as directly attaching a directive to a specific element.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is More Appropriate?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pipe&lt;/strong&gt;: The pipe is likely the more appropriate choice if our error messages are relatively simple strings and we want to prioritize reusability and ease of use. We can easily apply the pipe to multiple form controls within our template.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Directive&lt;/strong&gt;: We can consider a directive if we need more complex error message displays (e.g., custom styling, conditional formatting, or rich UI elements) or if we need to perform more sophisticated DOM manipulations based on the error state.&lt;/p&gt;

&lt;p&gt;The final answer depends on the application needs, but in many cases Angular applications tend to become larger and more complex and my choice would be to go with directives.&lt;/p&gt;

&lt;p&gt;Additionally, we can combine both approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the pipe to generate the error message string.&lt;/li&gt;
&lt;li&gt;Use the directive to apply that message to the appropriate element (e.g., a  tag) and potentially handle any additional styling or UI logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using third-party libraries
&lt;/h2&gt;

&lt;p&gt;While the custom directive and pipe I've implemented provide a solid foundation for displaying validation errors, more complex form scenarios might require additional features and flexibility. Thankfully, the Angular ecosystem offers several third-party libraries that that can make handling validation errors in our forms easier. I've found two popular libraries that could be useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/DmitryEfimenko/ngspot/tree/main/packages/ngx-errors/package"&gt;ngx-errors&lt;/a&gt;: This library provides a declarative way to manage validation errors in your templates. It offers features like:

&lt;ul&gt;
&lt;li&gt;simplified syntax: we can define error messages directly within the template using simple directives.&lt;/li&gt;
&lt;li&gt;dynamic error display: it automatically shows and hides error messages based on the validation status of the form controls.&lt;/li&gt;
&lt;li&gt;customization: we can customize error messages for specific error types or create our own error templates.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@rxweb/reactive-form-validators"&gt;@rxweb/reactive-form-validators&lt;/a&gt;: this library goes beyond basic validation and offers a comprehensive set of validators for various scenarios, such as:

&lt;ul&gt;
&lt;li&gt;complex validations: it includes validators for cross-field validation, conditional validation, and more.&lt;/li&gt;
&lt;li&gt;customizable error messages: we can easily provide custom error messages for each validator.&lt;/li&gt;
&lt;li&gt;internationalization (i18n): it supports multiple languages for the error messages.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Refactoring techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Extract Function/Method
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Description
&lt;/h4&gt;

&lt;p&gt;This refactoring technique is a fundamental principle in software development aimed at improving code organization, readability, and maintainability. The essence of this technique is to take a section of code within a larger function or method that performs a specific task and move it into its own separate function or method.  This new function is given a descriptive name that clearly indicates its purpose.&lt;/p&gt;

&lt;p&gt;More details at &lt;a href="https://refactoring.guru/extract-method"&gt;https://refactoring.guru/extract-method&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to refactor
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify&lt;/strong&gt;: Locate a block of code within a function that represents a cohesive unit of work (e.g., calculating a value, validating input, formatting data).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract&lt;/strong&gt;: Create a new function and move the identified code block into it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace&lt;/strong&gt;: Replace the original code block with a call to the new function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: Give the new function a clear, descriptive name that accurately reflects its purpose.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Benefits
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Readability&lt;/strong&gt;: Breaking down large functions into smaller, more focused functions makes the code easier to understand and follow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Duplication&lt;/strong&gt;: If the extracted code is used in multiple places, this technique eliminates the need to repeat it, leading to a cleaner codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Testability&lt;/strong&gt;: Smaller functions are easier to test in isolation, as you can focus on specific inputs and expected outputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increased Reusability&lt;/strong&gt;: Extracted functions can be reused in other parts of our application, saving development time and effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Maintainability&lt;/strong&gt;: The code becomes more modular, making it easier to modify and update without affecting other parts of the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Where it was used
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The error message logic was extracted from the template into the pipe and the directive.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;getErrorMessage&lt;/code&gt; method has been extracted from the main &lt;code&gt;generateErrorMessage&lt;/code&gt; method in the directive class. This improves readability and modularity, making the code easier to understand and maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Replace Conditional with Polymorphism
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Description
&lt;/h4&gt;

&lt;p&gt;This technique addresses situations where we have conditional statements (e.g., if, else if, switch) that determine the behavior of a piece of code based on the type or properties of an object. The goal is to replace these conditionals with a more object-oriented approach using polymorphism.&lt;/p&gt;

&lt;p&gt;Polymorphism allows objects of different classes to be treated as if they were of the same type. By creating a common interface or abstract class and then implementing specific behaviors in subclasses, we can eliminate the need for explicit conditional checks.&lt;/p&gt;

&lt;p&gt;More details at &lt;a href="https://refactoring.guru/replace-conditional-with-polymorphism"&gt;https://refactoring.guru/replace-conditional-with-polymorphism&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to refactor
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify the Conditional&lt;/strong&gt;: Locate the conditional statement (or series of statements) that you want to refactor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Common Interface/Class&lt;/strong&gt;: Define an interface or abstract class that represents the common behavior or properties of the objects being tested in the conditional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Subclasses&lt;/strong&gt;: For each branch of the conditional, create a subclass that implements the interface or extends the abstract class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement Behavior in Subclasses&lt;/strong&gt;: Move the code from each branch of the conditional into the corresponding subclass, implementing the shared method or property defined in the interface/abstract class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace Conditional with Polymorphic Call&lt;/strong&gt;: Replace the conditional statement with a call to the shared method or property on the object. The specific behavior will be determined at runtime based on the actual type of the object (polymorphism).&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Benefits
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Readability&lt;/strong&gt;: Code becomes more concise and easier to understand by eliminating explicit conditional checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open/Closed Principle&lt;/strong&gt;: The code becomes more extensible. We can add new behaviors (subclasses) without modifying the existing code that uses the interface/abstract class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Complexity&lt;/strong&gt;: Complex conditional structures are replaced with simpler polymorphic calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Maintainability&lt;/strong&gt;: Changes to a specific behavior only require modifying the corresponding subclass, leaving other parts of the code unaffected.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Where it was used
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;ERROR_MESSAGES&lt;/code&gt; injection token&lt;/strong&gt; is a way that implements a form of polymorphism.&lt;/p&gt;

&lt;p&gt;The default provider in our core module provides a default implementation of &lt;code&gt;ERROR_MESSAGES&lt;/code&gt;. In &lt;code&gt;UserProfileComponent&lt;/code&gt;, we override this provider, essentially providing a new implementation of &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; with custom error messages.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ValidationErrorDirective&lt;/code&gt; injects the &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; token. Depending on where it's used, we have a &lt;strong&gt;polymorphic behavior&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If used in a component without a custom provider, it receives the default error messages.&lt;/li&gt;
&lt;li&gt;If used in a component with a custom provider, it receives the customized messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;code&gt;validation-error.directive.ts&lt;/code&gt;, the &lt;code&gt;getErrorMessage&lt;/code&gt; method doesn't contain any explicit conditionals. Instead, it directly accesses the injected &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; object, which behaves polymorphically depending on the provider.&lt;/p&gt;

&lt;p&gt;This polymorphic behavior is achieved through Angular's dependency injection system, which provides the appropriate &lt;code&gt;ERROR_MESSAGES&lt;/code&gt; object at runtime based on the component's configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  More to come: Stay tuned for future form enhancements
&lt;/h2&gt;

&lt;p&gt;This refactoring journey has demonstrated how small, deliberate improvements can lead to a more robust and maintainable Angular form. Refactoring is not just about fixing problems; it's about continuously evolving our code to make it more elegant, maintainable, and adaptable.&lt;/p&gt;

&lt;p&gt;But there's more to come! Stay tuned for future articles where I'll tackle other refactoring strategies and unlock even more potential in your Angular development toolkit.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>forms</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>Demystifying Token-Based Authentication: Your Angular Roadmap (Part 1)</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Fri, 31 May 2024 14:38:28 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/demystifying-token-based-authentication-your-angular-roadmap-5bon</link>
      <guid>https://dev.to/cezar-plescan/demystifying-token-based-authentication-your-angular-roadmap-5bon</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I continue the exploration of authentication methods, after the article about &lt;a href="https://dev.to/cezar-plescan/demystifying-session-based-authentication-your-angular-roadmap-1b9n"&gt;session-based authentication&lt;/a&gt;, by focusing on the role of &lt;strong&gt;tokens&lt;/strong&gt; and highlighting their advantages over session IDs in certain scenarios. While the term "token" might sound familiar to you, let's delve into their evolution and specific challenges they address where session IDs prove to be less valuable.&lt;/p&gt;

&lt;p&gt;The topics I'll cover provide a basic foundation for authentication systems that use JWTs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what are the benefits of such method compared to session IDs&lt;/li&gt;
&lt;li&gt;what is the structure of a JWT&lt;/li&gt;
&lt;li&gt;what is required to create a token&lt;/li&gt;
&lt;li&gt;how to send it to the client and how can the client store it&lt;/li&gt;
&lt;li&gt;how to pass the token within requests&lt;/li&gt;
&lt;li&gt;how the server verifies the token and send the response&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Evolution
&lt;/h2&gt;

&lt;p&gt;I want to describe a scenario to illustrate where session cookies become harder to handle.&lt;/p&gt;

&lt;p&gt;Let’s imagine an e-commerce application. In its early days everything ran on one monolithic server, where both frontend and backend were hosted. The server had a database to store user sessions and for every user a session cookie was created. So far, the cookie mechanism worked smoothly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F355yfueuc78ua9bgn1fo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F355yfueuc78ua9bgn1fo.png" alt="Monolith server with Session cookies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application gained popularity and the traffic encountered huge spikes. To fix that, load balancers were introduced to distribute requests across multiple servers. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyywmbz77p8gywvtag3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyywmbz77p8gywvtag3y.png" alt="Load balancer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what if a user’s subsequent requests landed on a different server that doesn’t have their session data? A workaround was developed: to have “sticky sessions”, where the load balancer routed a user’s requests to the same server, but that reduced scaling flexibility, which was a major drawback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhujdj9clg3wl59zo8e9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhujdj9clg3wl59zo8e9.png" alt="Sticky sessions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another improvement mechanism was added: horizontal scaling with &lt;strong&gt;microservices&lt;/strong&gt;. That implied decomposing parts of the application into smaller units running on their own servers. Below is a simplistic representation of microservices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktazctwdybnt6sfb2v9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktazctwdybnt6sfb2v9b.png" alt="Microservices"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But how do these servers coordinate the user sessions? Possible solutions could be to &lt;strong&gt;replicate the session data&lt;/strong&gt; across all servers (which adds overhead, synchronization delays, or potential inconsistencies), or to introduce a &lt;strong&gt;single dedicated session storage database&lt;/strong&gt; (like Redis - this creates a single point of failure and adds network latency when multiple servers fetch session data). It looks that the complexity starts to increase a lot, which leads to reduced maintainability of the system.&lt;/p&gt;

&lt;p&gt;What if we were able to &lt;strong&gt;decentralize the session data&lt;/strong&gt;, so each server would have a mechanism to validate the authentication information, without the need of a central session store? This leads to the introduction of &lt;strong&gt;&lt;em&gt;web tokens&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concept
&lt;/h2&gt;

&lt;p&gt;What’s the idea behind these tokens? Unlike session IDs, which are stored by the server along with the user data, &lt;strong&gt;the tokens themselves contain the user data&lt;/strong&gt; (e.g. user ID, username, email, roles, etc.), are generated by the server, stored by each client, and passed within the requests.&lt;/p&gt;

&lt;p&gt;Tokens are cryptographically signed and all servers in the entire system are able to decode them and extract the user information by using a shared key (I’ll cover how this mechanism works later in the article).&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;Let’s explore some general benefits this concept brings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scaling&lt;/strong&gt; can be easily achieved, each added server can independently validate the tokens.&lt;/li&gt;
&lt;li&gt;Seamless integration with &lt;strong&gt;RESTful APIs&lt;/strong&gt;, due to their stateless nature. This makes tokens a good fit for integration with mobile applications.&lt;/li&gt;
&lt;li&gt;Tokens can be sent across &lt;strong&gt;different domains&lt;/strong&gt;, they are a good fit for distributed systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want to mention the main practical benefit in the context of the previous scenario with the e-commerce application: the horizontal scaling that involves multiple servers or microservices can be done with minimal effort; the newly added servers would need to know the shared key only to validate the tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Internals
&lt;/h2&gt;

&lt;p&gt;On the surface, the web tokens look like a robust system with significant benefits. Ever wondered how they actually work? In the article about &lt;a href="https://dev.to/cezar-plescan/demystifying-session-based-authentication-your-angular-roadmap-1b9n"&gt;session-based authentication&lt;/a&gt; I talked about session IDs which utilize the browser cookies, but what are the web tokens more specifically, how are they passed with the request, and how do servers validate them?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: I’ve used the general term &lt;strong&gt;web token&lt;/strong&gt;, but in the context of authentication the &lt;strong&gt;JSON Web Tokens (JWT)&lt;/strong&gt; are the dominant and widely supported standard and I’ll refer exclusively to them throughout the entire article. More details about JWT can be found at &lt;a href="https://jwt.io/introduction" rel="noopener noreferrer"&gt;https://jwt.io/introduction&lt;/a&gt; or &lt;a href="https://datatracker.ietf.org/doc/html/rfc7519" rel="noopener noreferrer"&gt;https://datatracker.ietf.org/doc/html/rfc7519&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Similar to session IDs, JWTs go through a similar process of &lt;strong&gt;creation&lt;/strong&gt;, &lt;strong&gt;storage&lt;/strong&gt; on the client side, and &lt;strong&gt;sending&lt;/strong&gt; them with subsequent requests. The difference lies in their implementation which I'll describe in the following sections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6yuzqoe7q400gqvhmwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6yuzqoe7q400gqvhmwo.png" alt="JWT transmission"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The structure of a JWT
&lt;/h3&gt;

&lt;p&gt;While session IDs are random characters, a JWT contains information about the user and there is no need on the server side for a storage mechanism of all generated tokens; each client stores their own JWT and servers will decode and extract the user information from it. This makes JWT systems stateless.&lt;/p&gt;

&lt;p&gt;Basically, a JWT represents a Base64 encoded string composed of 3 parts: &lt;strong&gt;Header&lt;/strong&gt; (Metadata), &lt;strong&gt;Payload&lt;/strong&gt; (Data), and &lt;strong&gt;Signature&lt;/strong&gt; (Verification).&lt;/p&gt;

&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;

&lt;p&gt;Let's consider the JWT taken from &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;https://jwt.io/&lt;/a&gt;&lt;br&gt;
&lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&lt;/code&gt;. As you may notice, the separator is represented by the dot &lt;code&gt;.&lt;/code&gt; symbol.&lt;/p&gt;

&lt;p&gt;By decoding the first two parts, we get JSON objects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Header: &lt;code&gt;{ "alg": "HS256", "typ": "JWT" }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Payload: &lt;code&gt;{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see why do we need such a structure and what are the problems to be addressed: &lt;strong&gt;data integrity&lt;/strong&gt;, &lt;strong&gt;authentication of the issuer&lt;/strong&gt;, &lt;strong&gt;statelessness and scalability&lt;/strong&gt;, &lt;strong&gt;transmission&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Data Integrity
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: We need to ensure that the information in a token hasn't been altered by anyone else.&lt;br&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: The signature part is a cryptographic hash of the header and payload, created using a secret key. When a server (which knows the secret key) receives a token, it computes a hash based on the header, payload and the secret key, and it expects to match the signature part from the token.&lt;/p&gt;
&lt;h4&gt;
  
  
  Authentication of the Issuer
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: How do we know that the token was indeed created by our authentication server?&lt;br&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: A secret key, which is available to our servers only, is used to generate the signatures, so no one else would be able to generate valid signatures.&lt;/p&gt;
&lt;h4&gt;
  
  
  Statelessness and Scalability
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: How do we handle scalability?&lt;br&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: As mentioned above, the token itself contains user information in the payload part, and there is no need for servers to store any session data, because they can immediately extract the user information from the token. This way the servers can deal with a large number of users without using additional resources.&lt;/p&gt;
&lt;h4&gt;
  
  
  Transmission
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: How can we efficiently transmit tokens in HTTP headers or query parameters?&lt;br&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Using Base64 encoding. More information about this topic can be found at &lt;a href="https://base64.guru/learn/what-is-base64" rel="noopener noreferrer"&gt;https://base64.guru/learn/what-is-base64&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creation of tokens
&lt;/h2&gt;

&lt;p&gt;As in the case of session IDs, the JWTs are created during the authentication process, after the server validates the user’s credentials. One important aspect I need to mention here, compared to the previous solution, is that the login endpoint could be located anywhere, no matter the domain. &lt;/p&gt;

&lt;p&gt;Let's see what do we need at minimum to create a valid JWT for authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Algorithm (&lt;strong&gt;alg&lt;/strong&gt;): the algorithm used to sign the token (e.g. HS256 or RS256)&lt;/li&gt;
&lt;li&gt;Type (&lt;strong&gt;typ&lt;/strong&gt;), usually set to "JWT"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Subject (&lt;strong&gt;sub&lt;/strong&gt;): used to identify the user, e.g. user ID or email&lt;/li&gt;
&lt;li&gt;Expiration time (&lt;strong&gt;exp&lt;/strong&gt;): The timestamp after which the token becomes invalid&lt;/li&gt;
&lt;li&gt;Issued At Time (&lt;strong&gt;iat&lt;/strong&gt;): The timestamp of when the token was issued.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret Key&lt;/strong&gt;: A secret key known only to the server is used to sign the token. This is crucial for ensuring the token's authenticity and integrity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a simple implementation for generating a JWT in Node.JS:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 The actual value of the generated token is &lt;code&gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywidXNlcm5hbWUiOiJqb2huZG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSIsImlhdCI6MTYzMzMyOTU2MywiZXhwIjoxNjMzMzMzMTYzfQ.v6nGb_QkO_w1x_88r81176tXF699eQz5555555555555&lt;/code&gt;. If you go to &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;https://jwt.io/&lt;/a&gt; and decode this token, you'll see the payload with data from the source code, along with other 2 keys, &lt;strong&gt;iat&lt;/strong&gt; and &lt;strong&gt;exp&lt;/strong&gt;, which were automatically generated by the library. &lt;br&gt;
You may notice that I've added the username and the email in the payload, and kept it flat, not nested, which is the recommendation for such cases.

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The token can be decoded independently of the secret key which is used for verifying the token, not to extract its content.&lt;/p&gt;
&lt;h2&gt;
  
  
  Transmission of the token between the server and the client
&lt;/h2&gt;

&lt;p&gt;Now that the token is created after a successful authentication, the server needs to send it to the client. Later on, when the client needs to make an authenticated request, it will also send the token to that specific server.&lt;/p&gt;

&lt;p&gt;There are two common options of sending the token from the server to the client: via cookies or in the response body. There is no one-size-fits-all solution here, the right method depends on the application's requirements and security considerations. Depending on each method, the client will then send the token with subsequent requests to servers in different ways, which we'll explore soon.&lt;/p&gt;
&lt;h3&gt;
  
  
  Transmitting the token as a cookie
&lt;/h3&gt;

&lt;p&gt;This process is similar to the one described in the &lt;a href="https://dev.to/cezar-plescan/demystifying-session-based-authentication-your-angular-roadmap-1b9n"&gt;session-based authentication&lt;/a&gt; article:&lt;br&gt;
&lt;code&gt;Set-Cookie: access_token=&amp;lt;JWT_value&amp;gt;; HttpOnly; Secure; SameSite=Strict&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Some &lt;strong&gt;benefits&lt;/strong&gt; of this methods include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatic transmission of the cookie that contains the JWT in subsequent requests to the same domain;&lt;/li&gt;
&lt;li&gt;using the &lt;strong&gt;HttpOnly&lt;/strong&gt; protection to mitigate Cross-Site Scripting (XSS) attacks
If you have servers located on &lt;strong&gt;different domains&lt;/strong&gt;, additional CORS configuration is needed on both the server and the client (that is, the browser).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the server side, the following two response headers need to be defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Access-Control-Allow-Origin: &amp;lt;our_application_domain&amp;gt;&lt;/code&gt; - this header tells the browser that the server accepts requests from our application domain (by default, browsers block requests to different domains unless servers are configured properly)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Access-Control-Allow-Credentials: true&lt;/code&gt; - as another security measure, browsers won't send any cookies with cross-origin requests; when this header is set, the browser will ignore this restriction and will be able to send the cookies associated with that specific domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a simple implementation of creating and sending the token as a cookie from the server to the client, considering our application makes cross-site requests:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
There is one additional setting to be done on the client side too: even if the server is configured to allow the browser to send cookies, we need to explicitly instruct our HTTP API (which could be the &lt;code&gt;fetch&lt;/code&gt; method, &lt;code&gt;XMLHttpRequest&lt;/code&gt; object or &lt;code&gt;HttpClient&lt;/code&gt; from Angular) to include the cookies when making cross origin requests.

&lt;p&gt;Here are some basic examples using each of these three APIs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now let's see how a server could define a protected endpoint and read the token from the cookies:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Sending the token in the response body
&lt;/h3&gt;

&lt;p&gt;This method is simple to implement, more flexible, independent of server domains, suitable for SPAs or mobile apps. On the other hand, the client needs to explicitly extract and store the token. Popular applications like Twitter, Facebook, Netflix, Spotify and many others use this approach.&lt;/p&gt;

&lt;p&gt;In this section we'll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how the token is generated and sent to the client&lt;/li&gt;
&lt;li&gt;how the client can store the token&lt;/li&gt;
&lt;li&gt;methods of sending the token in subsequent requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple implementation of how the token is generated and sent to the client could look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Storing the token
&lt;/h4&gt;

&lt;p&gt;Once the server responds with the token, it is the client's responsibility to store it. The fundamental principle of token-based (or session-based) authentication is that the token should be kept secret and not shared with anyone. If an attacker could steal the token, the server won't know that the request wasn't made by the intended client, and this would lead to unauthorized access to sensitive data.&lt;/p&gt;

&lt;p&gt;With this in mind, let's explore some options and their trade-offs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using JavaScript &lt;strong&gt;variables&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Pros:

&lt;ul&gt;
&lt;li&gt;it's easy and straightforward to implement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cons:

&lt;ul&gt;
&lt;li&gt;could be vulnerable to XSS attacks (malicious scripts could access the variable), even though Angular provides a significant degree of protection against such attacks.&lt;/li&gt;
&lt;li&gt;the token is lost when the page is refreshed or closed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;localStorage&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Pros:

&lt;ul&gt;
&lt;li&gt;the token persists after the page is reloaded or closed.
can be easily accessed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cons:

&lt;ul&gt;
&lt;li&gt;could be vulnerable to XSS attacks (malicious scripts could access the variable), even though Angular provides a significant degree of protection against such attacks - basically it's the same concern as with normal in-memory variables.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sessionStorage&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;the same as localStorage, excepting that the token is lost when the page is closed (only when closed, not when reloaded)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a simple implementation of an Angular login component, that stores the JWT in the localStorage:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Sending the token to servers
&lt;/h4&gt;

&lt;p&gt;So far we have the JWT stored on the client side in order to use it when we need to make authenticated requests. But how can we send it in requests? While there could be various options, the &lt;a href="https://datatracker.ietf.org/doc/html/rfc6750#section-2.1" rel="noopener noreferrer"&gt;RFC 6750&lt;/a&gt; standard specifies that the token should be sent in the request header, like this &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt;. This is the most widely used and recommended method, it’s well supported by libraries and frameworks, and works across different domains (no CORS concerns).&lt;/p&gt;

&lt;p&gt;A simple implementation of this method is illustrated below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In a real application a more elegant solution is used, that is, to define an HTTP interceptor that automatically adds the authorization header, but only for the requests that need it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Token extraction and validation
&lt;/h2&gt;

&lt;p&gt;Now that I’ve discussed the token transmission to servers, let’s see how the servers handle the received JWT. There are several steps involved in this process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;token extraction&lt;/li&gt;
&lt;li&gt;signature verification&lt;/li&gt;
&lt;li&gt;payload decoding&lt;/li&gt;
&lt;li&gt;expiration check&lt;/li&gt;
&lt;li&gt;authorization&lt;/li&gt;
&lt;li&gt;response&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Token extraction
&lt;/h3&gt;

&lt;p&gt;The server first extracts the JWT from the incoming request header (e.g. Authorization header) or from the cookie, depending on the initial implementation.&lt;/p&gt;

&lt;p&gt;Here are some basic implementations:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/protected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get the Authorization header value&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Split the header to get the token (format: "Bearer &amp;lt;token&amp;gt;")&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenParts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokenParts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; 

  &lt;span class="c1"&gt;//... the rest of the logic&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/protected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get the token from the cookie, assuming the cookie name is 'token'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

  &lt;span class="c1"&gt;//... the rest of the logic&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Signature verification
&lt;/h3&gt;

&lt;p&gt;Then the server needs to verify the signature of the token. But why is this step necessary? In the section about the JWT structure I've briefly mentioned about the main roles of the signature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it ensures authenticity, proving that the JWT was indeed issued by our servers and not forged by an attacker.&lt;/li&gt;
&lt;li&gt;the signature is generated using a secret key that only our servers know; without the signature verification, an attacker could create fake JWTs.&lt;/li&gt;
&lt;li&gt;it is a cryptographic hash of the token's header and payload and any modification to either the header or payload would result in a different signature.&lt;/li&gt;
&lt;li&gt;JWTs are often used in distributed systems where multiple services rely on the token to authenticate and authorize users; by verifying the signature, each service can independently trust that the token is authentic and hasn't been tampered with, even if it was issued by a different service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if the token can be decoded by anyone to extract the payload, the signature verification process ensures its authenticity and integrity. This is because the header and payload part of the token are &lt;em&gt;simply encoded&lt;/em&gt; using the Base64 algorithm, and &lt;em&gt;not encrypted&lt;/em&gt;. Both encoding and encryption transform data from one form to another, and this process can be reversed to recover the original data. The encryption relies on a secret key to transform back the data, making it unreadable without the correct key. Decoding typically uses a publicly known algorithm, making it easy to reverse the transformation without needing a secret key.&lt;/p&gt;

&lt;p&gt;Let's see what are the &lt;strong&gt;steps&lt;/strong&gt; involved in the signature verification process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract Header and Payload

&lt;ul&gt;
&lt;li&gt;The JWT is a string with three parts separated by dots: header.payload.signature.&lt;/li&gt;
&lt;li&gt;The verifier splits the string at the dots to separate the header and payload segments.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Base64Url Decode

&lt;ul&gt;
&lt;li&gt;Both the header and payload are Base64Url decoded. This converts them from their URL-safe representation back into their original JSON format.&lt;/li&gt;
&lt;li&gt;The resulting JSON objects contain information about the algorithm (alg) and token type (typ) in the header, and the claims in the payload.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create Signing Input

&lt;ul&gt;
&lt;li&gt;The Base64Url encoded header and payload strings are concatenated with a dot (.) in between.&lt;/li&gt;
&lt;li&gt;This concatenated string is the input that was used to create the signature during token generation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Signature Verification (Algorithm-Specific)

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HMAC&lt;/strong&gt; (Symmetric):

&lt;ul&gt;
&lt;li&gt;The verifier takes the signing input string and applies the HMAC algorithm specified in the header (alg) - more details about it at &lt;a href="https://www.okta.com/identity-101/hmac/" rel="noopener noreferrer"&gt;https://www.okta.com/identity-101/hmac/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The shared secret key, which is stored on our servers, is used as the input to the HMAC function, along with the signing input string.&lt;/li&gt;
&lt;li&gt;The output of the HMAC function is the calculated signature.&lt;/li&gt;
&lt;li&gt;The verifier compares the calculated signature to the signature extracted from the JWT. If they match, the token is valid.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RSA&lt;/strong&gt; (Asymmetric):

&lt;ul&gt;
&lt;li&gt;The verifier retrieves the public key corresponding to the private key used by the issuer to sign the token (read the articles in the links below to find more information about public/private keys and the RSA algorithm).&lt;/li&gt;
&lt;li&gt;The verifier uses the public key to decrypt the signature extracted from the JWT.&lt;/li&gt;
&lt;li&gt;The decryption process recovers the original hash value that was created by the issuer when signing the token.&lt;/li&gt;
&lt;li&gt;The verifier calculates the hash of the signing input string using the same algorithm (alg) as the issuer.&lt;/li&gt;
&lt;li&gt;The calculated hash is compared to the recovered hash. If they match, the token is valid.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More detailed explanation about the verification process can be found in the articles &lt;a href="https://www.criipto.com/blog/jwt-validation-guide" rel="noopener noreferrer"&gt;Understanding JWT Validation: A Practical Guide with Code Examples&lt;/a&gt; or &lt;a href="https://software-factotum.medium.com/validating-rsa-signature-for-a-jws-10229fb46bbf" rel="noopener noreferrer"&gt;Validating RSA signature for a JWS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The secret key (for HMAC) or the private key (for RSA) is crucial for verification. If these keys are compromised, the entire system is at risk. The choice between HMAC and RSA depends on our specific security requirements. HMAC is simpler but requires sharing the secret key. RSA is more complex but allows for better key management. Most JWT libraries handle the cryptographic details of signature verification, so we don't need to implement the algorithms ourselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional checks&lt;/strong&gt; can be performed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expiration (&lt;strong&gt;exp&lt;/strong&gt;): The verifier checks the exp claim (expiration time) in the payload to ensure the token hasn't expired.&lt;/li&gt;
&lt;li&gt;Not Before (&lt;strong&gt;nbf&lt;/strong&gt;): The verifier might check the nbf claim (not before time) to ensure the token is not being used before its intended start time.&lt;/li&gt;
&lt;li&gt;Issuer (&lt;strong&gt;iss&lt;/strong&gt;) and Audience (&lt;strong&gt;aud&lt;/strong&gt;): The verifier might check the iss (issuer) and aud (audience) claims to ensure the token was issued by a trusted party and is intended for the current recipient.&lt;/li&gt;
&lt;li&gt;Other Claims: Depending on your application's requirements, you might perform additional checks on other claims in the payload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all the verification steps pass, the JWT is considered valid. Otherwise, the token is rejected as invalid or compromised.&lt;/p&gt;

&lt;p&gt;In practice we don't need to manually implement the verification algorithm, but simply used dedicated libraries. In Node.JS, the verification could look like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsonwebtoken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Payload decoding and response
&lt;/h3&gt;

&lt;p&gt;Once the signature is confirmed valid, the Base64Url-encoded payload is decoded into its original JSON representation. This JSON object contains the claims (data) that were included in the token by the issuer. The claims typically include information about the user (e.g., user ID, username, email) and other relevant details (e.g., expiration time, issued at time, permissions). The server can use the claims in the token (e.g., user ID, permissions) to determine whether the user has access to the requested resource. If so, the server will process the request and will send the appropriate response; otherwise, it can respond with 401 or 403 status codes.&lt;/p&gt;
&lt;h4&gt;
  
  
  401 Unauthorized response
&lt;/h4&gt;

&lt;p&gt;This status code is appropriate when the user's authentication credentials (the JWT in our case) are either missing, invalid, or expired:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Authorization header is missing or empty.&lt;/li&gt;
&lt;li&gt;The JWT has an invalid signature (e.g., due to tampering or an incorrect secret key).&lt;/li&gt;
&lt;li&gt;The JWT has expired (the exp claim is in the past).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  403 Forbidden response
&lt;/h4&gt;

&lt;p&gt;This status code is appropriate when the user is authenticated (the JWT is valid) but does not have the necessary permissions or authorization to access the requested resource. It indicates that the server understood the request but refuses to fulfill it due to insufficient privileges.&lt;/p&gt;
&lt;h4&gt;
  
  
  Basic implementation
&lt;/h4&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;The topics I've covered in this article provide a basic foundation for authentication systems that use JWT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what are the benefits of such method compared to session IDs&lt;/li&gt;
&lt;li&gt;what is the structure of a JWT&lt;/li&gt;
&lt;li&gt;what is required to create a token&lt;/li&gt;
&lt;li&gt;how to send it to the client and how can the client store it&lt;/li&gt;
&lt;li&gt;how to pass the token within requests&lt;/li&gt;
&lt;li&gt;how the server verifies the token and send the response&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;As I prefer to explain and build things gradually, in the next article I'll cover more advanced topics about JWTs that are required by a robust authentication system, so it can be integrated into a large scale application. Such topics will include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;token revocation to prevent unauthorized access if a token is compromised&lt;/li&gt;
&lt;li&gt;rate limiting to prevent brute-force attacks where an attacker tries to guess valid tokens&lt;/li&gt;
&lt;li&gt;refresh tokens to issue new access tokens when they expire, improving user experience and security&lt;/li&gt;
&lt;li&gt;encrypting the token if the payload contains sensitive information&lt;/li&gt;
&lt;li&gt;frameworks, protocols, and systems that use JWT: Single-Sign-On, OAuth, Auth0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  See it in action
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/P2CPd9ynFLg"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/V5D8RuB6Xps"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>authentication</category>
      <category>jwt</category>
      <category>angular</category>
      <category>token</category>
    </item>
    <item>
      <title>Demystifying Session-Based Authentication: Your Angular Roadmap</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Thu, 16 May 2024 14:50:22 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/demystifying-session-based-authentication-your-angular-roadmap-1b9n</link>
      <guid>https://dev.to/cezar-plescan/demystifying-session-based-authentication-your-angular-roadmap-1b9n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this series of articles I want to describe &lt;strong&gt;private communication solutions&lt;/strong&gt; between a client and a server. What does private mean? It means that I want to access or manipulate my own private data that is stored on a server, and it should be only me that can perform such actions. Other people should be rejected by the server if they try to access my personal data. Why would somebody want my data, why should I care if my data gets into somebody else's hands? Imagine how would you feel if your online bank account is accessed by a person and they steal your money; or write posts on your social media account; or access a website where you paid for having access and they use its services. Bottom line is that we need some ways to protect our data.&lt;/p&gt;

&lt;p&gt;The server needs to know that the request for a specific data is made by the owner of that data (or by someone who is entitled by the owner to make requests on their behalf, but this is another story which I’ll cover later in the series). This process is called &lt;strong&gt;authentication&lt;/strong&gt;. On the other hand, there is another process called &lt;strong&gt;authorization&lt;/strong&gt;, that determines what a user is allowed to do after they are authenticated.&lt;/p&gt;

&lt;p&gt;I want to explore the ways of creating an authenticated connection, by describing a step-by-step evolution of solutions. I’ll start with a basic scenario, then discussing the issues and introducing improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;: A client makes an HTTP request to a server, without sending any private information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A client initiates an HTTP request to a server&lt;/li&gt;
&lt;li&gt;The server knows nothing about the identity of the client&lt;/li&gt;
&lt;li&gt;The server responds with public data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1r2ig4qarhrdummihiq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1r2ig4qarhrdummihiq.png" alt="anonymous HTTP request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Obviously, the server doesn’t know who made the request and won’t be able to send back any private data. &lt;/li&gt;
&lt;li&gt;Of course, we need to find a way to inform the server who we are.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Improvement&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let’s say I’ll add my user ID to the request with the intention to tell the server who I am.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sending the User ID in the request
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhz62eq89ri1dieqekdm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhz62eq89ri1dieqekdm.png" alt="HTTP request with userID"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Positives&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server has a hint at the client’s possible identity. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The network traffic can be intercepted and the user ID can be exposed (I’ll cover how the network can be intercepted later in the series)&lt;/li&gt;
&lt;li&gt;IDs can be guessed or leaked (usually IDs follow a sequential or predictable pattern)&lt;/li&gt;
&lt;li&gt;The server doesn’t really know that the request came from the legitimate owner of that ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a secure communication through the HTTPS protocol (I’ll cover this topic later in the series). This way no one will "see" what I send to the server.&lt;/li&gt;
&lt;li&gt;Create a secret password associated with the account that is known only by me.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: Throughout the entire article I will assume that all communication use HTTPS.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a secret password associated with the account
&lt;/h3&gt;

&lt;p&gt;This mechanism implies that the user defines their own password which will be associated with their account on its creation. The user ID is generated automatically by the server, but the password is something that is created by the user and known by the user only.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: The passwords have to be stored on the server and it must be so in a secure way (hashed and salted), never in plain text! More detailed information on this topic could be found at &lt;a href="https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/" rel="noopener noreferrer"&gt;https://auth0.com/blog/adding-salt-to-hashing-a-better-way-to-store-passwords/&lt;/a&gt; or &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html" rel="noopener noreferrer"&gt;https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now I can send my user ID and my password in the request to the server and it will detect that it is me, and so, it can respond with my private data or perform the requested action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ffl2iekz8ulrpptyf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3ffl2iekz8ulrpptyf5.png" alt="HTTP request with credentials"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Positives&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server authenticates me.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The passwords could be weak and vulnerable to brute-force attacks.&lt;/li&gt;
&lt;li&gt;They can be stolen through phishing or data breaches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Force the passwords to have a minimum length and be as random as possible.&lt;/li&gt;
&lt;li&gt;Add a second layer of authentication (2FA or MFA) - I will discuss this topic later in the series.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we have 2 pieces of information to send to the server so it can acknowledge our identity, which is a significant step in making the communication private. But here is a catch: should we send the user ID and the password over and over for each subsequent request? &lt;/p&gt;

&lt;h2&gt;
  
  
  Dealing with subsequent requests
&lt;/h2&gt;

&lt;p&gt;Even if we use HTTPS, which encrypts the information sent over the network, repeatedly sending the password for every request exposes some real dangers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An attacker could make the exact same request with different password combinations until they guess the correct one. Of course, this process requires a lot of time, but as long as the password doesn’t change, the chances to guess the password increase.&lt;/li&gt;
&lt;li&gt;Even with HTTPS, some potential interception points do exist, like malware on the user’s device, or someone inside a local network monitoring communications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What is the solution then? To create temporary identifiers that are very hard to guess, have an expiration time, and are associated with the user’s identity. These keys are generated by the server and sent to the client after a successful authentication, and the client will send them back to the server for subsequent requests, so the server will know the identity of the client. This is just a high-level description of the concept. &lt;/p&gt;

&lt;h3&gt;
  
  
  Evolution of temporary identifiers
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Session identifiers
&lt;/h4&gt;

&lt;p&gt;In the early days, the industry developed &lt;strong&gt;session IDs&lt;/strong&gt;. The core idea was to generate a random identifier and associate it with the user information. These two pieces of information (the random identifier and the user data) were stored on the server side and the identifier was sent back to the client via &lt;strong&gt;cookies&lt;/strong&gt;, which was a mechanism already in place in the browsers. Subsequent requests to the server automatically included this cookie (this is how the browser works by default), so the usage was straightforward. Limitations became apparent in distributed systems and API-driven architecture due to the complexity of managing and synchronizing the session IDs across different servers.&lt;/p&gt;

&lt;h4&gt;
  
  
  API keys
&lt;/h4&gt;

&lt;p&gt;To address some limitations, &lt;strong&gt;API keys&lt;/strong&gt; emerged, which were long, random strings that granted access to specific APIs. They helped with scalability, but they focused on simply identifying an application, not a specific user, they didn’t have a proper standardized structure, and they lacked flexibility and fine-grained authorization.&lt;/p&gt;

&lt;h4&gt;
  
  
  Web Tokens
&lt;/h4&gt;

&lt;p&gt;The need for identifying the user based on these keys led to the development of &lt;strong&gt;self-contained tokens&lt;/strong&gt;. Early implementations lacked a common standard, but then the &lt;strong&gt;JSON Web Token (JWT)&lt;/strong&gt; specifications were adopted, which provided a standardized way to structure, sign, and verify tokens. This approach was characterized by statelessness and proved to be more consistent and interoperable for authentication, allowing APIs to validate user identity and permissions without relying on server-side session data.&lt;/p&gt;

&lt;p&gt;To address the security concern of not exposing the tokens for a long period of time, a new type of token was introduced, namely &lt;strong&gt;refresh token&lt;/strong&gt;, that is used to generate new &lt;strong&gt;access tokens&lt;/strong&gt;, which have a relatively shorter lifespan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Session IDs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Definition
&lt;/h3&gt;

&lt;p&gt;Session IDs are a random and unique list of characters that the server generates after it receives and successfully validates the user’s credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;The client sends the credentials to the server.&lt;/li&gt;
&lt;li&gt;The server validates them and if they are valid, a session ID is generated.&lt;/li&gt;
&lt;li&gt;The server has a session management system that stores the ID (in memory, in a file, in a database, etc…) along with the user’s details.&lt;/li&gt;
&lt;li&gt;The server responds to the client with that ID.&lt;/li&gt;
&lt;li&gt;The client stores the ID.&lt;/li&gt;
&lt;li&gt;The client will send that ID with future requests to the server.&lt;/li&gt;
&lt;li&gt;The server validates the received session ID, extracts the associated user information and processes the request.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;This mechanism typically relies on &lt;strong&gt;browser cookies&lt;/strong&gt; to handle the transmission of the ID between the client and the server.&lt;/p&gt;

&lt;p&gt;Let me explain how things happen under the hood on the server-side. For a robust and secure session cookie management, the server often sets two response headers, &lt;strong&gt;Set-Cookie&lt;/strong&gt; and &lt;strong&gt;Strict-Transport-Security&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Set-Cookie: sessionid=12345; Secure; HttpOnly; SameSite=...&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Secure&lt;/em&gt;: Instructs the browser to only send the cookie over HTTPS connections&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;HttpOnly&lt;/em&gt;: Prevents client-side JavaScript from accessing the cookie. This mitigates some cross-site scripting (XSS) attacks that could attempt to steal cookies.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;SameSite&lt;/em&gt;: Helps defend against cross-site request forgery (CSRF) attacks. The appropriate choice of SameSite values depends on your application's cross-origin behavior needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Strict-Transport-Security: max-age=31536000; includeSubDomains&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This header tells the browser to always use HTTPS for the specified domain and potentially subdomains, even if the user tries to access it via a non-secure HTTP link (more details about this header can be found on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security" rel="noopener noreferrer"&gt;MDN docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;These being said, after the user successfully authenticates, the browser will store the session ID from the server in a cookie.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: For more information about cookies you can refer to the &lt;a href="https://datatracker.ietf.org/doc/html/rfc6265" rel="noopener noreferrer"&gt;IETF specs page&lt;/a&gt;, or to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies" rel="noopener noreferrer"&gt;MDN Cookie docs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Same-domain vs Cross-domain requests
&lt;/h3&gt;

&lt;p&gt;Let’s say that our application is hosted on a domain. When a user enters their credentials and submits the login form, a request is sent to an API endpoint. This endpoint could be located on the same domain, on a subdomain, or on a different domain, depending on the application architecture, even though the first two options are more commonly used. How are the cookies set and sent in these scenarios, knowing that cookies have some restrictions, which will explore right away?&lt;/p&gt;

&lt;h4&gt;
  
  
  Same domain
&lt;/h4&gt;

&lt;p&gt;Let’s assume the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;our application is served from &lt;code&gt;https://www.myangularapp.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the login endpoint is located at &lt;code&gt;https://www.myangularapp.com/api/login&lt;/code&gt; (the same domain as previous)&lt;/li&gt;
&lt;li&gt;other API endpoints are accessed at &lt;code&gt;https://www.myangularapp.com/api/&lt;/code&gt; (again, the same domain)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this case when all requests are made to the same domain (or subdomain) the browser automatically includes any existing cookies set by the server, so no additional configuration is needed.&lt;/p&gt;

&lt;p&gt;The server creates the session ID cookie when we make the login request and the browser stores it as a cookie. Any other subsequent requests to &lt;code&gt;https://www.myangularapp.com&lt;/code&gt; or any other subdomains will automatically include this session cookie. &lt;/p&gt;

&lt;p&gt;This approach is very popular due to its simplicity and security, the deployment is made on a single server and there is not need for additional cookie configuration. However, for larger and more complex applications, scalability could be negatively impacted. Additionally, a major drawback this approach introduces is the single point of failure: if the server goes down, the application will be unavailable, or, a vulnerability in the API could compromise the entire application.&lt;/p&gt;

&lt;h4&gt;
  
  
  Different subdomains
&lt;/h4&gt;

&lt;p&gt;Let's now assume this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;our application is served from &lt;code&gt;https://www.myangularapp.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the api endpoints (including the login) are located at &lt;code&gt;https://api.myangularapp.com&lt;/code&gt;, so we have a different subdomain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we make the login request, without additional configuration, the origin server creates the session ID cookie for its domain (&lt;code&gt;https://api.myangularapp.com&lt;/code&gt;), and the browser will send that cookie when it makes a request to that domain or its subdomains, but not to &lt;code&gt;https://www.myangularapp.com&lt;/code&gt;, which is like a sibling subdomain. In order to be able to send the cookie to another subdomain, the server should define the &lt;strong&gt;Domain&lt;/strong&gt; attribute value for the cookie to be &lt;code&gt;myangularapp.com&lt;/code&gt;. This will result in the browser to be able to send the cookie to any subdomains.&lt;/p&gt;

&lt;p&gt;But why would we need multiple subdomains for an application? The main reasons are scalability and isolation. Each subdomain could be responsible with a specific part of the app and have its own resources. But this introduces additional work: a server from a subdomain would be unaware of the session cookies created by another server, so a session management system needs to be developed. This is itself another complex topic that mostly involves software architects, but I shortly list some options: centralized session store, session replication, or session on each subdomain.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cross-origin requests
&lt;/h4&gt;

&lt;p&gt;There could be cases when requests need to be made to a different origin. Due to the CORS mechanism and the same-origin policy, we need some extra configuration on both the server and the client side to handle the cookie transmission. More technical documentation about these mechanisms can be found on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;MDN CORS docs&lt;/a&gt;. By default, browsers will not send cookies in requests to other origins. To allow sending and receiving cookies, both the server and the client need some extra configuration.&lt;/p&gt;

&lt;p&gt;The servers on different origins needs at minimum to set these response headers:&lt;br&gt;
&lt;code&gt;Access-Control-Allow-Origin: &amp;lt;our_application_domain&amp;gt;&lt;br&gt;
 Access-Control-Allow-Credentials: true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On the client side, when we make the request, we need to declare it as "credentialed", so the cookies can be sent to the server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;for the native &lt;code&gt;fetch&lt;/code&gt; function, we need to set the option &lt;code&gt;{credentials: 'include'}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;if we use the native &lt;code&gt;XMLHttpRequest&lt;/code&gt; constructor, we need to set &lt;code&gt;withCredentials = true&lt;/code&gt; on the request instance.&lt;/li&gt;
&lt;li&gt;in Angular apps, we have to set &lt;code&gt;{ withCredentials: true }&lt;/code&gt; option within every &lt;code&gt;HttpClient&lt;/code&gt; request method.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Benefits and limitations of session-based authentication
&lt;/h3&gt;

&lt;p&gt;Like any other system, there is no one-size-fits-all solution, and every approach needs to be analyzed in its context. Let's explore a summary of the main benefits and limitations of session-based authentication.&lt;/p&gt;
&lt;h4&gt;
  
  
  Benefits
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Simple implementation: once the session ID is received by the browser, the subsequent requests automatically send the ID to the server (with the exceptions with the cross-domain or subdomain requests I've mentioned above)&lt;/li&gt;
&lt;li&gt;Reduced server load for small-scale applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This solution is best suitable for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small projects with simple architecture&lt;/li&gt;
&lt;li&gt;Applications with primarily server-rendered pages&lt;/li&gt;
&lt;li&gt;Frontend and backend residing on the same domain&lt;/li&gt;
&lt;li&gt;The need of revoking the session immediately (in sectors like banking, critical healthcare)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Limitations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;By definition, this mechanism is &lt;strong&gt;stateful&lt;/strong&gt;, and the server needs a robust session management system to handle an increasing number of active sessions.&lt;/li&gt;
&lt;li&gt;Non-browser clients (e.g. mobile apps) that don’t support cookies would need additional logic to handle how the ID is transmitted back to the server.&lt;/li&gt;
&lt;li&gt;Cookies are strictly tied to the domain that set them and are not shareable by default with other domains, unless the &lt;code&gt;Domain&lt;/code&gt; attribute is explicitly configured.&lt;/li&gt;
&lt;li&gt;Working with distributed backends servers or microservices would require additional complexity to handle the session IDs&lt;/li&gt;
&lt;li&gt;Potential risks like CSRF (Cross-Site Request Forgery), Session hijacking, XSS (Cross-Site Scripting) vulnerabilities leading to cookie theft (I'll cover these in another article).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Use cases / Examples in Angular
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Systems that use session cookies
&lt;/h4&gt;

&lt;p&gt;There are numerous web frameworks that heavily rely on session-based authentication using cookies: Laravel, CodeIgniter, Symfony, Ruby on Rails, Django, Spring MVC, ASP.NET MVC, platforms like Magento, Shopify, WordPress, or legacy applications.&lt;/p&gt;
&lt;h4&gt;
  
  
  Logging out
&lt;/h4&gt;

&lt;p&gt;We’ve discussed how to login, but at some point we also want to logout. For this we usually make a request to a logout endpoint, similar to the login. The server needs to invalidate the session ID on its side and then to clear the cookie, by specifying an expiry date in the past, so the browser will automatically clear the session cookie.&lt;br&gt;
&lt;code&gt;Set-Cookie: sessionid=; Expires=Thu, 01 Jan 1970 00:00:00 UTC; Path=/;&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Authentication failures
&lt;/h4&gt;

&lt;p&gt;For certain reasons the server could respond with HTTP 401 UNAUTHORIZED status code, or any other responses indicating that the user is not authenticated. Such cases can happen due to an expired or invalid session ID. In our frontend Angular app we can handle these errors by redirecting the user to the login page to submit their credential again. For this we can use an interceptor:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  See it in action
&lt;/h4&gt;

&lt;p&gt;Here are some useful videos about the topic I've discussed so far:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/qN5aLvs7Rys"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/BgsQrOHNKeY"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UzF7eb2iZe0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>angular</category>
      <category>authentication</category>
      <category>cookies</category>
    </item>
    <item>
      <title>A Simple Angular User Profile Form: Building a Foundation for Refactoring</title>
      <dc:creator>Cezar Pleșcan</dc:creator>
      <pubDate>Wed, 17 Apr 2024 13:29:19 +0000</pubDate>
      <link>https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-1-4m1c</link>
      <guid>https://dev.to/cezar-plescan/level-up-your-angular-code-a-transformative-user-profile-editor-project-part-1-4m1c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article series I want to walk you through a journey of code evolution within a practical user profile editor application. I will transform and optimize a simple Angular implementation into a well structured, maintainable solution using advanced techniques.&lt;/p&gt;

&lt;p&gt;If you've ever felt overwhelmed by growing code complexity, this series is for you! I'll guide you step-by-step, demonstrating how to tackle common challenges through &lt;strong&gt;state management&lt;/strong&gt;, &lt;strong&gt;imperative programming&lt;/strong&gt;, &lt;strong&gt;reactive programming&lt;/strong&gt;, &lt;strong&gt;SOLID principles&lt;/strong&gt;, &lt;strong&gt;Observables&lt;/strong&gt;, &lt;strong&gt;NgRx component-store&lt;/strong&gt;, &lt;strong&gt;NgRx global store&lt;/strong&gt;, &lt;strong&gt;signals&lt;/strong&gt;, &lt;strong&gt;error handling&lt;/strong&gt;, &lt;strong&gt;uploading files&lt;/strong&gt;, &lt;strong&gt;form validation&lt;/strong&gt;, &lt;strong&gt;custom form control&lt;/strong&gt;, &lt;strong&gt;HTTP interceptors&lt;/strong&gt;, &lt;strong&gt;local web server&lt;/strong&gt; with node.js, &lt;strong&gt;directives&lt;/strong&gt;, and many more. I'll start with the basics and progressively add layers of sophistication and discuss their tradeoffs. You will be able to better understand when and why to choose the proper solution for a specific context.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The task at hand is to create a &lt;strong&gt;user profile editor&lt;/strong&gt;, similar to the one in the image below. The editor will include a form with four fields: &lt;em&gt;Name&lt;/em&gt;, &lt;em&gt;Email&lt;/em&gt;, &lt;em&gt;Address&lt;/em&gt;, and &lt;em&gt;Avatar&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmyx4axzgbq30k6rique.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcmyx4axzgbq30k6rique.png" alt="User profile editor form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;From the end-user perspective, I want to be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change any of the fields (name, email, address)&lt;/li&gt;
&lt;li&gt;change the avatar image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an improved user experience, I'll add some functional requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;display any validation error messages (e.g. “Please enter a valid email address”)&lt;/li&gt;
&lt;li&gt;disable the Save button if there are no changes or while the saving request is in progress&lt;/li&gt;
&lt;li&gt;provide a button for resetting the changes&lt;/li&gt;
&lt;li&gt;display a progress indicator for upload status (when changing the avatar).&lt;/li&gt;
&lt;li&gt;display a loading indicator while the user data is retrieved from the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other improvements may come later on the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Project set up
&lt;/h3&gt;

&lt;p&gt;Begin by cloning the &lt;a href="https://github.com/cezar-plescan/user-profile-editor.git" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and checkout the &lt;code&gt;00.base&lt;/code&gt; branch. The project uses Angular v17.3.3.&lt;/p&gt;

&lt;p&gt;Then run &lt;code&gt;yarn install&lt;/code&gt; and &lt;code&gt;ng serve&lt;/code&gt;. Currently there is nothing implemented so far, but this is what we'll be doing next.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: Yarn is a replacement for npm. It can be installed with &lt;code&gt;npm install -g yarn&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Planning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before actually writing code, we need some planning&lt;/strong&gt;. It might be tempting to jump right into the editor and start writing code. However, I consider it essential to take a step back and think about the overall structure of the application. &lt;/p&gt;

&lt;p&gt;In this planning phase I want to focus on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;What is the end result, what will the user see on the page?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What are the main tasks that need to be completed?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In what order should these tasks be done?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The high-level tasks I would define are, in order of their priority:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create the form layout (just displaying the necessary fields, without any data handling)&lt;/li&gt;
&lt;li&gt;read user data from an API and populate the form&lt;/li&gt;
&lt;li&gt;handle server errors&lt;/li&gt;
&lt;li&gt;updating the user data&lt;/li&gt;
&lt;li&gt;uploading a new avatar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we have a good understanding of the overall plan, we can start to break down the tasks into smaller, more manageable pieces, which will make it easier to write the code and track the progress.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 1: Create the form layout
&lt;/h3&gt;

&lt;p&gt;I will use a reactive form with Material components. Open &lt;em&gt;user-profile.component.html&lt;/em&gt; file and paste the following content:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Then open &lt;em&gt;user-profile.component.ts&lt;/em&gt; and paste:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Your user-profile component code should look like &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/01.form/src/app/user-profile" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I also added some styling for the template.

&lt;p&gt;The form is now in place with some placeholder values. We want to make this realistic and read the data from the backend, which will be covered in the next section.&lt;/p&gt;
&lt;h3&gt;
  
  
  Task 2: Develop an API endpoint for user data retrieval
&lt;/h3&gt;

&lt;p&gt;To facilitate communication with the backend to read and save data, I'll use a simple node.js express server that simulates a real-world backend. The code needs to reside in a folder named &lt;code&gt;backend&lt;/code&gt; within the root project folder. You can checkout the entire code from &lt;code&gt;02.get-api&lt;/code&gt; branch where you'll find the contents of the backend server. The &lt;code&gt;package.json&lt;/code&gt; file includes the new packages and scripts for building and running the server. The data is stored locally in the db.json file. You need to run &lt;code&gt;yarn install&lt;/code&gt; and then &lt;code&gt;yarn run backend:run&lt;/code&gt; to start the server (run these commands from the root project folder). The server starts listening on port 3000, so make sure no other app is using it.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;a href="http://localhost:3000/users" rel="noopener noreferrer"&gt;http://localhost:3000/users&lt;/a&gt; or &lt;a href="http://localhost:3000/users/1" rel="noopener noreferrer"&gt;http://localhost:3000/users/1&lt;/a&gt; and you should see the contents from the db.json file.&lt;/p&gt;

&lt;p&gt;Now that we have the get endpoint API in place, let's read the data from our component.&lt;/p&gt;
&lt;h3&gt;
  
  
  Task 3: Read user data and populate the form
&lt;/h3&gt;

&lt;p&gt;First of all I'll jump to our component class and declare my intention to read the user data when the component is initialized. &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: As a &lt;em&gt;general practice&lt;/em&gt;, I prefer to use &lt;strong&gt;descriptive functions or methods&lt;/strong&gt; for &lt;strong&gt;what&lt;/strong&gt; I have to to, and then to describe &lt;strong&gt;how&lt;/strong&gt; to implement them. This approach is like starting with building a skeleton of a system, along with the high-level workflow, and after that to actually implement how each operation is executed. Working this way has been helping me to implement complex systems and I consider it paramount in designing efficient software.&lt;/p&gt;

&lt;p&gt;Now let's describe what &lt;code&gt;loadUserData()&lt;/code&gt; should do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make an http get request to the API.&lt;/li&gt;
&lt;li&gt;when the response is ready, to populate the form with the user data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For simplicity, I'll write the entire necessary code in the component class, without creating an additional service for getting user data. Of course, this is not a good practice for a production code, but I want to make small steps and gradually improve the code. &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I had to create some interfaces for our data:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;pick&lt;/code&gt; function is from the lodash library, so you need to install some packages: &lt;code&gt;yarn add lodash-es&lt;/code&gt;, &lt;code&gt;yarn add --dev @types/lodash-es&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You also need to inject the &lt;code&gt;HttpClient&lt;/code&gt; service into the component class and to declare its provider in the &lt;code&gt;app.config.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;For now I assume a successful API response, and later we'll implement the error handling.&lt;/p&gt;

&lt;p&gt;Having the server started, reload the page. Your code should now be similar to the one on the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/03.get-user-data/src/app/user-profile" rel="noopener noreferrer"&gt;03.get-user-data&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 4. Display a loading indicator
&lt;/h3&gt;

&lt;p&gt;We have the user data displayed, which is a good progress. I want to improve the user experience by showing a loading indicator until the request is completed.&lt;/p&gt;

&lt;p&gt;For this I need two pieces of information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a flag that indicates that the load request is in progress,&lt;/li&gt;
&lt;li&gt;a visual representation of the loading state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will implement the flag as a boolean class property &lt;code&gt;isLoadRequestInProgress&lt;/code&gt; and update its value right before the request is initiated and after it completes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this moment we know when the load request is in progress and we have to update the template based on this state. There could be different ways to visualize the loading progress (like a spinner animation), but I will choose the simplest one, that is, to hide the entire form and show a message.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: My intention with this tutorial is to provide the mechanisms to handle different scenarios, upon which visually appealing content could be created in collaboration with the UX team. From a software design perspective, I achieve the separation of concerns: I just have the information about the loading status, but I'm not concerned about how the view will look like. This separation enhances extensibility, someone could want later to change the view, but the logic of how the loading status is computed will be unaffected by the change request.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you may notice, I'm using the &lt;code&gt;@if&lt;/code&gt; new control syntax from Angular v17. It does the same as the classical &lt;code&gt;*ngIf&lt;/code&gt; directive:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Because you're serving the application form your localhost you may not notice the loading message. You could simulate a slower connection from the Network tab in DevTools window.&lt;/p&gt;

&lt;p&gt;Your code should now be similar to the one on the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/04.loading-indicator/src/app/user-profile" rel="noopener noreferrer"&gt;04.loading-indicator&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 5. Handling loading errors
&lt;/h3&gt;

&lt;p&gt;In the real world we could encounter errors when making an HTTP request, for whatever reasons. This is something that we must handle and not assume that errors don't happen.&lt;/p&gt;

&lt;p&gt;What do we need in order to handle any errors from the loading request?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to know when an error occurs&lt;/li&gt;
&lt;li&gt;to inform the user about the error&lt;/li&gt;
&lt;li&gt;to offer the user the ability to retry the request&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Catching the error
&lt;/h4&gt;

&lt;p&gt;I'll use the &lt;code&gt;catchError&lt;/code&gt; RxJS operator (&lt;a href="https://rxjs.dev/api/index/function/catchError" rel="noopener noreferrer"&gt;see RxJS docs&lt;/a&gt;) and for simplicity I'll just set a flag &lt;code&gt;hasLoadingError&lt;/code&gt; that indicates the presence of an error. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: As I've mentioned earlier, I want to provide only the mechanism of signaling the error, without considering any other details. In a real-world scenario the errors could have various sources, like no network connection, internal server error, unauthorized access, and so on. Handling these errors is a different topic not covered by this tutorial.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the updated &lt;code&gt;loadUserData()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I must return an observable from the catchError callback, so I'm using the &lt;code&gt;EMPTY&lt;/code&gt; observable (&lt;a href="https://rxjs.dev/api/index/const/EMPTY" rel="noopener noreferrer"&gt;see RxJS docs&lt;/a&gt;), otherwise, if I return a non-empty observable, the subscriber's &lt;code&gt;next&lt;/code&gt; method will be invoked with the returned observable value.&lt;/p&gt;

&lt;p&gt;I've also used the &lt;code&gt;finalize&lt;/code&gt; operator (&lt;a href="https://rxjs.dev/api/operators/finalize" rel="noopener noreferrer"&gt;see RxJS docs&lt;/a&gt;), which has its callback executed when the request observable terminates on complete or error, to reset the &lt;code&gt;isLoadRequestInProgress&lt;/code&gt; flag.&lt;/p&gt;

&lt;h4&gt;
  
  
  Inform the user about the error and retry the operation
&lt;/h4&gt;

&lt;p&gt;Now that we have the information if an error occurred, we have to update the template to display a simple message. Additionally we can add a Retry button to reload the data. In a real-world application there could be different use cases, but I choose a simple scenario for now.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;To simulate an error there are some options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to modify the URL in the &lt;code&gt;getUserData$()&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;to stop the backend server&lt;/li&gt;
&lt;li&gt;to simulate an offline network connection from DevTools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I consider stopping the backend server the most convenient option. Here is my scenario you could follow: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stop the backend server&lt;/li&gt;
&lt;li&gt;reload the app in the browser: you should see the error message&lt;/li&gt;
&lt;li&gt;click Retry: the loading message should be visible and then the error (you can eventually throttle down the network connection)&lt;/li&gt;
&lt;li&gt;restart the backend server and click Retry: the form should be now displayed with the expected user data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage you should have the code similar to the one on the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/05.loading-errors/src/app/user-profile" rel="noopener noreferrer"&gt;05.loading-errors&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 6. Saving the user data
&lt;/h3&gt;

&lt;p&gt;One of the main features of this tutorial is the ability to update the user profile data. For this we need a data persistent storage which is provided by the backend server. At this stage I'll focus on updating the name, email and address, without the profile avatar, which I'll cover in a separate task. I'll implement error handling in a later step, but for now, I'll assume successful save operations.&lt;/p&gt;

&lt;p&gt;The implementation for this features requires two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sending the data to the server&lt;/li&gt;
&lt;li&gt;saving the data on the server-side&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Sending the data to the server
&lt;/h4&gt;

&lt;p&gt;This action is triggered when the form is submitted, so I need to define the &lt;code&gt;ngSubmit&lt;/code&gt; handler to make a PUT request to the server with the current form value. To keep the things simple, I leave aside any form validation for now.&lt;/p&gt;

&lt;p&gt;Update the user-profile.component.html template:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;and the user-profile.component.ts component class:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Updating the data on the server
&lt;/h4&gt;

&lt;p&gt;Add the following content to your server.ts file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is just a simple implementation to save the user data and shouldn't be used in a production code.&lt;/p&gt;

&lt;p&gt;After making these changes restart the backend server, reload the browser, and make any changes in the form (keep in mind that there are no validations in place). After you submit the form reload the page and the changes should be persistent.&lt;/p&gt;

&lt;p&gt;At this stage you should have the code similar to the one on the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/06.save-user-data/src/app/user-profile" rel="noopener noreferrer"&gt;06.save-user-data&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 7. Validate user data and display the validation errors
&lt;/h3&gt;

&lt;p&gt;The user data need to have some constraints to be saved in the database, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the name should not be empty and it should be unique&lt;/li&gt;
&lt;li&gt;the email should have a valid format&lt;/li&gt;
&lt;li&gt;the address should not be empty&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: these are just some simple validation rules to illustrate how to work with them.&lt;/p&gt;

&lt;p&gt;As a general best practice, the server should always validate the data it receives, regardless on any client-side validations. For simplicity, our server will validate only if the user's name is unique, leaving the other validation rules on the client-side.&lt;/p&gt;

&lt;h4&gt;
  
  
  Client-side validation and error messages
&lt;/h4&gt;

&lt;p&gt;We already have some validators in place for the form fields (see the &lt;code&gt;form&lt;/code&gt; property definition in the &lt;code&gt;UserProfileComponent&lt;/code&gt; class). What we need is to display the validation error messages in the template:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I've added the &lt;code&gt;&amp;lt;mat-error&amp;gt;&lt;/code&gt; element for displaying the validation errors, which is automatically shown by Angular Reactive Forms only when the corresponding field has errors.&lt;/p&gt;

&lt;h4&gt;
  
  
  Server-side validation
&lt;/h4&gt;

&lt;p&gt;As mentioned above, I'll validate on the server-side only if the user's name is unique:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In order for the validation to fail, you need to add a new user in the &lt;code&gt;db.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;As a convention, the server responds with 400 HTTP status code, and with a body containing the validation errors.&lt;/p&gt;

&lt;h4&gt;
  
  
  Display server-side validation errors
&lt;/h4&gt;

&lt;p&gt;In the case of client-side validation, the errors were implicitly set by the Angular Reactive Form, but for the server-side validation errors we need to manually handle them. Update the &lt;code&gt;saveUserData()&lt;/code&gt; method from the &lt;code&gt;user-profile.component.ts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The template should be updated too:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Disable form submission
&lt;/h4&gt;

&lt;p&gt;You may have noticed that even if there are client-side validation errors, the form can still be submitted. To avoid this we can simply disable the submit button. The form will internally detect the status of the submit button and even if we hit the ENTER key in any of the fields, the form won't be submitted.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this stage you should have the code similar to the one on the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/07.validate-user-data/src/app/user-profile" rel="noopener noreferrer"&gt;07.validate-user-data&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 8. Add some UX improvements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;add a Reset button&lt;/li&gt;
&lt;li&gt;disable the Save button while the save request is in progress or the form is pristine&lt;/li&gt;
&lt;li&gt;add a notification when the saving successfully completes&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Reset the form
&lt;/h4&gt;

&lt;p&gt;This feature is useful when we want to reset the form to the last saved state.&lt;br&gt;
These are the changes I've made:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;created a new Reset button&lt;/li&gt;
&lt;li&gt;defined the &lt;code&gt;restoreForm()&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;defined the disabled state of the button&lt;/li&gt;
&lt;li&gt;determine is the form value is changed since the last save&lt;/li&gt;
&lt;li&gt;disable the Save button if there are no changes&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Disable the Save button
&lt;/h4&gt;

&lt;p&gt;One additional condition can be used to disable the Save button: while the save request is in progress. I also want to disable the form controls during this state.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Add a notification
&lt;/h4&gt;

&lt;p&gt;After a successful save I want to display a notification. I'll use the Material snackbar component for this. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this stage you should have the code similar to the one on the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/08.ux-improvements/src/app/user-profile" rel="noopener noreferrer"&gt;08.ux-improvements&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 9. Handle unexpected saving errors
&lt;/h3&gt;

&lt;p&gt;Earlier I've described how to handle validation errors, which is something that we can expect to happen. But what about other errors, like internal server error, or no network connection error. If any of such errors happen, I want to simply display a notification to the user. I'll just use the &lt;code&gt;NotificationService&lt;/code&gt; for this, which can eventually be customized to use different colors for the snackbar, but this is out of scope for now.&lt;br&gt;
Let's update the &lt;code&gt;catchError&lt;/code&gt; callback in the &lt;code&gt;saveUserData()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We need to simulate some errors to see how the error handling works. Here are some possible options to try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simulate no network connection from the browser DevTools,&lt;/li&gt;
&lt;li&gt;stop the backend server,&lt;/li&gt;
&lt;li&gt;programatically send an error from the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code below shows how to generate a server error. Just for demo purposes, if we enter '&lt;em&gt;error&lt;/em&gt;' in the name field, the server will send a 500 error:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Task 10. Uploading an avatar image
&lt;/h3&gt;

&lt;p&gt;Here comes a very nice feature to implement: uploading and displaying an image. The entire process requires some steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;select an image to upload&lt;/li&gt;
&lt;li&gt;display the uploaded image&lt;/li&gt;
&lt;li&gt;store the image on the server&lt;/li&gt;
&lt;li&gt;generate a URL for it&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Select an image and display it
&lt;/h4&gt;

&lt;p&gt;The straightforward implementation is to use the &lt;code&gt;&amp;lt;input type="file" /&amp;gt;&lt;/code&gt; element for selecting an image from the local computer.&lt;/p&gt;

&lt;p&gt;In the template, we'll have to update 2 parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the source of the image&lt;/li&gt;
&lt;li&gt;the button to upload an image&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;onImageSelected&lt;/code&gt; method is called every time the user selects a new image. We need to display that image in place of the existing avatar. An image needs an URL, which can be an external or local resource. I don't want to upload the image right after selecting it, because the user could maybe change their mind, so I'll use &lt;code&gt;URL.createObjectURL()&lt;/code&gt; function that will generate a local URL based on the file content.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I want to add a fix here: after I select an image and then click the Reset button, the avatar is indeed restored, but the name of the previously selected file is still displayed, and it should be reset too. For this I'll just write an empty value to the input element:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  Upload the image to the server
&lt;/h4&gt;

&lt;p&gt;The standard and recommended way to send files to the server is using the &lt;code&gt;FormData&lt;/code&gt; browser API. The object created with this constructor will be sent as a payload to the saving request. As a consequence, the values of the other 3 form fields (name, email, address) will be part of this FormData object.&lt;/p&gt;

&lt;p&gt;Let's see how we can construct this object. Instead of directly sending the form values, we'll create a FormData object that holds the form values and replace the avatar with the selected image, if any.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Once the client-side is set up, we have to upgrade the backend server to support uploading files. An implementation option is to use the &lt;code&gt;multer&lt;/code&gt; package. First of all, we have to install the additional packages as dev dependencies:&lt;br&gt;
&lt;code&gt;yarn add -D multer @types/multer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then open your &lt;em&gt;server.ts&lt;/em&gt; file and add the following contents to set up the library configuration (it's recommended to be added at the beginning of the file):&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: There are some additional things to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create the &lt;code&gt;images&lt;/code&gt; folder inside &lt;code&gt;backend&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add the contents of that folder to &lt;code&gt;.gitignore&lt;/code&gt;: &lt;code&gt;/backend/images/*&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we need to run the uploading process and return the generated filename. For this we have to update the &lt;code&gt;/users/:id&lt;/code&gt; route:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Make sure to restart the backend server and then go to the app and upload an image. If you refresh the page, the image is not displayed anymore, because its URL points to the local Angular web server, instead of the server URL. Let's fix this.&lt;/p&gt;

&lt;p&gt;In the template I'll use a method to compute the image URL:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Try to upload again an image and save the profile. You may notice that the Save and Reset buttons are still enabled, but they should be disabled. Why is this happening? Because the form is not computed as pristine due to the avatar value holding the local URL to the selected file. To solve this, we have to reset the form after the saving completes successfully:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;At this stage you should have the code similar to the one on the &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/10.avatar-upload/src/app/user-profile" rel="noopener noreferrer"&gt;10.avatar-upload&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 11. Upload progress indicator
&lt;/h3&gt;

&lt;p&gt;A nice feature I want to have is an indicator for the upload progress. For the visual representation I pick the &lt;code&gt;MatProgressBar&lt;/code&gt; component from the Angular Material library. Let's explore how the compute the percentage value of the progress bar.&lt;/p&gt;

&lt;h4&gt;
  
  
  Getting access to upload progress information
&lt;/h4&gt;

&lt;p&gt;It is made available by setting &lt;code&gt;reportProgress&lt;/code&gt; and &lt;code&gt;observe&lt;/code&gt; options of the request:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Now the return type of the &lt;code&gt;saveUserData$&lt;/code&gt; method is &lt;br&gt;
&lt;code&gt;Observable&amp;lt;HttpEvent&amp;lt;UserDataResponse&amp;gt;&amp;gt;&lt;/code&gt;, where before it was just &lt;code&gt;Observable&amp;lt;UserDataResponse&amp;gt;&lt;/code&gt;. We'll make the necessary adjustments soon.
&lt;h4&gt;
  
  
  Visual representation
&lt;/h4&gt;

&lt;p&gt;Add the following snippet right before the closing &lt;code&gt;&amp;lt;/form&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
The &lt;code&gt;uploadProgress&lt;/code&gt; variable will be described in the next paragraph.
&lt;h4&gt;
  
  
  Computing the upload progress percentage
&lt;/h4&gt;

&lt;p&gt;We need to update the observer function of the &lt;code&gt;saveUserData$()&lt;/code&gt; observable in the &lt;code&gt;saveUserData()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Let's see it in action! Select an image having around 1MB and throttle down the network connection from the DevTools. You should see how the progress bar grows and then it hides.&lt;/p&gt;

&lt;p&gt;You should be in sync with the code from &lt;a href="https://github.com/cezar-plescan/user-profile-editor/tree/11.upload-progress/src/app/user-profile" rel="noopener noreferrer"&gt;11.upload-progress&lt;/a&gt; branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outro
&lt;/h3&gt;

&lt;p&gt;We've covered up the requirements from the beginning of the tutorial and we've made a good progress so far. While functional, the current implementation prioritized getting the features working, without discussing about any software design best practices. I've just wanted to have a starting point for the following articles that will be about improving this simple version by focusing on scalability and extensibility, which are the foundation of Angular enterprise applications. &lt;/p&gt;

&lt;p&gt;To manage something big we have to be able to control small things first, every oak grows from a little seed, and so is our expertise: start small, add little by little continuously, and the result will get bigger and bigger.&lt;/p&gt;

&lt;p&gt;In the next articles I'll focus on refactoring the existing code by following the SOLID principles and by covering the implementation of: &lt;strong&gt;custom form control&lt;/strong&gt; for image management, error handling in &lt;strong&gt;HTTP interceptors&lt;/strong&gt;, &lt;strong&gt;custom RxJs operators&lt;/strong&gt;, &lt;strong&gt;directive&lt;/strong&gt; for displaying the form field errors.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>form</category>
    </item>
  </channel>
</rss>
