Front End Developer Interview Questions For 3 Years Of Experience

The main difference between `ng add` and `npm install` lies in their purpose and functionality within the Angular ecosystem. 1. `ng add`: This is a command specific to the Angular CLI (Command Line Interface). It is used to add new packages, libraries, or schematics to an Angular project. When you run `ng add`, it performs several tasks automatically, such as installing the necessary packages, configuring the project, and making any required changes to the project's files. The `ng add` command is often used to quickly integrate third-party libraries or extensions into an Angular project with minimal effort. Suppose you want to add the Angular Material library to your Angular project. Instead of manually configuring and installing Angular Material, you can use the `ng add` command to simplify the process. Here's how you would do it:

   ng add @angular/material

When you run this command, the Angular CLI will perform the following tasks: - Install the `@angular/material` package and its dependencies using `npm install`. - Configure the project to use Angular Material by updating the `angular.json` file. - Import and configure the necessary modules and styles in the project's files (e.g., `app.module.ts` and `styles.scss`). `ng add` is a convenient way to add Angular-specific packages or schematics to your project while automating the necessary setup steps. 2. `npm install`: This is a general command provided by npm (Node Package Manager) for installing packages from the npm registry. It is not specific to Angular but is used across various JavaScript and Node.js projects. When you run `npm install `, it installs the specified package and its dependencies into the project. It typically updates the `package.json` file to include the installed package as a dependency. However, `npm install` does not perform any specific configuration or modification of the project's files. Let's say you want to install the `lodash` library, which is a popular utility library for JavaScript. In this case, you would use the `npm install` command directly, as follows:

   npm install lodash

Running this command will: - Fetch the `lodash` package and its dependencies from the npm registry. - Install the package locally within your project's `node_modules` directory. - Update the `package.json` file to include `lodash` as a dependency. `npm install` is a general-purpose command used to install any package from the npm registry into your project, regardless of the specific framework or library being used. In summary, `ng add` is a specialized command within the Angular CLI that automates the installation and configuration of packages specifically designed for Angular projects. On the other hand, `npm install` is a general command provided by npm to install packages from the npm registry into any JavaScript or Node.js project, including Angular projects.
Custom directives are a feature in Angular that allow developers to extend the functionality of HTML by creating their own custom HTML elements or attributes. With custom directives, developers can define their own behavior, such as adding event listeners, modifying the DOM, or manipulating data. To create a custom directive in Angular, follow these steps: 1) Create a new directive using the @Directive decorator. The decorator specifies the selector for the directive and any inputs, outputs, and other options. 2) Define the directive class, which contains the logic for the directive. The class should implement the OnInit and OnDestroy interfaces to handle the initialization and destruction of the directive. 3) Add the directive to the declarations array in the module that will use it. This tells Angular that the directive should be available for use in that module. Here is an example of a simple custom directive that changes the background color of an element:

import { Directive, ElementRef, Renderer2 } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) { }

  ngOnInit() {
    this.renderer.setStyle(this.el.nativeElement, 'background-color', 'yellow');
  }

  ngOnDestroy() {
    this.renderer.removeStyle(this.el.nativeElement, 'background-color');
  }
}

In this example, the HighlightDirective sets the background color of an element to yellow when it is initialized, and removes the background color when it is destroyed. The ElementRef and Renderer2 classes are used to access and manipulate the element in the DOM. To use this directive in a template, simply add the appHighlight attribute to an element:

  <p apphighlight>
  This text will have a new yellow background.
 </p>

When the template is rendered, the HighlightDirective will be applied to the element, changing its background color to yellow.
The main differences between the HTTP `GET` and `POST` methods are their intended purposes and the way they handle data: 1. Purpose: - `GET`: The `GET` method is used to retrieve data from a server. It is meant for reading or fetching a resource without modifying it on the server. - `POST`: The `POST` method is used to send data to the server to create or update a resource. It is meant for submitting data, such as form submissions, to be processed by the server. 2. Data Handling: - `GET`: Data is appended to the URL as query parameters. For example, `https://example.com/api/users?id=123`. This makes the data visible in the URL and is limited in size due to URL length restrictions. It is suitable for passing small amounts of data, but it's not recommended for sensitive or large data. - `POST`: Data is sent in the body of the HTTP request, which is not visible in the URL. This allows for sending larger amounts of data, and there are no URL length restrictions. It is suitable for sensitive or large data, such as JSON payloads. 3. Caching: - `GET`: `GET` requests can be cached by the browser or intermediate proxies since they are considered safe and idempotent. The same `GET` request can be repeated multiple times without any side effects. - `POST`: By default, `POST` requests are not cached because they may have side effects on the server, such as creating or updating resources. However, caching can be explicitly enabled for `POST` requests using appropriate cache headers. 4. Idempotence: - `GET`: `GET` requests are idempotent, meaning that making the same `GET` request multiple times should have the same result. It should not modify any data on the server. - `POST`: `POST` requests are not idempotent since they typically result in the creation or modification of a resource on the server. Making the same `POST` request multiple times may create multiple resources or have different outcomes. 5. Security: - `GET`: Since `GET` requests append data to the URL, the data becomes visible in browser history, server logs, and can be bookmarked. It is not recommended to send sensitive data via `GET` requests as it can be easily exposed. - `POST`: Data sent via `POST` requests is included in the body and is not directly visible in browser history or server logs, offering better security for sensitive information. Example : Here are examples that demonstrate the difference between the `GET` and `POST` methods: 1. `GET` Method Example: Let's say you have a RESTful API that provides information about users. To retrieve the details of a specific user with the ID "123", you would use a `GET` request. Here's an example using JavaScript's `fetch` API:

   fetch('https://example.com/api/users/123', {
     method: 'GET'
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `GET` request is made to the URL `https://example.com/api/users/123`, indicating that you want to retrieve user information for the user with the ID "123". The server will respond with the requested user data. 2. `POST` Method Example: Suppose you have a contact form on a website, and you want to send the form data to a server for processing. In this case, you would use a `POST` request to submit the data. Here's an example using JavaScript's `fetch` API:

   const formData = {
     name: 'John Doe',
     email: 'john@example.com',
     message: 'Hello, World!'
   };

   fetch('https://example.com/api/contact', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json'
     },
     body: JSON.stringify(formData)
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `POST` request is made to the URL `https://example.com/api/contact` with the form data serialized as JSON in the request body. The server will process the submitted data, such as storing it in a database or sending an email. In summary, the `GET` method is used for retrieving data without modifying it, while the `POST` method is used for sending data to create or update a resource. `GET` requests append data to the URL, have caching advantages, and are idempotent. `POST` requests send data in the request body, are not cached by default, and are not idempotent.
The main differences between the HTTP `POST` and `PUT` methods are their intended purposes and the way they handle data: 1. Purpose: - `POST`: The `POST` method is used to send data to the server to create a new resource. It is often used when submitting forms or sending data that needs to be processed and stored on the server. Each `POST` request typically creates a new resource on the server, and the server assigns a unique identifier to it. - `PUT`: The `PUT` method is used to send data to the server to update an existing resource or create a resource at a specific URL. It is used when you know the exact location of the resource you want to update or create. A `PUT` request can either update an existing resource or create a new one if it doesn't already exist at the specified URL. 2. Idempotence: - `POST`: `POST` requests are not idempotent. Sending the same `POST` request multiple times may result in the creation of multiple resources with different identifiers or cause repeated side effects on the server. - `PUT`: `PUT` requests are idempotent. Making the same `PUT` request multiple times will have the same outcome, ensuring that the resource on the server is updated or created consistently. 3. Data Handling: - `POST`: Data is sent in the body of the HTTP request, typically as form data or serialized JSON/XML. The server processes this data and performs the necessary actions to create a new resource. - `PUT`: Data is sent in the body of the HTTP request, similar to the `POST` method. However, in a `PUT` request, the data represents the complete updated representation of the resource. The server uses this data to replace the existing resource or create a new one. 4. URL Convention: - `POST`: The URL for a `POST` request typically points to a collection endpoint or a general resource endpoint. For example, `https://example.com/api/users` to create a new user. - `PUT`: The URL for a `PUT` request usually points to a specific resource or a unique identifier for that resource. For example, `https://example.com/api/users/123` to update the user with the ID "123". 5. Safe Operations: - `POST`: `POST` requests are not considered safe operations as they can result in the creation of new resources or have side effects on the server. - `PUT`: `PUT` requests are not considered safe operations either, as they can update or create resources on the server. Example: Here are examples that demonstrate the difference between the `POST` and `PUT` methods: 1. `POST` Method Example: Let's say you have an API that handles blog posts. To create a new blog post, you would use a `POST` request. Here's an example using JavaScript's `fetch` API:

   const newPost = {
     title: 'My New Blog Post',
     content: 'This is the content of my new blog post.'
   };

   fetch('https://example.com/api/posts', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json'
     },
     body: JSON.stringify(newPost)
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `POST` request is made to the URL `https://example.com/api/posts`, indicating that you want to create a new blog post. The server will process the request, create a new blog post with the provided data (`newPost`), and respond with the created post details. 2. `PUT` Method Example: Suppose you have an API that manages user profiles, and you want to update an existing user's information. To achieve that, you would use a `PUT` request. Here's an example using JavaScript's `fetch` API:

   const updatedUser = {
     name: 'Jane Doe',
     email: 'jane@example.com'
   };

   fetch('https://example.com/api/users/123', {
     method: 'PUT',
     headers: {
       'Content-Type': 'application/json'
     },
     body: JSON.stringify(updatedUser)
   })
     .then(response => response.json())
     .then(data => {
       console.log(data);
     })
     .catch(error => {
       console.error('Error:', error);
     });

In this example, the `PUT` request is made to the URL `https://example.com/api/users/123`, indicating that you want to update the user with the ID "123". The server will process the request, replace the existing user's information with the provided data (`updatedUser`), and respond with the updated user details. Conclusion: In summary, the `POST` method is used to send data to create a new resource, while the `PUT` method is used to send data to update an existing resource or create a new one at a specific URL. `POST` requests create new resources, are not idempotent, and send data representing the resource to be created. `PUT` requests update or create resources, are idempotent, and send data representing the complete updated representation of the resource.
The canActivateChild route guard in Angular allows you to check if a user is allowed to activate child routes. It is used to protect child routes of a particular route from being activated if certain conditions are not met. When a user navigates to a child route, Angular checks if there is an associated canActivateChild guard in the parent route's route configuration. If there is, Angular executes the guard before activating the child route. The canActivateChild guard is implemented as a service that implements the CanActivateChild interface. This interface has a single method, canActivateChild(), which returns a boolean or a promise that resolves to a boolean. If the method returns true, the child route is activated. If it returns false or a promise that resolves to false, the child route is not activated, and the user is redirected to a different route, or a custom error page is displayed. Here is an example of how to use the canActivateChild route guard:

import { Injectable } from '@angular/core';
import { CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivateChild {

  canActivateChild(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable | Promise | boolean {
    // Check if the user is authenticated
    if (this.authService.isAuthenticated()) {
      return true;
    } else {
      // If the user is not authenticated, redirect to the login page
      this.router.navigate(['/login']);
      return false;
    }
  }
}

In this example, we have an AuthGuard service that implements the CanActivateChild interface. The canActivateChild() method checks if the user is authenticated using a method isAuthenticated() provided by an AuthService. If the user is authenticated, the method returns true, allowing the child route to be activated. If the user is not authenticated, the method navigates to the login page and returns false, preventing the child route from being activated. To use the AuthGuard service, you can add it to the canActivateChild property in the route configuration for the parent route that guards its child routes:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { ParentComponent } from './parent.component';
import { ChildComponent } from './child.component';

const routes: Routes = [
  {
    path: 'parent',
    component: ParentComponent,
    canActivateChild: [AuthGuard],
    children: [
      {
        path: 'child',
        component: ChildComponent
      }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

In this example, the canActivateChild property is set to an array containing the AuthGuard service. This guards the child route with the AuthGuard. Still confused? if yes, check link canActivateChild - tektutorialshub for explanation
Custom events are events that are created by developers to handle specific scenarios or to extend the capabilities of the built-in events. Custom events allow you to define and trigger events that are not natively available in the browser or the DOM. The ability to create custom events gives you more flexibility in designing event-driven systems and enables you to build modular, reusable components that communicate with each other using custom events. Custom events can be useful in various scenarios, such as: 1. Communication between components: Custom events provide a way for different components of an application to communicate and exchange information. Components can listen for custom events and respond accordingly. 2. Application-level events: You can create custom events to represent application-level events, such as "applicationInitialized" or "userLoggedOut." These events can be triggered at specific points in your application and can be used to trigger actions or update the UI. 3. Event-driven architecture: Custom events facilitate an event-driven architecture, where different parts of your application can be decoupled and communicate through events. This promotes loose coupling and improves the modularity and maintainability of your codebase. To work with custom events, you can use the `CustomEvent` constructor and the `dispatchEvent()` method to create and trigger custom events. Additionally, you can use the `addEventListener()` method to listen for and handle custom events. Here's a step-by-step guide on how to create custom events: 1. Create an event using the `CustomEvent` constructor:

   const myEvent = new CustomEvent('myEvent', {
     detail: { key: 'value' },
     bubbles: true, // Specifies whether the event should bubble up through the DOM tree (optional)
     cancelable: true // Specifies whether the event can be canceled with preventDefault() (optional)
   });

In the example above, we create a custom event named `'myEvent'`. The event can carry additional data in the `detail` property, which is an optional object. The `bubbles` and `cancelable` properties determine the behavior of the event during event propagation and allow for event cancellation if desired. 2. Dispatch the custom event on an element:

   const element = document.getElementById('myElement');
   element.dispatchEvent(myEvent);

In this step, we select the desired HTML element on which the event should be dispatched. Here, we use `document.getElementById('myElement')` to obtain the element with the ID `'myElement'`. Then, we call the `dispatchEvent()` method on the element, passing in the custom event `myEvent` as the argument. 3. Listen for the custom event:

   const element = document.getElementById('myElement');
   element.addEventListener('myEvent', event => {
     console.log('Custom event triggered!', event.detail);
   });

Finally, we register an event listener on the element to capture and handle the custom event. In this example, when the `'myEvent'` event is triggered, the provided callback function will execute. You can access the additional data passed in the event's `detail` property using `event.detail`. You have created a custom event, dispatched it on an element, and set up an event listener to respond to the event. You can adapt this approach to meet your specific use cases and define custom behavior for your events in JavaScript.
In Angular, a ReplaySubject is a type of subject provided by the RxJS library. It is a variant of the Subject class and allows you to multicast values to multiple subscribers. A ReplaySubject remembers and replays a specific number of values to any subscriber that subscribes to it. When a new subscriber subscribes to a ReplaySubject, it will immediately receive the buffered values, up to a specified buffer size or timeframe. The key features of a ReplaySubject are: 1. Buffering: A ReplaySubject keeps a buffer of values that it has emitted. You can specify the maximum number of values to buffer using the buffer size parameter when creating the ReplaySubject. 2. Subscription: When a new subscriber subscribes to a ReplaySubject, it immediately receives the buffered values. If the buffer size is reached, older values are dropped from the buffer to accommodate new values. 3. Timeframe: In addition to the buffer size, you can also specify a timeframe for the ReplaySubject. With a timeframe, the ReplaySubject will only buffer values emitted within a specific time window. ReplaySubjects are useful in scenarios where you want subscribers to receive previously emitted values, even if they subscribe at a later time. For example, if you have a component that needs to fetch some initial data on initialization, you can use a ReplaySubject to cache the data and ensure that any subsequent subscribers receive the cached values. Here's an example usage of a ReplaySubject in Angular:

import { ReplaySubject } from 'rxjs';

// Create a ReplaySubject with a buffer size of 3
const subject = new ReplaySubject(3);

// Emit values to the ReplaySubject
subject.next('Value 1');
subject.next('Value 2');
subject.next('Value 3');

// Subscribe to the ReplaySubject
subject.subscribe(value => console.log('Received:', value));

// Output: Received: Value 1, Received: Value 2, Received: Value 3

// Emit another value
subject.next('Value 4');

// Output: Received: Value 4 (new subscriber receives the latest value)

// Subscribe again after some time
setTimeout(() => {
  subject.subscribe(value => console.log('Received later:', value));
}, 2000);

// Output: Received later: Value 2, Received later: Value 3, Received later: Value 4

In the example above, the ReplaySubject buffers the last 3 emitted values. When a new subscriber subscribes, it immediately receives the buffered values. The second subscriber, which subscribes after some time, receives the buffered values that were emitted within the specified timeframe. ReplaySubjects are a powerful tool in Angular when you need to share and replay values among multiple subscribers, especially when dealing with asynchronous data streams.
In JavaScript, both the `for...in` and `for...of` loops are used for iteration, but they serve different purposes and work with different types of data structures. Here's the difference between them: 1. `for...in` loop: The `for...in` loop is used to iterate over the enumerable properties of an object. It iterates over the keys of an object and provides access to the property names. Here's the basic syntax:

for (variable in object) {
  // code to be executed
}

The `variable` represents the property name in each iteration. Here's an example:

const obj = { a: 1, b: 2, c: 3 };

for (let key in obj) {
  console.log(key); // Outputs: a, b, c
  console.log(obj[key]); // Outputs: 1, 2, 3
}

Note that `for...in` loop can also iterate over the inherited properties of an object. To avoid this, you can use the `hasOwnProperty` method to check if the property is directly defined on the object itself. 2. `for...of` loop: The `for...of` loop is used to iterate over iterable objects like arrays, strings, sets, maps, etc. It provides access to the values of the elements rather than their indices or keys. Here's the basic syntax:

for (variable of iterable) {
  // code to be executed
}

The `variable` represents the value in each iteration. Here's an example:

const arr = [1, 2, 3];

for (let value of arr) {
  console.log(value); // Outputs: 1, 2, 3
}

Unlike the `for...in` loop, the `for...of` loop does not give you access to the keys or indices of the iterable object. It directly provides the values. It's a simpler and more concise way to iterate over arrays and other iterable objects. Conclusion: `for...in` loop is used to iterate over object properties (keys), while the `for...of` loop is used to iterate over iterable objects, providing access to their values.
In Angular, there is a distinction between adding styles and scripts in the `angular.json` configuration file and adding them directly in the `index.html` file. Here's a breakdown of the key differences: 1. `angular.json`: The `angular.json` file is the configuration file for an Angular project. It contains various settings and options related to the project's build process, including the configuration for styles and scripts. In the `angular.json` file, you can specify the styles and scripts that should be included as part of the project build process. The styles are typically specified using the `"styles"` property, which accepts an array of file paths for CSS or Sass files. The scripts are typically specified using the `"scripts"` property, which accepts an array of file paths for JavaScript or TypeScript files. These styles and scripts defined in the `angular.json` file will be automatically included and bundled as part of the build process. They will be loaded and applied to the application during runtime. 2. `index.html`: The `index.html` file is the main HTML file for an Angular application. It is the entry point for the application and contains the basic structure of the HTML document. In the `index.html` file, you can add external stylesheets or scripts directly using `<link>` and `<script>` tags. These tags reference external CSS or JavaScript files hosted on a server or included from a CDN. By including them in the `index.html` file, they will be loaded by the browser when the application is accessed. The styles and scripts added directly in the `index.html` file are separate from the ones specified in the `angular.json` file. They are not part of the build process or bundling performed by Angular. Instead, they are loaded and applied by the browser directly when the `index.html` file is loaded. In summary, the key differences are: - Styles and scripts specified in the `angular.json` file are part of the Angular build process and bundled with the application during build time. - Styles and scripts added directly in the `index.html` file are loaded by the browser at runtime, separate from the Angular build process. Typically, it is recommended to use the `angular.json` file for including styles and scripts that are part of the project and managed by Angular. On the other hand, adding external stylesheets or scripts in the `index.html` file is useful for including third-party libraries or custom styles and scripts that are not managed by Angular.
In SCSS (Sass), mixins are a feature that allows you to define reusable blocks of CSS code. A mixin is similar to a function in programming, as it can accept parameters and generate CSS styles based on those parameters. Mixins provide a way to encapsulate and reuse common styles, making your code more modular and maintainable. To define a mixin in SCSS, you use the `@mixin` directive, followed by a name and a block of CSS code. Here's an example:

@mixin center-element {
  display: flex;
  align-items: center;
  justify-content: center;
}

In this example, we define a mixin called `center-element` that applies common styles to center an element both vertically and horizontally using flexbox. To use a mixin, you can include it in a selector using the `@include` directive, followed by the name of the mixin. Here's an example:

.container {
  @include center-element;
}

The `@include` directive includes the `center-element` mixin in the `.container` selector, which applies the styles defined in the mixin to that selector. After compilation, the generated CSS will include the styles from the mixin:

.container {
  display: flex;
  align-items: center;
  justify-content: center;
}

Mixins can also accept parameters, allowing you to customize the generated styles. Here's an example of a mixin with parameters:
@mixin link-color($color) {
  color: $color;
  text-decoration: none;
  
  &:hover {
    text-decoration: underline;
  }
}

In this example, the `link-color` mixin accepts a `$color` parameter. When using the mixin, you can pass a specific color value to customize the link's color. Here's an example:

a {
  @include link-color(blue);
}

After compilation, the generated CSS for the `a` selector will include the customized color:

a {
  color: blue;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

Mixins in SCSS provide a powerful way to reuse and share CSS code, making your stylesheets more maintainable and reducing code duplication. They are particularly useful for common styles or styles that require customization in different parts of your project.