This commit is contained in:
mrinal1224
2024-05-07 12:57:28 +05:30
parent b6c1969f0e
commit 98aa698dd9
48 changed files with 24210 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
## Introduction
When we think about accessing a website, there's more happening behind the scenes than meets the eye. The URL, or Uniform Resource Locator, is what we usually type into the address bar to access a web page. However, the URL represents much more than just a web address. It's a pathway to the actual resource we're trying to access on the internet.
## Process
To break it down, when we enter a URL, the full form of URL comes into play: **Uniform Resource Locator**. This term accurately describes what it does — it locates a specific resource on the internet. This resource could be anything from a text document to a video, and the server's job is to provide us with that resource.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/110/original/upload_06940025115633bec555d541d722b876.png?1695109040)
A **server**, in this context, isn't a physical location but rather a program running somewhere in the world. This program generates the website content for us. It's important to note that a server is not a database. Rather, it's a responsive entity that resides somewhere in the vast expanse of the digital world. Imagine it as a helpful entity that receives your request and promptly serves you the requested information.
## Dairy Farm Analogy
Here's an analogy to help clarify the roles involved: Imagine you own a dairy farm and have numerous customers who regularly place orders for dairy products. To manage this influx of orders, you have an **operations team** that handles the order-taking process. They ensure that customers' requests are recorded accurately and are then forwarded to the **production team**.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/111/original/upload_9cef5b39282b29f01216e706282bbb64.png?1695109075)
In the context of websites, the operations team can be likened to the **DNS (Domain Name System)** system. The DNS system acts like an operations team, taking in requests and translating them into specific IP addresses. Think of DNS as a phonebook for the internet. When you enter a domain name like "scaler.com," the DNS system translates it to an IP address that points to a particular server.
However, it's important to note that the server itself is not where the data comes from. Instead, it's comparable to the dairy farm in our analogy—it's responsible for assembling and providing the products. In our website world, the actual data resides in a **database**. This database is akin to a **warehouse** for the dairy farm. All the products are stored there, ready to be accessed when needed.
When a request is made, the server applies specific protocols and data logic to retrieve the necessary information from the database. This process is what ensures that we receive the correct data as a response to our request.
Bringing it all together, the customers in our dairy farm analogy represent clients or users of the website. The operations team corresponds to the DNS system, efficiently directing requests. The dairy farm itself serves as the server, assembling and providing the desired information. And finally, the warehouse embodies the database, housing all the necessary data for the website.
In the grand scheme of things, even though we might simply see a website's interface through our browser, there's a complex interplay of components behind every web page that ensures we get the right information at the right time.
### HTML boiler plate code
#### Code
```html
<!doctype html>
<html lang = "en">
<head>
<meta charset = "utf - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1">
<title>Bootstrap demo</title>
</head>
<body>
<h1>Hello, world ! </h1>
</body>
</html>
```
### Structure your code
#### Div
Div elements are often used to structure and style sections of a web page, making it easier to apply CSS styles or JavaScript functionality to specific groups of content.
#### Code:
```html
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML</title>
</head>
<body>
<div>
<h2>Welcome to Scalar Topics</h2>
<p>
We're glad you're here
</p>
</div>
</body>
</html>
```
#### Output
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/112/original/upload_b1f0471a9f3040bdd40eb364e9101835.png?1695109208)
#### Section
Sections are used to structure the content of a web page into logical parts, such as chapters, articles, or different sections of a document.
#### Code:
```html
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML</title>
</head>
<body>
<section>
<h2>Section Title</h2>
<p>This is a section of content.</p>
</section>
</body>
</html>
```
### Tags and buttons
### Header tags
Header tags are used to structure the hierarchy of content on a webpage, with `<h1>` typically being the main title and `<h2>`, `<h3>`, and so on used for subsections. They help improve the accessibility and readability of content.
#### Code:
```html
<h1>Main Heading</h1>
<h2>Subheading</h2>
<h3>Sub-subheading</h3>
```
#### Output
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/113/original/upload_0666ce092529c955114832a23824d462.png?1695109287)
#### Anchor tags
Anchor tags are used to link to other web pages or resources, both within the same website or externally to other websites.
#### Code:
```html
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML</title>
</head>
<body>
<section>
<a href = "https://www.scaler.com/topics/autoboxing-in-java/">Learn autoboxing - in - java</a>
</section>
</body>
</html>
```
#### Output
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/114/original/upload_a280807eb2f3df89d88691096051f0eb.png?1695109351)
#### Image tags
Image tags are used to display graphics, photographs, icons, or any other visual content on a webpage. The alt attribute provides alternative text for accessibility and SEO purposes.
#### Code:
```html
<!DOCTYPE html>
<html>
<head>
<title>Basic HTML</title>
</head>
<body>
<img src = "img.jpg" alt = "Description of the image">
</body>
</html>
```
#### Output
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/115/original/upload_4f0ea4a42e75394bc79c854cbe6cafce.png?1695109387)
#### Buttons
Button elements are used to create clickable elements that can trigger actions when clicked, such as submitting a form or triggering JavaScript functions.
#### Code:
```html
<button type = "button"> BUTTON </button>
```
#### Output
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/116/original/upload_e873e3ecb310330c64503dafcf33ad53.png?1695109456)

View File

@@ -0,0 +1,244 @@
## List
In HTML you can structure your content in a more readable and organized manner using lists. There are two types of lists such as
1. Ordered lists `<ol>` and,
2. Unordered lists `<ul>`
## 1. Ordered List
An ordered list is used to create a list of items that have a specific sequence or order. Each item in an ordered list is typically numbered, and the numbers usually increment in a sequential manner.
Each list item is defined with the `<li>` (list item) element, this will be more clear further in this article.
An ordered list can store content in two ways as well:
1. Non-alphabetically ordered
2. Alphabetically ordered
#### 1.1 Ordered List
In the following code you can clearly see that the output generated is not structured alphabetically but listed in an orderely fashion
#### Pseudocode
```html
<!DOCTYPE html>
<html>
<head>
<title>List Examples</title>
</head>
<body>
<ol>
<li>Banana</li>
<li>Apple</li>
<li>Strawberry</li>
</ol>
</body>
</html>
```
#### Output:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/117/original/upload_2e99c2e6057a7cefa9e96c95d9c27f37.png?1695110158)
### 1.2. Alphabetical Ordered List
In order to structure list in alphabetical fashion we can use `type = "A"` between `<ol>` tags
#### Pseudocode
```html
<!DOCTYPE html>
<html>
<head>
<title>Alphabetical Ordered List</title>
</head>
<body>
<h1>Alphabetical Ordered List Example</h1>
<ol type = "A">
<li>Banana</li>
<li>Apple</li>
<li>Strawberry</li>
</ol>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/118/original/upload_ef0ce2a0099cb2aed2158fe245251153.png?1695110223)
### 2. Unordered List
Use `<ul>` tags for displaying a list with the help of symbols/shapes. In this case we're considering shapes.
#### Pseudocode
```html
<!DOCTYPE html>
<html>
<head>
<title>List Examples</title>
</head>
<body>
<ul type = "circle">
<li>Banana</li>
<li>Apple</li>
<li>Strawberry</li>
</ul
</body>
</html>
```
#### Output:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/119/original/upload_c4298d24e8363ee59290b3ea888e16ca.png?1695110251)
## Tables in HTML
We gnerally tables are used to organize and display data in a structured format. Tables consist of rows and columns, where each cell is capable of storing data in form of text, images, links, or other HTML elements. But in-order to create tables in HTML one needs to be familiar with certain tags and attributes used such as:
1. `<table`: tag is used to define the beginning of a table
2.`<td>`: tag represents a table cell that contains data
3. `<tr>`: tag represents a table row
4. `<th>` : tag represents a table header and is used to label columns or provide additional information about the data
5. `<thead>`: tag is used to group the header content in a table. It typically contains one or more `<tr>` elements with `<th>` elements inside.
6. `<tbody>`: tag groups the body content of the table.
7. `<caption>`: tag is used to provide a title or caption for the table. It is placed immediately after the opening `<table>` tag.
8. **border attribute**: is used to specify the thickness of the border around the table and its cells.
#### Pseudocode
```html
<!DOCTYPE html>
<html>
<head>
<title>List Examples</title>
</head>
<body>
<table border style = "text-align: center;">
<caption>Student's Marksheet</caption>
<thead>
<th>Student</th>
<th>Roll No.</th>
<th>Marks</th>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2016911</td>
<td>98</td>
</tr>
<tr>
<td>2</td>
<td>2015911</td>
<td>78</td>
</tr>
</tbody>
</table>
</body>
</html>
```
#### Output:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/120/original/upload_051534e2f8bbaa444c5fff971dbba317.png?1695110359)
## HTML Forms
Just like any other forms are used to collect information, in context of HTML it collects user input on a web page. They allow users to enter data, make selections, and submit that information to a server for processing. HTML forms are created using a combination of form-related tags and input elements such as
1. `<form>`: tag defines the beginning of a form and contains the elements that make up the form, such as text fields, checkboxes, and buttons. It has several attributes, including action (specifying the URL to which the form data should be submitted) and method (specifying the HTTP request method, usually "**GET**" or "**POST**")
2. `<input>`: The `<input>` tag is used to create various types of form input fields. The type attribute specifies the type of input field to be displayed. Common type values include:
* **text:** Creates a single-line text input.
* **password:** Creates a password input field (text is masked for security).
* **checkbox:** Creates a checkbox for binary (true/false) choices.
* **radio:** Creates a radio button for selecting one option from a group.
* **submit:** Creates a submit button to send the form data.
* **button:** Creates a generic button.
* **file:** Allows users to upload files.
* **date, email, number, and more:** Various input types for specific data formats.
3. `<label>`: tag is used to associate a label with a form element, making the form more accessible and user-friendly. The for attribute of the `<label>` tag should match the id attribute of the form element it labels.
4. `<select>`: tag creates a dropdown list, allowing users to select one option from a list of choices. It contains one or more `<option>` elements.
5. `<option>`: tag defines an individual item in a `<select>` dropdown list. The value attribute specifies the value that will be sent to the server when the form is submitted.
6. `<button>`: acts as an input element in HTML and is used for various purposes in a form including the submission of a form. We have different types of input elements as well such as checkboxes `<input type = "checkbox">`, radio buttons `<input type = "radio">`, and other input elements as needed to gather user input
#### Pseudocode
```html
<!DOCTYPE html>
<html>
<head>
<title>List Examples</title>
</head>
<body>
<form>
<label>Name</label>
<input type = "text">
<label>Email</label>
<input type = "email">
<label>Phone</label>
<input type = "number" maxlength = "10">
<label>age</label>
<input type = "number" min = "1" max = "100">
<label>Birthday</label>
<input type = "date" min = "1905 - 06 - 18" max = "2002 - 06 - 10">
<p>Food</p>
<label>Apple</label>
<input type = "checkbox">
<label>Orange</label>
<input type = "checkbox">
<label>Banana</label>
<input type = "checkbox">
<p>Gender</p>
<label>Male</label>
<input type = "radio">
<label>Female</label>
<input type = "radio">
<label for = "">Eyecolor</label>
<select>
<option>Blue</option>
<option>Black</option>
<option>Red</option>
</select>
</form>
</body>
</html>
```
#### Output:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/121/original/upload_f466e18fe597bb978dd2f93f02a9c0f7.png?1695110510)
## Inline Elements
In HTML, elements are categorized as either block-level elements or inline elements. Inline elements do not create new block-level containers and flow within the content of surrounding elements. The `<span> `element is one such common inline element used for various purposes. It's a generic inline container that is often used to apply styles or attributes to a specific portion of text within a larger block of content.
#### Pseudocode
```html
<!DOCTYPE html>
<html>
<head>
<title>Span in Inline Element Example</title>
</head>
<body>
<h1>Inline Element with Span</h1>
<p>This is a <strong><span style = "color: red ; ">red</span></strong> word.</p>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Span in Inline Element Example</title>
</head>
<body>
<h1>Inline Element with Span</h1>
<p>This is a <strong><span style = "color: red;">red</span></strong> word.</p>
</body>
</html>
```
#### Output:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/122/original/upload_c10b7b956b460dadbe2f8996096eed32.png?1695111073)

View File

@@ -0,0 +1,314 @@
# Agenda:
- What is CSS
- Need for CSS
- CSS Selectors
- Properties of CSS (color, background, fonts, and texts)
So, lets start!
>Various ways of using CSS
They are:
1. Inline
2. Internal
3. External
These are the things that we will discuss. Now, let us see them one by one.
- Create a basic HTML file and add a heading **h1** inside the body tag entitled: "Heading 1" and open it in a browser to show how it looks.
- Now, let us see how to add CSS to this element.
# Steps to add style tag:
- Add `<style>` and `</style>` tag inside the head tag of your HTML file. Inside this **style** tag, you can use all the CSS stylings.
- Select the element that you need to add CSS.
- For example, h1 and add curly braces to it. Inside this, you can define the CSS properties as shown below:
```HTML=
h1{
color : brown;
}
# Using CSS in three different ways:
## 1. Internal CSS
- When you write CSS in the same HTML file. (using the style tag)
## 2. Inline CSS
- It is writing CSS for a particular element. (using style attribute.)
### Example
```
<h2 style = "color: red;"> I am heading 2 </h2>
```
- Here we are providing the CSS to that specific element "h2" only, known as Inline CSS. Always try to add a style tag inside the **head tag**.
- Inline CSS has more priority than Internal CSS.
## 3. External CSS
- You can create a separate file for CSS having the extension "**.css**".
- Here you do not need to use any HTML tag. You can directly write your CSS and properties.
### Example
```HTML=
H3{
color: green;
}
```
To reflect these CSS into your HTML file, you need to link that CSS file to the HTML file.
- Use the "link" tag to do this as shown below.
```
<link rel="CSS_file_name" href="./CSS_file_name.css">
```
- You can add the file location in the **href** to add the CSS file if it is at some other location in your system.
> These are the three ways that can be used to apply CSS to your HTML file. Now we will discuss the ways of selecting a particular element.
- As we have seen in the previous example, **h1{}** was the selector. But there are more ways of selecting elements.
- Have a brief discussion about how the students can select an element as the previous method that we learned till now.
## Descendent selectors
- Anything down the order can be termed as a descendent. For example, a father is the descendent of their child.
- Now, you need to select the obvious list as shown in the image and make it blue color.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/035/original/upload_741aa927d48eecd0dcd6ec59f15eba43.png?1695311213)
- For that first discuss the "**descent selector**".
- When you select **ol li{}** as the selector then all the elements will get blue. So you need to select the specific parents that are "div" as shown in the example.
- We will write the selector as:
```HTML=
div li{
color: blue;
}
```
Now moving to Children Selectors.
## Children selectors
Let us take an example as shown below.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/036/original/upload_6e331819a556124295cda866b1ad80df.png?1695311277)
There are two span tags. Here we need to make the text "I am the direct son" blue using CSS.
- Use the greater than **">"** symbol to use direct children.
```html=
div > h1 > span{
color: blue;
}
```
- It means that you are directing to apply CSS to the span element that is directly children to the **h1**.
Let's see another type of selector which is the Classes
## Classes
- It is a very important part when you are learning CSS.
- Classes are defined as separate entities of similar elements.
- For real real-life example, if you are in class 10, then all the students there should be in class 10 only.
- Similarly, in CSS, similar elements having the same behavior are separated in a specific class as shown below code:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/037/original/upload_130e57374ed680b30734deba9ee0b2c7.png?1695311313)
- We use dot (.) to select a class and apply CSS to them. Here the name of the class is test so we will apply CSS to this class as below:
```HTML=
.test{
color: blue;
}
```
This will be applied to both the elements of the class namely the **test**.
> Discuss a question having two different classes namely **class1** and **class2** and apply CSS on them to make one class **blue** and one class **red**.
- Solution:
```HTML=
.class1{
color: blue;
}:
.class2{
color: red;
}
```
### Question on multiple classes
Suppose a situation when there are two or more than two classes for a single element as shown below and you have to make some specific elements blue.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/038/original/upload_1a43d7aa8cd4e546b6264e88799e956b.png?1695311389)
- Here we need to select multiple classes **m1** and **m2** and make them blue.
- Solution:
```HTML=
.m1.m2{ <!-- it is multiple selector -->
color : blue;
}
```
Here you will use multiple classes as shown in the example to apply the CSS to some specific elements in such cases.
### Question on a combination of all the previously discussed selectors.
You have to make the text "**I'm here, find me**" blue as shown in the question image below. Feel free to use any selector or combination of selectors you want.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/039/original/upload_e17f8e227f4328a482d1cc22803e0dc5.png?1695311505)
- **Solution**:
```HTML=
.c1 .c2{
color: blue;
}
```
### Question on Class with Children Combinator
In this question you need to make the select and make the text "**I'm a direct son**" blue.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/041/original/upload_6e8bceeaca78cf983eb897650d522316.png?1695311598)
- **Solution**:
```HTML=
.c1>p{
color: blue;
}
```
### Question on id selector
- The id selector uses the id attribute of an HTML element to select a specific element.
- The id of an element is unique within a page, so the id selector is used to select one unique element.
- We use "#" to address the id.
### Example
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/042/original/upload_0a85830042d3f0d832f66b0c11e9ab40.png?1695311661)
- You need to select **s2** with the "id=the-one" and make blue
- Solution:
```HTML=
#the-one{
color: blue;
}
```
### Question on attribute selector
- CSS attribute selectors are used to select and style HTML elements with the specified attributes and values.
- Let us take an example as shown below. Here we need to select the button element and make the color blue.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/044/original/upload_106e606616e173b79a0a71bd501d9812.png?1695311699)
- **Solution**:
```HTML=
input[value="Select me"]{
color: blue;
}
```
Here you can write the element like "input" and start the bracket "[]". Inside the bracket, you can write the attribute that you are selecting to apply CSS.
- That's how the attribute selector works.
# 1. Normal Color
- The very basic way of using colors is as follows as we have discussed earlier that is `h1{color: color_name;}`
- Using this method you can use the 140 colors of CSS. But what do you need to use a color apart from these 140 colors?
# 2. RGB
- Then we use the **RGB value** to change the element colors.
- RGB is the color concept that shows how you can create any color using a definite proportion of these three colors that are **Red**, **Green**, and **Blue**.
The range of RGB values is from **(000 to 255)**.
Let us take an example how to use RGB values for CSS.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/045/original/upload_001bf16ebd83d5ea0df12fc19d263397.png?1695311917)
[
- You can also add **opacity** property where you pass the RGB values.
- Range of opacity is from (0.00 to 1.00)
- For example-
```HTML=
color : rgba(255, 165, 100, 0.50);
```
# 3. Color code (Hexadecimal code)
- Color codes are three-byte hexadecimal numbers (meaning they consist of **six digits**), with each byte, or pair of characters in the Hex code, representing the intensity of **red**, **green**, and **blue** in the color respectively.
- We have hex code for every color in CSS.
- You can find these codes on Google search also.
- You can use these hex codes to paste into your CSS thing.
- Remember to apply hashtag **"#"** before the codes. For example- **#DC143C**
Now, let us see the 4th method of CSS Colors.
# 4. HSC
- It stands for **Hue**, **Saturation**, and **Lightness**.
## Example
```HTML=
h2{
color : hsl(100%, 15%, 25%)
}
```
- In this, we define these three properties in percentage.
# Final Question of the session
Now, let us summarize the session and use all the previous selectors and color properties to solve this problem shown below:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/046/original/upload_cf0319b7c90943234aa84a26a0e8ff6f.png?1695312027)
- **Solution**:
First, we will add a style tag and then write all the CSS provided in the question like this:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/047/original/upload_2ea1f412f739128739787f0498ccba03.png?1695312161)
Coming to the point 6 of the question. Let us first see what is pseudo selector.
## Pseudo selector
- When you hover over any element and it changes its behavior, it is known as a Pseudo selector.
- For example, if you go on a sign-in button it becomes popped up.
```HTML=
a.hover{
color : hsla(100%, 15%, 25%, 0.6)
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/048/original/upload_14bb1001ae9af53dda59aa7200cd2c8f.png?1695312197)

View File

@@ -0,0 +1,363 @@
The topics we are going to cover in today's session are:
- Previous lecture recap with left topics
- CSS Fonts
- Box Model
- Display Property in CSS
So, let us start with the session.
So, in the previous lecture, we left the background color in CSS.
- It works the same as the color.
- You can add CSS code for background color as :
```HTML=
h1{
background-color : blue;
}
```
## **Example**:
[IMAGE_1 START SAMPLE]
![](https://hackmd.io/_uploads/ryUXY8npn.png)
[IMAGE_1 FINISH SAMPLE]
Here you will select the class namely "background" and apply CSS to them. It will be reflected to all the **h1s** inside that div tag.
> `color` is used to change the text color but `background-color` is used to change the color area behind that text.
- You can also use an image as the text background. To do this, you need to provide the image URL in the CSS as shown below:
```HTML=
h1{
background-color : url(" url_of_that_specific_image.jpg ");
}
```
You can set the background size as:
```HTML=
h1{
background-color : url(" url_of_that_specific_image.jpg ");
background-size : 400px;
}
```
- In the CSS background property, when it finds any empty space, it starts to repeat its element. You can use the property `background : no-repeat;` to avoid it.
- -You can customize the repetition of the BG image using the X and Y axis.
- You can customize various aspects of the font such as size, look, and many dynamics of it. Let us see them one by one.
- First, create a file as shown below:
[IMAGE_2 START SAMPLE]
![](https://hackmd.io/_uploads/rJ3VYLh62.png)
[IMAGE_2 FINISH SAMPLE]
Then we will apply CSS to the fonts. First, let us see font family.
## Font family
- The different types of font available in the CSS are termed as font family.
- The font-family property can hold several font names as a "**fallback**" system. If the browser does not support the first font, it tries the next font.
- Font family are like "times", "courier", "arial". If you want to use a single font from this, you can just remove the other font styles from the font family.
- Syntax:
```HTML=
.heading_1{
font-family: your_desired_font
}
```
## Font weight
- This font property is used to decide the intensity of the font.
- It's value ranges from lightest **100px** to boldest **800px**.
- Syntax:
```HTML=
font-weight: 800px;
```
## Font size
- It refers to the size of the text.
- Syntax:
```HTML=
font-size: xx-larger;
```
```HTML=
font-size: 50%;
```
- There is also a font-style that you can choose to customize your font like bold, italic, etc.
## Google font
- If you are not unable to get the font requirement from the above font properties, then you can use the Google font option.
- You just need to go to the Google search and search "**google font**". Refer website: `https://fonts.google.com/`.
- Here you can find and choose the font. Click over the font and you will get a lot of options to customize that font.
- Click on "**select font_style**" and select the link there.
- Then paste it on your HTML file head tag as shown below.
[IMAGE_3 START SAMPLE]
![](https://hackmd.io/_uploads/Hyn8KInT3.png)
[IMAGE_3 FINISH SAMPLE]
- Now copy the font family from the Google font website and you can use it in the CSS.
- Since you have imported the Google fonts using API till now. Now you can just copy the font family from Google font and paste it at the font family section in the link of the head tag.
- Font and text can be seen as similar but they are not.
- Font focus on the look and styling but Text is the internal working of your text such as spacing, line width, etc.
- First, create a file of HTML is shown below
[IMAGE_4 START SAMPLE]
![](https://hackmd.io/_uploads/Sy-OFI263.png)
[IMAGE_4 FINISH SAMPLE]
First, let us see the text alignment property
## Text Alignment
- It is used to make the alignment of your text like from the left, from the right, or in the center.
- Syntax:
```HTML=
.heading_1{
text-align : left;
}
```
## Text Decoration
- It is used to make your text attractive using some properties.
- These are oval-line, line-through, underline, etc.
- Syntax:
```HTML=
.heading_1{
text-decoration : oval-line;
}
```
## Word Spacing
- It defines how many spaces are there between any two consecutive words.
- It is defined using the pixel values.
- Syntax:
```HTML=
.heading_1{
text-decoration : oval-line;
word-spacing : 100px;
}
```
## Line Height
- It defines the height between two consecutive lines.
- Syntax:
```HTML=
.heading_1{
text-decoration : oval-line;
word-spacing : 100px;
line-height : 100px;
}
```
These are all text and font properties.
- Anything that we create in HTML and CSS takes the form of a box.
- You can check this by inspecting.
- Go to any website that we have created till now, select an html element paragraph and right click, then click on **inspect**. Here you see the boxing of your content in the right panel.
Always remember to add height and width to your box otherwise, it will not be visible. Make the box as shown below.
[IMAGE_5 START SAMPLE]
![](https://hackmd.io/_uploads/SkQYFU2a2.png)
[IMAGE_5 FINISH SAMPLE]
You can customize the size of the box by changing the height and width values.
> Take a real-life example of creating a township including steps like making land cut, house placing, etc. to discuss the box concept. And introduce `padding`.
## Padding
- It is the empty area inside the container.
- You can apply the padding outside your content but it should be inside your container box.
- Syntax to add padding:
```HTML=
h1{
padding : 20px;
}
```
- **Increasing the padding** area **decreases the container** space. Because padding is only inside that specific container, it can not go outside of the box.
## Margin
- The CSS margin properties are used to create space around two different elements, outside of any defined borders.
- It prevents your elements from getting overlapped.
- Syntax:
- Create another box as in the previous example then,
```HTML=
.box_2{
width: 100px;
height: 120px;
background-color: blue;
}
```
- There are already some margins by CCS default. But you can customize it as :
```HTML=
.box_2{
width: 100px;
height: 120px;
background-color: blue;
margin : 0px;
}
```
> You can use the universal selector (*) to apply the desired CSS property to all the elements.
Syntax:
```
*{
CSS_Property
}
```
## Applying Margins, Padding and Border
- First, let us create three containers:
[IMAGE_6 START SAMPLE]
![](https://hackmd.io/_uploads/BkXsY836h.png)
[IMAGE_6 FINISH SAMPLE]
- The CSS that we apply in the containers will be applied to all the containers because they all have the same name.
- When you apply margin 20px, it gets reflected in the element from all sides.
- If you want to apply margin from a specific side say top, then :
```HTML=
.container{
background-color : lightgreen;
martin-top : 20px;
}
```
- Likewise, you can apply margin for **bottom**, **left** and **right** side too.
Now, let us apply padding.
- If you apply padding to the container, it gets reflected in the **div tag**. You can check by applying padding to the container.
- So, it should be avoided. Rather it is best practice to **apply padding to the elements** by targeting the element separately in the style tag as:
```HTML=
h1{
padding : 20px;
}
```
Coming to margins,
- If can apply four values to the margin property as `margin : 10px 20px 30px 40px;` it will be reflected as `margin : top right bottom left`
- You can try using different margin values to see the different results.
- If you pass three values to the margin property and forget the last value, as `margin: 10px 20px 30px;`, then it will be reflected as "10 px for top and 20 px as left and right both and 40px for bottom".
- If you pass two values to the margin property as `margin : 10px 20px;`, then it will be reflected as "10px for top and bottom, and 20px as left and right.
> All these concepts are similar for **padding**. You just need to use padding in place of margins. And the rest of the rules will be the same.
## Border
- Border is defined as that extra space that you can create around your HTML elements.
- There are many types of borders such as **solid**, **dashed**, **dotted**, etc. that you need to define in the syntax to see it.
- Syntax:
```HTML=
.container{
background-color : lightgreen;
martin-top : 20px;
border : 2px solid red;
}
```
### Border Alignment Properties
- border-top : 5px solid blue ;
- border-left: 2px solid red;
- border-right: 6px dashed green;
- border bottom: 5px solid red;
### Border Radius Properties
- It is used to apply curved edges to the borders
- Syntax:
```HTML=
border-radius : 20px;
```
You can also apply this radius to a specific side of the border for example : `` border-top-right_radius: 20px;``
> These are the all properties and concepts (Margins, Padding, and Borders) that together are termed as "**Box Model** in CSS".
# Question
What is the order of values when passing all four values in the margin?
# Choices
- [ ] top right left bottom
- [ ] right left top bottom
- [ ] top bottom left right
- [x] top right bottom left
> Summarizing
- Everything that we create in an HTML is taken as a Box known as **Box Model**.
- It will have two dimensions that are Height and Width.
- And these boxes have three properties that are **Margin**. **Padding**, and **Border**.
First, let us create a file as shown below to understand the working of the Display property:
[IMAGE_7 START SAMPLE]
![](https://hackmd.io/_uploads/H1pRtL2T3.png)
[IMAGE_7 FINISH SAMPLE]
> Note: Rem is also a unit to define pixels. It converts the pixel into 10 times. For example **5rem** is **50px**.
When you see the output you can see that the border is taking the whole area of paragraphs. But it should be on the paragraph **p1** only.
Now, let us add a span tag to the file as shown below:
[IMAGE_8 START SAMPLE]
![](https://hackmd.io/_uploads/Bk8TjInTh.png)
[IMAGE_8 FINISH SAMPLE]
When you run this file, You can see that these **span tags** are not acting as a paragraph. This means that there are no borders applied to it.
Now, give this span tag border, height, and width property as :
```HTML=
span{
border: 2px solid blue;
height: 50px;
width: 50px;
}
```
Now give the p tag height and width as :
```HTML=
p{
border: 2px solid red;
height: 50px;
width: 50px;
}
```
You can see in the output that there are no changes seen in the span tag.
> Note: Block elements can have customizable height and width but you can not provide height and width to the inline elements (**span tag** in this case).
Here comes the need for **Display property**
- Add a random image to the file using **img src tag**.
- Also add an anchor tag entitled "click me".
- You can see that you can provide customizable height and width to the image. Here, it is working as both inline and block elements.
- CSS allows the user to add all of these tags as a Display property.
- Syntax:
```HTML=
p{
display : inline;
}
```
This makes your element an **inline-block**. That's the work of the Display property in CSS. After that, you can provide height and width to the inline elements too.

View File

@@ -0,0 +1,851 @@
## Agenda
Today we will be discussing about positioning and flexbox in CSS.
We will talk about Flexbox and responsive later, So first let's start with positioning in CSS.
So let's start.
**[Ask the learners]**
What do you understand when we talk about positioning or position?
> **Note to Instructor:** Wait for 5-10 seconds to get some answers and then continue
The concept of positioning in CSS can be understood by arrangement of players on a sports field. Imagine football or cricket field.
**Think of a scenario in football:** you have a center forward positioned at one spot, and you'll find the center midfielder occupying another specific spot. This arrangement of players in different positions is scattered over the field.
Similarly, in CSS, positioning involves placing elements within a web page layout. Just as players are situated precisely on the field, web elements can be located using various CSS positioning techniques.
Now that we know what is positioning in CSS. We will be now discussing types of position in CSS.
So, let's understand two types of position in CSS:
* **Absolute Position**
* **Relative Position**
Think about a table you have in front of you. Imagine you're looking straight down at the table. It's just a flat square surface, like the top of the table. This is the place where you might put things, like your phone or a cup of coffee.
What we're going to talk about is how you can move this "cell phone" around on the table in two different ways.
**[Ask the learners]**
Can anyone tell what are the two ways we can move this cell phone on table.
When we talk about moving the box around, we're talking about changing its position in these two main directions X-Axis and Y-Axis. These two coordinates, X and Y, help us understand exactly where the box (or any object) is located on the table.
Let's say you start with the cell phone placed at a specific spot on the table. Now, you want to move it a bit. In CSS terms, this is done by specifying how many centimeters (like pixels) you want to move the box from its current position.
For example, if you move the box 10 centimeters to the right. So the relative position of the cell phone from its starting point to ending point has changed by 10 cm.
If you are measuring the distance from the starting of the Table. The distance between starting of the table and the cell phone will be the absolute position.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/071/original/upload_189ab829bb663b192a1033cb2dbf5b52.png?1695317443)
So we can define in terms of CSS as:
* **Relative Position:** The element is positioned relative to its previous position.
* **Absolute Position:** The element is positioned absolutely to its parent container.
> **Tip to Instructor:** Ask learners if they have any doubt.
Now, that we know what is Relative and Absolute Positioning. Let's move forward by coding and understanding positions.
Now let's move to VS Code and write some code to understand it better.
> **Tip to instructor:** Make a boilerplate code by creating 4 boxes and use those boxes for explaining position property. While writing the code, make sure to explain each line to the learners.
**HTML Code:**
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning</title>
<style>
.container {
background-color: dodgerblue;
}
.box {
display: inline-block;
height: 150px;
width: 150px;
background-color: tomato;
margin: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Now lets target this 3rd box and apply position property on it.
### Exercise 1
#### Problem Statement
Give **static** position value to box 3.
#### Solution
* we can give a separate id to the box3, say box_3.
* Now, in style tag, we can use position property and set the value as **static.**
#### Pseudocode
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning</title>
<style>
.container {
background-color: dodgerblue;
}
.box {
display: inline-block;
height: 150px;
width: 150px;
background-color: tomato;
margin: 10px;
}
#box_3 {
position: static;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box" id="box_3">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Static is the default position in which all the elements are positioned, so it will do nothing to the position of box3
There are 5 positions in CSS that we will talk about:
* static
* relative
* absolute
* fixed
* sticky
Now lets give it relative position.
### Exercise 2
#### Problem Statement
Give **relative** position value to box 3 and move it 20px.
#### Solution
* In style of box_3, we can use position property and set the value as **relative** along with top property as 20px.
#### Pseudocode
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning</title>
<style>
.container {
background-color: dodgerblue;
}
.box {
display: inline-block;
height: 150px;
width: 150px;
background-color: tomato;
margin: 10px;
}
#box_3 {
position: relative;
top: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box" id="box_3">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Now, it will move 20 pixels from the top. We're instructing the box to move '20 pixels down from your original position at the top'.
> **Tip to instructor:** Use different-different values to explain relative value to learners and ask learners if they have any doubt or not.
Now lets give it absolute position.
### Exercise 3
#### Problem Statement
Give **absolute** position value to box 3 and move it 20px.
#### Solution
* In style of box_3, we can use position property and set the value as **absolute**.
#### Pseudocode
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning</title>
<style>
.container {
background-color: dodgerblue;
}
.box {
display: inline-block;
height: 150px;
width: 150px;
background-color: tomato;
margin: 10px;
}
#box_3 {
position: absolute;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box" id="box_3">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
> **Tip to instructor:** This box 4 vanishing case can be tricky for learners. so take time explaining this.
Now, we can see that box 4 has vanished but "box 4" could be positioned directly underneath "box 3", and because of the overlap, "box 4" might be hidden by "box 3".
If we want "box 4" to be visible and not hidden by "box 3", you might need to adjust the positioning of box 3 and give some top value as 100px.
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning</title>
<style>
.container {
background-color: dodgerblue;
}
.box {
display: inline-block;
height: 150px;
width: 150px;
background-color: tomato;
margin: 10px;
}
#box_3 {
position: absolute;
top: 100px;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box" id="box_3">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Now, we can see it is moving down, so that means box3 is now trying to adjust with the window itself.
In this case, the element box3 is removed from the normal document flow. The other elements will behave as if that box3 is not in the document. No space is created for the element in the page layout. The values of left, top, bottom and right determine the final position of the box3.
Now box3 is not trying to move relative to its original position, it will try to move relative to the window, means from the entire top of the window it is taking 100px.
> **Tip to instructor:** Now use right, left and bottom properties to explain it better.
Now lets understand what fixed position is.
**Fixed position:**
* Fixed Position is basically when your Element will take a place with the Respect to the window and it will not move from there.
* Fixed-positioned element is "fixed" in a specific location on the screen, and it won't move when the user scrolls up or down the page. This can be useful for creating elements that should always be visible, like navigation bars or call-to-action buttons, regardless of where the user is on the page.
* The element will maintain its position relative to the viewport's coordinates, providing a consistent visual reference point as the user interacts with the content.
### Exercise 4
#### Problem Statement
Give **fixed** position value to box 3 and fix it at the bottom of the scrollable page.
#### Solution
* In style of box_3, we can use position property and set the value as **fixed**.
* For fixing it to the bottom of the page, we can give right property as 4px and bottom as 1px.
#### Pseudocode
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning</title>
<style>
.container {
background-color: dodgerblue;
height: 4000px;
}
.box {
display: inline-block;
height: 150px;
width: 150px;
background-color: tomato;
margin: 10px;
}
#box_3 {
position: fixed;
right: 4px;
bottom: 1px;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box" id="box_3">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Now if we scroll the page, the box3 will be fixed at the bottom right of the page.
> **Tip to instructor:** Ask the learners, if they have any doubts
Now, lets understand sticky value:
**Sticky:**
* When an element is given a "position" value of "sticky," it acts like a relative-positioned element within its containing element until a certain scroll threshold is reached. Once the user scrolls beyond that threshold, the element becomes "stuck" in place and behaves like a fixed-positioned element, remaining visible on the screen.
* In other words, a sticky element starts as part of the normal document flow, just like a relatively positioned element. As the user scrolls, the element follows its normal position until it reaches a designated point (usually when its top or bottom edge reaches a specific distance from the viewport's edge). At that point, it becomes "sticky" and remains fixed at that position while the rest of the content scrolls.
Lets go to the zomato website and see its navbar, here you can see when we scroll the page, this navbar is getting fixed at the top of the page.
So on reaching a particular value, sticky gets fixed.
### Exercise 5
#### Problem Statement
Give **sticky** position value to box 3 and fix it at the top of the scrollable page.
#### Solution
* In style of box_3, we can use position property and set the value as **sticky**.
* For fixing it to the top of the page, we can give top property as 0.
#### Pseudocode
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Positioning</title>
<style>
.container {
background-color: dodgerblue;
height: 4000px;
}
.box {
display: inline-block;
height: 150px;
width: 150px;
background-color: tomato;
margin: 10px;
}
#box_3 {
position: sticky;
top: 0;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box" id="box_3">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Now if we scroll the page, the box3 will behave normally will it touches the top and them it becomes fixed to the top of the page.
Now let's talk about Flex box.
**Flex Box:**
* Flex box stands for flexible box
* liberty to align and Justify our Elements with just some line of code but you need to think graphically.
* It's a layout model in CSS that makes arranging elements a whole lot easier.
* With flexbox, you have a container that holds your elements, and it's like your shelf. Inside this container, you can use the power of flexbox to control how your elements behave.
Flexbox is just a concept of CSS that makes your life very easy.
> **Note to instructor:** Now use the below boilercode to start with explaining flex.
Now, lets create some boxes again in HTML to start with flexbox.
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox</title>
<style>
.container {
background-color: tomato;
border: 2px solid black;
height: 500px;
width: auto;
}
.box {
height: 100px;
width: 100px;
background-color: dodgerblue;
margin: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
So this is the basic boxes that we made similar to above.
Now lets use the **display: flex;** property in the parent container
```css
.container {
background-color: tomato;
border: 2px solid black;
height: 500px;
width: auto;
display: flex;
}
```
When you apply the CSS property display: flex; to an element, you're essentially creating a flex container. This container establishes a flex formatting context for its child elements, or "flex items." This means that the child elements within this container will follow the rules and behaviors defined by the flexbox layout model.
Default property of flex box is to arrange all the elements in the row as you can also see here.
You can also use many flex-direction property to arrange the elements in different ways.
> **Note to instructor:** Use different-different values with flex-direction property like row, column, row-reverse, column-reverse etc, to explain it better.
You can also use this important property called **justify-content**.
**justify-content:** Defines how flex items are distributed along the main axis (horizontal for row layout, vertical for column layout).
So if we want to center all the row items, we can use **justify-content: center**
```css
.container {
background-color: tomato;
border: 2px solid black;
height: 500px;
width: auto;
display: flex;
justify-content: center;
}
```
The justify-content: center; property applied to a flex container in CSS aligns its child elements **horizontally** at the center of the container along the main axis.
But there is 1 more property that is used to align items vertically that is **align-items**.
> **Note to instructor:** Explain the axis using the below diagram. diagram refence: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/073/original/upload_1ec9fd4a198aa44de652e4864bd69a53.png?1695317491)
**[Ask the learners]**
If I say, I want my flex direction is row, what will be my main axis?
- X axis
**[Ask the learners]**
And If I say, I want my flex direction is column, what will be my main axis?
- Y axis
Now lets take more examples of **justify-content** property.
> **Note to Instructor:** Explain each of the values with examples.
* **justify-content: flex-start;**
* Flex items are aligned at the beginning of the container (left for a row layout, top for a column layout).
* **justify-content: flex-end;**
* Flex items are aligned at the end of the container (right for a row layout, bottom for a column layout).
* **justify-content: center;**
* Flex items are centered along the container's main axis.
* Equal space is added before the first item and after the last item, creating a balanced appearance.
* **justify-content: space-between;**
* Flex items are evenly spaced along the main axis.
* The first item aligns with the container's start, the last item aligns with the container's end, and equal space is added between the items.
* **justify-content: space-around;**
* Flex items are evenly spaced along the main axis, with space distributed around them.
* Space is added before the first item, after the last item, and between each pair of items.
* **justify-content: space-evenly;**
* Flex items are evenly spaced along the main axis, with equal space added between them.
* Equal space is added before the first item, between all items, and after the last item.
There are many options for this property, you can choose to play around with all of them.
Now lets understand **align-items** property.
The **align-items property** lets you control how flex items are positioned on the cross axis within the container.
> **Note to Instructor:** Explain each of the values of align-items with examples.
* **align-items: flex-start;**
* Flex items align at the start of the cross axis (top for row layout, left for column layout).
* No additional space is added between items and the container's cross axis edge.
* **align-items: flex-end;**
* Flex items align at the end of the cross axis (bottom for row layout, right for column layout).
* No additional space is added between items and the container's cross axis edge.
* **align-items: center;**
* Flex items are vertically centered along the cross axis.
* Equal space is added above and below the items, creating a balanced appearance.
* **align-items: baseline;**
* Flex items are aligned along their text baselines.
* This value can be useful when items have varying font sizes or text content.
* **align-items: stretch;**
* Flex items are stretched to fill the container's cross axis.
* If no height is explicitly set on the items, they will take up the full height of the container.
**[Ask the learners]**
If flex-direction if set to row and justify-content is center, which direction it will align?
- Horizontally
Now, we have covered two important properties, justify-content and align-items. Now we will move formward with some interesting properties.
**[Ask the learners]**
Do you know what is a responsive website?
- A responsive website is a website that can work of different screen size and can adapt to those different screen sizes. It responds and adjusts its layout, images, and content to fit the screen it's being viewed on, whether that's a desktop computer, tablet, or smartphone.
Lets take an example to scaler's website
> **Note to Instructor:** Navigate to https://www.scaler.com/ and show its responsiveness
If you minimize the screen, you can see that everything is changing according to the screen size. Design is not breaking and is adapting to the screen size whether it is mobile view or the desktop view.
> **Note to Instructor:** Now to explain screensize better, you can navigate to our HTML page and show different mobile and responsive views in inspect tab.
In inspect tab of our HTML website, we can check how our website will look in different dimensions and screen sizes.
If we decrease the screen size, we can see that the boxes are shrinking. That means it is not responsive, There are various properties in flex box that we can use to make our website responsive.
In the current scenario the boxes are getting shrinked but if I don't want it to shrink and I want them to adjust according to the screen size, for that we can use some CSS properties.
There is 1 property know as **flex-wrap**
**flex-wrap:** The flex-wrap property in flexbox controls whether flex items should wrap onto multiple lines within the flex container when there's not enough space to fit them all on a single line.
So if we use the above code with flex-wrap property, we can see that the boxes are now responsive and not shrinking.
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox</title>
<style>
.container {
background-color: tomato;
border: 2px solid black;
height: 500px;
width: auto;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
.box {
height: 200px;
width: 200px;
background-color: dodgerblue;
margin: 10px;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
> **Note to Instructor:** Now create more boxes to expain **flex-wrap** property better.
The flex-wrap property is especially useful when dealing with responsive layouts. It allows you to control how flex items reorganize when the available space changes. For example, if you have a row of cards and they don't fit on a smaller screen, setting flex-wrap: wrap; would cause them to stack vertically, creating a more readable layout.
Here's what each value of flex-wrap does:
* flex-wrap: nowrap;
* This is the default value.
* Flex items will stay on a single line, even if they cause overflow. They won't wrap to the next line.
* flex-wrap: wrap;
* When there's not enough space for all items on a single line, flex items will wrap to the next line. They will stack vertically if needed.
* flex-wrap: wrap-reverse;
* Similar to wrap, but the wrapping happens in reverse order. The last flex item becomes the first item on the new line, and so on.
Now we can discuss about item wise flex box,For now we were using flex box in container but now we will use flex box within the container items
> **Note to Instructor:** Navigate to https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-properties-for-the-childrenflex-items and explain that we can use flex properties for children items.
There is 1 property called **order** that is used for ordering the items.
The **order** property in flexbox allows you to control the visual order in which flex items appear within a flex container, regardless of their order in the HTML markup. It's particularly useful for reordering items for different screen sizes or creating unique visual layouts. H
lets give the order property to box1, and see what happens:
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox</title>
<style>
.container {
background-color: tomato;
border: 2px solid black;
height: 500px;
width: auto;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
.box {
height: 200px;
width: 200px;
background-color: dodgerblue;
margin: 10px;
}
.box1{
order: 5;
}
</style>
</head>
<body>
<div class="container">
<div class="box box1">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Now you can see that the box1 is ordered at the last.
let's understand this with an example:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/074/original/upload_738cab0d87a85a216d67986e5105b3e4.png?1695317536)
Suppose, we gave order: 4 to box1 and order: 2 to box3.
> **Note to Instructor:** So the question is how will the boxes arrange themselves?
* box1 has the order 4, so it will go at last.
* box3 has order 2, so it will go at third.
* box 2 will be at 2nd position
* box1 will be at first.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/075/original/upload_e5323381ac5d2c537cd4459693a88763.png?1695317559)
Now lets' understand flex-shrink property.
The **flex-shrink** property in flexbox determines how flex items shrink when the container's available space is limited. It defines the ability of an item to shrink in relation to other items in the container when the container's size is reduced.
let's understand this with an example:
We are providing flex-shrink property as 2 to box3.
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flexbox</title>
<style>
.container {
background-color: tomato;
border: 2px solid black;
height: 500px;
width: auto;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.box {
height: 200px;
width: 200px;
background-color: dodgerblue;
margin: 10px;
}
.box3{
flex-shrink: 2;
}
</style>
</head>
<body>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box box3">3</div>
<div class="box">4</div>
</div>
</body>
</html>
```
Now, whenever we are downsizing the screen box will be the first one to shrink the most.
Similarly there is a property called flex-grow
**flex-grow:**
* This defines the ability for a flex item to grow if necessary. It accepts a unitless value that serves as a proportion. It dictates what amount of the available space inside the flex container the item should take up.
> **Note to Instructor:** Show the similar example as flex-shrink.
lets see a basic example:
```htmlembedded
.box3{
flex-shrink: 2;
}
```
Now the box3 will grow the most on increasing the screen size
> **Note to Instructor:** Now ask for doubts to the learners about the doubt that came up during the class.

View File

@@ -0,0 +1,824 @@
## Agenda
Today we will be discussing about Responsive Layout and Grid in CSS.
In previous session, we learned about flexbox and media queries. you can practice more about Flexbox via https://flexboxfroggy.com/.
So let's start with today's session.
Sometimes, when you have to create a very complex layout, you can use a Grid.
**[Ask the learners]**
What is a Grid?
Let's take an example to understand this better, when we were kids, there were notebooks and the pages were of below structure.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/076/original/upload_ea53ca4c4a28ffe4b4386302f64cd526.png?1695317786)
This is basically a grid structure.
**CSS Grid Layout** is a layout in CSS that allows you to create two-dimensional grid layouts for arranging and aligning elements on a web page. It provides a powerful way to structure and design both rows and columns of content, allowing for complex and flexible layouts.
In CSS Grid, we can use more properties as compared to Flexbox. So, lets jump on to VS Code to understand CSS Grid better.
> **Tip to instructor:** Make a boilerplate code by creating 9 items and use those items for explaining CSS Grid. While writing the code, make sure to explain each line to the learners.
Let's create a basic layout of 9 items which we will use to understand Grid.
**HTML Code:**
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Grid</title>
<style>
*{
padding: 0;
margin: 0;
}
body{
font-size: 20px;
line-height: 1.5;
color: dodgerblue;
background-color: tomato;
}
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
}
.item{
background-color: dodgerblue;
color: white;
font-size: 20px;
padding: 20px;
border: 1px solid lightblue;
}
</style>
</head>
<body>
<div class="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
<div class="item">Item 8</div>
<div class="item">Item 9</div>
</div>
</body>
</html>
```
**[Ask the learners]**
Suppose we want to arrange these items in a row order, so what properties can I use?
**display: flex;**. In last lecture we learn't about flex, so giving display as flex will make the items align in row manner.
**[Ask the learners]**
Now, what happens if I give display property as Grid?
Nothing will change, they will look the same, but if you go to inspect, you can see a Grid keyword to the container.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/077/original/upload_bf8d96c8c569d50f6f73d4f21f594191.png?1695317817)
It tells that this container is assigned to a grid value, so we are allowed to use any grid property available with the CSS.
### grid-template-columns
So lets move to the first property called **grid-template-columns** and give some value to this as grid-template-columns
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 100px 100px 100px;
}
```
Here grid will be with three columns, each with a width of 100px. This means that the entire grid will be 300px wide, and each of the nine grid items will occupy a cell that is 100px wide having 3 rows of the grid.
Similarly if we remove 1 column of 100px (grid-template-columns: 100px 100px) from the above property, it will create 2 columns of 100px each.
But, there is a catch, suppose I gave border to the container, Border will be applied to the entire container.
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 100px 100px 100px;
border: 2px solid black;
}
```
So here 300px will be taken by the items but the rest 900px will be completely empty. So we are not making any good use of the space. To fix this Grid provides Frames to handle this.
So I give the units like this:
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
border: 2px solid black;
}
```
It will take the entire space and equally divide 3 items in a row. The grid-template-columns: 1fr 1fr 1fr; declaration means that the available space in the container's width will be divided into three equal parts, each represented by 1fr. This is a flexible way to distribute space evenly among the columns.
Let's understand it that way:
* suppose the container is of length 120px.
* grid-template-columns: 1fr 1fr 1fr;
* Total frames will be 1+1+1 = 3.
* size of each frame will be 120/3 = 40px.
Hence in this case, each column of the three columns will be equal to 40px.
Now lets give the units like this:
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 2fr 1fr;
border: 2px solid black;
}
```
Now with grid-template-columns: 1fr 2fr 1fr;, you are distributing the available space into three columns with different proportions. The first column will take up 1fr that is 300px of the available space, the second column will take up 2fr that is 600px, and the third column will take up 1fr or 300px.
> **Tip to instructor:** Now use inspect to show learners the dimensions of the items created.
### column-gap
Now if we want to give some gap between the columns, we can use **column-gap** property
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 2fr 1fr;
column-gap: 20px;
}
```
The space between the columns will now be 20px, creating a gap between each column. This space will be added to the defined widths of the columns.
### row-gap
Similarly if we want to give some gap between the rows, we can use **row-gap** property
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 2fr 1fr;
column-gap: 20px;
row-gap: 20px;
}
```
The space between the rows and columns will now be 20px. This space will be added to the defined widths of the rows.
## gap
If you want to give gap to both rows and columns at once. you can give gap property only.
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 20px;
}
```
The result will be the same when we used both row-gap and column-gap property.
Let's now see row-level property of grid.
### grid-template-rows
Lets give some value to this as grid-template-rows.
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 2fr 1fr;
grid-template-rows: 1fr 2fr 1fr;
}
```
The **grid-template-rows: 1fr 2fr 1fr;** declaration defines the height proportions of the grid's rows. The first row will take up 1fr, the second row will take up 2fr, and the third row will take up 1fr.
Similar to how fr units distribute space among columns, here the vertical space is distributed among rows. The second row will be twice as tall as the first and third rows.
### grid-template-columns with repeat()
Suppose you want to divide each column in equal frames then we can use **repeat()** method.
Suppose you want to create a grid with 4 columns, each of 100px width. Instead of writing out 100px 100px 100px 100px, you can use the repeat() function:
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 1fr 2fr 1fr;
}
```
This method can also be applied to row level grid.
### grid-auto-rows
grid-auto-rows is a CSS property used in CSS Grid layouts to define the size of rows that are created implicitly (automatically) when the content doesn't fit into the explicitly defined rows. This property allows you to control the height of these automatically generated rows.
Suppose we give its value:
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-auto-rows: 200px
}
```
In this example, all the rows will have a fixed height of 200px. This is helpful while we want out grid to not dynamically grow according to the content.
We can also use all the flex properties with Grid like align-items, justify-content etc.
If I use align-items: end;
```css
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
align-items: end;
}
```
All the columns will be pushed to the end of the container.
Now we will be working on item level.
Suppose we want to get a particular item without making its another class or id. then we can use **.item:nth-of-type()** pseudo-selector.
Here suppose we want to change the styles particularly for 2nd items from out list, we can use it like:
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Grid</title>
<style>
*{
padding: 0;
margin: 0;
}
body{
font-size: 20px;
line-height: 1.5;
color: dodgerblue;
background-color: tomato;
}
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 200px 200px 200px;
}
.item{
background-color: dodgerblue;
color: white;
font-size: 20px;
padding: 20px;
border: 1px solid rgb(114, 118, 119);
}
.item:nth-of-type(2){
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div class="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
<div class="item">Item 8</div>
<div class="item">Item 9</div>
</div>
</body>
</html>
```
We have selected item 2 and gave its height and width as 100px.
Now, we can give some properties to it:
```css
.item:nth-of-type(2){
width: 100px;
height: 100px;
align-self: center;
justify-self: center;
}
```
These properties are used to control how the element aligns within its grid cell both vertically and horizontally. align-self: center; centers the element vertically, and justify-self: center; centers it horizontally within the grid cell.
These were the flex properties, all the flex properties are available for CSS grids also.
Keep in mind that these styles will only be applied to the second .item element due to the :nth-of-type(2) selector. The other .item elements will not be affected by these specific styles unless they are targeted by other rules.
**[Ask the learners]**
Now, let's take 1 more example, where you want item 1 to take space of item 4 vertically. So how we can do this?
We can give properties like:
```css
.item:nth-of-type(1){
background: black;
grid-row: 1/3;
}
```
This will create a lot of confusion that 1/3 row is how that much. So for that we will open inspect and see breakpoints.
You can see every element has breakpoint, so we are saying that 1st element should row-wise take the 1st to 3rd breakpoint.
grid-row: 1/3; sets the vertical span (row range) that the targeted .item element should occupy. In this case, the .item will span from row line 1 to row line 3.
If you make it grid-column: 1/3; it will horizontally take the space from 1st breakpoint to starting of 3rd breakpoint.
**[Ask the learners]**
Any questions in this topic?
There is 1 easier way to do this, you can give grid-column: span 2; and it will do the same thing.
```css
.item:nth-of-type(1){
background: black;
grid-column: span 2;
}
```
The grid-column: span 2; property on the .item class indicates that each .item element will span two columns horizontally.
> **Tip to instructor:** Now explain this property by using different different property values.
Okay, so now we will move ahead with media queries in CSS.
Media queries is used to make your webpage responsive. Media queries allows you to apply different styles based on the characteristics of the or screen, such as its width, height, orientation, resolution, and more. Media queries enable you to create responsive designs that adapt to various screen sizes and devices.
Lets just check the responsiveness of our basic code:
```css
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Grid</title>
<style>
*{
padding: 0;
margin: 0;
}
body{
font-size: 20px;
line-height: 1.5;
color: dodgerblue;
background-color: tomato;
}
.container{
width: 1200px;
margin: 100px auto;
padding: 10px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
border: 2px solid black;
}
.item{
background-color: dodgerblue;
color: white;
font-size: 20px;
padding: 20px;
border: 1px solid lightblue;
}
</style>
</head>
<body>
<div class="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<div class="item">Item 4</div>
<div class="item">Item 5</div>
<div class="item">Item 6</div>
<div class="item">Item 7</div>
<div class="item">Item 8</div>
<div class="item">Item 9</div>
</div>
</body>
</html>
```
Lets try to downsize the screen and move the screen. You can see that this webpage is not responsive and when we downsize it part of grid items is vanishing out of the window.
So, to make it responsive we will be using media queries.
Similarly, we can check this window using inspect responsive feature. Its not responsive there also, so we have to make it responsive.4
Suppose, we are downsizing the screen and want to it make responsive as soon as screen width reaches 500px. We can use the syntax as:
```css
@media (max-width: 500px){
.container{
grid-template-columns: 1fr;
}
}
```
Now lets see what happens as we downsize the window.
* When the screen width is 500px or narrower, the styles inside the media query will be applied.
* The .container element's grid layout will be set to a single column using grid-template-columns: 1fr;.
* This means that on screens narrower than 500px, the grid will collapse into a single column layout. Each grid item will take up the full width of the container, allowing for easy vertical scrolling on smaller screens.
When screen size is reaching 500px, we are telling the grid to make only 1 column and take the whole frame size for each item.
So media queries is applied this this, now we will cover 1 more topic from Grid that is grid areas.
For understanding the Grid Areas, we have to make the layout of the screen in this exercise:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/079/original/upload_0cd7eb9974551b72015c27bed98c5236.png?1695317926)
**[Ask the learners]**
Anyone has any idea how to approach this?
Topdown approach is correct one, we can use this to make our webpage, so lets start making it.
Lets first define all the components in our HTML page, header, navigation, sidebar, footer.
For all these components we can create semantic tags for all.
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid Layout</title>
</head>
<style>
*{
margin: 0;
padding: 0;
}
body{
font-size: 20px;
height: 100vh;
}
</style>
<body>
<header>Header</header>
<main>Content</main>
<nav>Navigation Bar</nav>
<aside>SideBar</aside>
<footer>footer</footer>
</body>
</html>
```
So, now we have created a basic layout of the application.
for initializing grid in our application, we can use display: grid; property.
```htmlembedded
body{
font-size: 20px;
height: 100vh;
display: grid;
}
```
now lets give some borders and colors to all the elements.
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid Layout</title>
</head>
<style>
*{
margin: 0;
padding: 0;
}
body{
font-size: 20px;
height: 100vh;
display: grid;
}
header, main, nav, aside, footer{
background-color: tomato;
color: white;
padding: 20px;
border: skyblue 1px solid;
}
</style>
<body>
<header>Header</header>
<main>Content</main>
<nav>Navigation Bar</nav>
<aside>SideBar</aside>
<footer>footer</footer>
</body>
</html>
```
To convert this layout to our desired layout, we can use 1 css property called grid-template-areas, where we can tell CSS what area to give to a specific section.
we can use grid-area to call that specific area and provide that area to our elements
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid Layout</title>
</head>
<style>
*{
margin: 0;
padding: 0;
}
body{
font-size: 20px;
height: 100vh;
display: grid;
grid-template-areas:
'top top top'
'nav content sidebar'
'nav footer footer';
}
header, main, nav, aside, footer{
background-color: tomato;
color: white;
padding: 20px;
border: skyblue 1px solid;
}
header{
grid-area: top;
}
nav{
grid-area: nav;
}
main{
grid-area: content;
}
aside{
grid-area: sidebar;
}
footer{
grid-area: footer;
}
</style>
<body>
<header>Header</header>
<main>Content</main>
<nav>Navigation Bar</nav>
<aside>SideBar</aside>
<footer>footer</footer>
</body>
</html>
```
The CSS styles define the grid layout using grid-template-areas, specifying where each section should appear in the grid. The classes header, nav, main, aside, and footer are assigned to their respective grid areas using the grid-area property.
Now using this code we have reached the basic layout requirements for us. But we have to play with heights and widths of the elements to make it exactly like the desired one.
We first need to add 1 more sidebar for our requirements.
**[Ask the learners]**
You can take some time and tell me how to do it?
Ans,
* we need to add 4 templates in grid-template-areas property,
* we have to make 1 more element for sidebar and give it the same styling
* then we can add new template name to grid-template-areas and create new element style with respect to our template.
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid Layout</title>
</head>
<style>
*{
margin: 0;
padding: 0;
}
body{
font-size: 20px;
height: 100vh;
display: grid;
grid-template-areas:
'top top top top'
'nav content sidebar sidebar2'
'nav footer footer footer';
}
header, main, nav, aside, footer, div{
background-color: tomato;
color: white;
padding: 20px;
border: skyblue 1px solid;
}
header{
grid-area: top;
}
nav{
grid-area: nav;
}
main{
grid-area: content;
}
aside{
grid-area: sidebar;
}
footer{
grid-area: footer;
}
div{
grid-area: sidebar2;
}
</style>
<body>
<header>Header</header>
<main>Content</main>
<nav>Navigation Bar</nav>
<aside>SideBar</aside>
<div>Sidebar 2</div>
<footer>footer</footer>
</body>
</html>
```
Now only height and width of the templates is left, so we can use grid-template-columns and grid-template-rows properties:
```htmlembedded
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Grid Layout</title>
</head>
<style>
*{
margin: 0;
padding: 0;
}
body{
font-size: 20px;
height: 100vh;
display: grid;
grid-template-areas:
'top top top top'
'nav content sidebar sidebar2'
'nav footer footer footer';
grid-template-columns: 1fr 4fr 1fr 1fr;
grid-template-rows: 80px 1fr 70px;
}
header, main, nav, aside, footer, div{
background-color: tomato;
color: white;
padding: 20px;
border: skyblue 1px solid;
}
header{
grid-area: top;
}
nav{
grid-area: nav;
}
main{
grid-area: content;
}
aside{
grid-area: sidebar;
}
footer{
grid-area: footer;
}
div{
grid-area: sidebar2;
}
</style>
<body>
<header>Header</header>
<main>Content</main>
<nav>Navigation Bar</nav>
<aside>SideBar</aside>
<div>Sidebar 2</div>
<footer>footer</footer>
</body>
</html>
```
* **grid-template-columns:** 1fr 4fr 1fr 1fr; defines the widths of the columns in the grid. The first column takes up 1 fraction of the available space, the second column takes up 4 fractions, and the last two columns take up 1 fraction each.
* **grid-template-rows:** 80px 1fr 70px; defines the heights of the rows in the grid. The first row has a fixed height of 80px, the second row takes up 1 fraction of the available space, and the third row has a fixed height of 70px.
In this, we have achieved the layout that we wanted to achieve

View File

@@ -0,0 +1,413 @@
# CSS Cascading, Specificity, Inheritance and Overflow
### Introduction
Before starting with the topic, let us understand the simple definition of *Priority and Specific*. These two words create a concept in CSS that is known as Specificity.
**Priority:** Suppose three tasks are to be done. All the tasks can be arranged based on the task that has to be done first. The task with the highest priority will be on the top.
**Specific:** Suppose you are diagnosed with fever then the doctor prescribes you with some medicines that are *particular* to the disease i.e., fever.
### What is Cascading in CSS?
Cascade is defined as top-to-bottom flow.
*Example:*
```css!
.h1{
colour: red;
}
.h1{
colour: blue;
}
```
The output will be blue because cascading always goes down therefore blue comes later therefore priority will be given to the later part.
**CSS Cascading involves Specificity and Inheritance which will be covered in this lecture.**
### Specificity
To understand specificity let's take an example.
**First**: Create an HTML file to understand specificity.
**Second**: Create an unordered list of let's say "fruits".
```htmlembedded!
<body>
<ul>
<li>Apple</li>
<li>Mango</li>
<li>Orange</li>
</ul>
</body>
```
**Third**: Assign 'id' and 'class' to your unordered list.
```htmlembedded!
<body>
<ul id = "fruits">
<li>Apple</li>
<li class ="favourite">Mango</li>
<li>Orange</li>
</ul>
</body>
```
**Fourth**: Using CSS, make the colour of the class "favourite" blue.
The Selector will be id i.e., "fruits" Inside this id select a list element and then a specific class i.e., "favourite".
```htmlembedded!
<style>
ul#fruits li.favourite{
color: blue;
}
</style>
```
**Fifth**: Using CSS, change the colour of the unordered list to blue as well.
```htmlembedded!
<style>
ul#fruits li.favourite{
color: blue;
}
ul#fruits li{
color: blue;
}
</style>
```
**[Ask the learners]**
What will happen if we change the colour of the unordered list to blue after changing the colour of class "favourite" to red?
```htmlembedded!
<style>
ul#fruits li.favourite{
color: red;
}
ul#fruits li{
color: blue;
}
</style>
```
--> The result will be that **"Mango" will be of red** colour and the rest list elements will be blue because it is using the **Specificity** property.
Hover over the selector `ul#fruits li` you will see "Selector Specificity: (1,0,2)".
Before understanding the Selector Specificity values:
**[Ask the learners]**
What if the style attribute is applied in the list tag with class = "favourite"?
```htmlembedded!
<body>
<ul id = "fruits">
<li>Apple</li>
<li class ="favourite" style= "color: yellow;">Mango</li>
<li>Orange</li>
</ul>
</body>
```
--> The result will be that **the value of Mango will turn to "yellow"** and the rest of the list elements will remain blue because the style attribute does not get affected by the style tag.
### Understanding values in specificity
Specificity can have **four** values if the style attribute is also included.
| Style attribute | IDs | Classes | Elements |
| :--------: | :--------: | :--------: | :---------: |
#### How to count specificity?
The selector `ul#fruits li` has "Selector Specificity: (1,0,2)". But how?
There is 1 ID i.e., fruits. There is no class added in the selector. There are 2 elements in the selector: ul and li. Therefore the value will be (1,0,2).
Similarly, the value of the selector `ul#fruits li.favourite` is (1,1,2).
#### How does the Selector Specificity make sure what selector should be applied?
> Remove style attribute from the list element with class = "favourite".
By comparing both the Selector Specificity values box by box: we see the value of the selector `ul#fruits li.favourite` is (1,1,2) which means it has 1 class while the other selector has no class. Therefore, the `ul#fruits li.favourite` selector will be applied.
**[Ask the learners]**
Arrange them from the least effective to the most effective selector.
1. `.test`
2. `h1.test`
3. `#try`
4. `h1`
--> **4<1<2<3** is the order.
Let's compare the values of each one of them.
1. `.test` - It has one class. Therefore, the value is (0,1,0).
2. `h1.test` - It has one class and one element. Therefore, the value is (0,1,1).
3. `#test` - It has one ID. Therefore, the value is (1,0,0).
4. `h1` - It has one element. Therefore, the value is (0,0,1).
**[Ask the learners]**
Calculate the value of the Selector Specificity of the following selector.
`#try ul div.test h2{}`
--> **(0,1,1,3)**
ID - #try
Class - .test
Element - ul, div, h2
| Style attribute | ID| Class|Element|
| :--------: | :--------: | :--------: | :-------: |
| 0| 1 | 1 |3|
**[Ask the learners]**
Calculate the value of the Selector Specificity of the following selector.
`#try span img .test .main header`
--> **(0,1,2,3)**
ID - #try
Class - .test, .main
Element - span, img, header
| Style attribute | ID| Class|Element|
| :--------: | :--------: | :--------: | :-------: |
| 0| 1 | 2 |3|
> You can use [Keegan](https://https://specificity.keegan.st/) to calculate Specificity value.
### Keyword - `!important`
If the keyword !important is used then it follows the cascading rule regardless of the Specificity Value. Use this keyword only once in a selector. If it is used twice for the same selector then the Specificity rule will be followed.
#### Example
```css!
ul#fruits li.favourite{
color: red;
}
ul#fruits li{
color: blue !important;
}
```
All the list elements will turn into a blue colour regardless of the Specificity Value.
> Priority of Inline CSS will be more than Internal CSS and External CSS.
> The priorities of the External CSS file and Internal CSS file can be changed by following the cascading rule. That means, whatever comes later will be followed.
### CSS Inheritance
#### Definition
As the last names are inherited in a family same way inheritance works in CSS which inherits some property from the parent.
We will be looking at the four properties - **Default, Inherit, Initial and Unset**.
Let's take an example where we create an unordered list and add href to all the list elements:
```htmlembedded!
<ul>
<li>Default <a href="">Link</a> Color </li>
<li>Inherit <a href="">Link</a> Color </li>
<li>Initial <a href="">Link</a> Color </li>
<li>Unset <a href="">Link</a> Color </li>
</ul>
```
First, Let's assign classes to the three properties: Inherit, Initial and Unset.
```htmlembedded!
<ul>
<li>Default <a href="">Link</a> Color </li>
<li class = "class-1">Inherit <a href="">Link</a> Color </li>
<li class = "class-2">Initial <a href="">Link</a> Color </li>
<li class = "class-3">Unset <a href="">Link</a> Color </li>
</ul>
```
### Inherit Property
Inherit is defined as the properties inherited from the parent. The properties applied to the parent are inherited by the child.
For example
```htmlembedded!
<style>
div{
color: blue;
}
</style>
<body>
<div>
<h2>
Heading
</h2>
</div>
</body>
```
The result will be that the heading tag will inherit the colour that is applied to the div element as it is the parent of the h2 tag.
If the parent is not specified then the parent will always be the body tag by default.
To understand it better, let's take "class-1" from our unordered list of inheritance properties and apply `color: inherit` to the anchor tag.
```css!
body{
color: red;
}
.class-1 a{
color: inherit;
}
```
The term inherit means that whatever the colour of the parent tag is **(the default colour of CSS is black in most browsers)**, will be applied to the anchor tag of class: class-1 as well.
### Initial Property
The initial property will always take the default properties.
```css!
body{
color: red;
}
.class-1 a{
color: inherit;
}
.class-2 a{
color: initial;
}
```
The color of the class-2 anchor tag will take the default color i.e., black.
> There exists properties that cannot be inherited like display, and columns etc., You can check [w3schools](https://https://www.w3schools.com/cssref/css3_pr_columns.php) for more details on the properties.
### Unset property
Unset depends on the inherited value.
Unset checks if any properties in the body can be inherited will be inherited.
For example:
```css
body{
color: red;
display: inline;
}
.class-1 a{
color: inherit;
}
.class-2 a{
color: initial;
}
.class-3 a{
color: unset;
}
```
### CSS Overflow
When the content goes out of the container is known as **Overflow**.
Let's take an example where we create a div element and add content of 100 words to it. We create a container and give it a static width and height.
```htmlembedded!
<style>
.content-box{
border: 2px solid tomato;
width: 200px;
height: 200px;
}
</style>
<body>
<div class = "content-box">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a.
</div>
</body>
```
The result will be that the box will not contain the whole content and it will create an overflow condition. Therefore, we will use overflow to overcome it. We can use auto, hidden, scroll and visible overflow properties.
#### Example
Let's understand how:
```htmlembedded!
<style>
.content-box{
border: 2px solid tomato;
width: 200px;
height: 200px;
overflow: hidden; /* the content going outside of the container will be hidden */
}
</style>
<body>
<div class = "content-box">
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a.
</div>
</body>
```
#### Properties of Overflow
**Hidden**: The content that remains outside the container will be hidden or not visible. It does not occupy space where the content is hidden.
**Scroll**: A scroll bar is added inside the container so that you can go through the whole content inside the container.
**Auto**: It works similarly to scroll the only difference is that scroll will always show scroll inside the container while auto will only show when the content is overflowing.
**Visible**: It will make the content visible. There will be no change.
> The container takes only the space designated to it. Therefore, the overflow content will overlap with the other content added after the container.
**[Ask the learners]**
How will we apply the overflow condition if we make the container horizontal for the following div content?
```htmlembedded!
<div class = "horizontal-content">
Scaler
</div>
```
-->
```htmlembedded!
<style>
.horizontal-content{
font-size: 30px;
width: 50px;
height: 30px;
border: 2px solid black;
overflow: hidden;
}
</style>
```
We can add any other property as well it will work similarly to the vertical container.
> If you want to hide the vertical or horizontal scroll bar you can use the following syntax: `overflow-x: hidden;` or `overflow-y: hidden;`.

View File

@@ -0,0 +1,223 @@
## SetTimeout Method
`SetTimeout` is a method that allows you to execute a specific function after a certain delay.
**Syntax:**
```javascript
SetTimeoutCfn(function, delay);
```
- `function`: The function you want to execute after the delay.
- `delay`: The time interval (in milliseconds) after which the specified function should be executed.
For example, if you want to start an animation after a delay of 1, 2, or 3 seconds, you could use the following code:
```javascript
function startAnimation() {
// Code to start the animation
}
// Using SetTimeout to call the startAnimation function after a delay
SetTimeoutCfn(startAnimation, 1000); // 1000 milliseconds (1 second)
SetTimeoutCfn(startAnimation, 2000); // 2000 milliseconds (2 seconds)
SetTimeoutCfn(startAnimation, 3000); // 3000 milliseconds (3 seconds)
```
```javascript
// Declare a variable to hold the timeout ID
let animationTimeout;
// Function to start the animation
function startAnimation() {
// Code to start the animation
console.log("Animation started");
}
// Function to stop the animation
function stopAnimation() {
// Clear the timeout to prevent the animation from starting
clearTimeout(animationTimeout);
console.log("Animation stopped");
}
// Using SetTimeout to call the startAnimation function after a delay
animationTimeout = setTimeout(startAnimation, 2000); // Starts after 2000 milliseconds (2 seconds)
// You can call stopAnimation to cancel the scheduled animation
// For example, if you want to stop it before it starts
stopAnimation();
```
In this example, we've introduced a variable `animationTimeout` to store the ID returned by the `setTimeout` function. This ID represents the scheduled timeout. When you want to stop the animation, you call `clearTimeout(animationTimeout)`, which cancels the scheduled animation and prevents it from starting.
Remember that using the `clearTimeout` function only has an effect if the specified timeout has not yet occurred. If the timeout has already triggered and the animation has started, clearing the timeout won't have any impact on the ongoing animation.
## Debouncing
**Debouncing** is a technique used in web development to control the rate at which a function is executed, particularly in response to frequent events like scrolling, resizing, or typing. It ensures that a function is only executed after a certain period of inactivity, effectively reducing the number of times the function is called and improving performance.
The basic idea behind debouncing is to postpone the execution of a function until a certain amount of time has passed since the last event. This is particularly useful when you want to avoid triggering a function multiple times rapidly, which can lead to unnecessary computations or actions.
Here's a simple example of debouncing using JavaScript:
```javascript
// Debounce function: accepts a function and a delay
function debounce(func, delay) {
let timeoutId;
// Return a function that will be executed after the delay
return function() {
// Clear the previous timeout
clearTimeout(timeoutId);
// Set a new timeout
timeoutId = setTimeout(func, delay);
};
}
// Function to be debounced
function processInput() {
console.log("Processing input...");
}
// Create a debounced version of the processInput function
const debouncedProcessInput = debounce(processInput, 300);
// Simulate frequent user input (e.g., typing)
document.addEventListener("input", debouncedProcessInput);
```
In this example, the `debounce` function takes two arguments: the function you want to debounce (`func`) and the delay period (`delay`). It returns a new function that, when invoked, will clear any existing timeout and set a new one to execute the original function after the specified delay.
The debounced version of the `processInput` function (`debouncedProcessInput`) is then used as an event listener for the "input" event. This means that the `processInput` function will only be executed after the user has stopped typing for 300 milliseconds. If the user continues typing, the timeout will be reset, ensuring that the function is only executed once there's a pause in typing.
This technique is useful in scenarios where you want to avoid overwhelming the system with frequent function calls, especially for tasks like live search suggestions, updating search results, or handling user interactions.
Certainly! Let's walk through the theoretical explanation of the debouncing code using the provided IDs and time intervals: `id=1` with `1000ms`, `id=2` with `2000ms`, `id=3` with `2500ms`, and `id=4` with `3000ms`. We'll use these values to simulate events and understand how the debouncing mechanism works.
**Scenario and Explanation:**
Imagine that we have a user interface with a search bar. The user is typing in the search bar to perform a search, and we want to send a network request to fetch search results. However, we don't want to send a network request for every keystroke; instead, we want to wait until the user has finished typing or paused before sending the request.
Here's how the events play out:
1. `id=1`, 1000ms: The user starts typing. This event triggers the `debouncedSendNetworkRequest` function, which is set to execute after a delay of 1000ms. Since the user hasn't finished typing yet, the timer is reset to 1000ms.
2. `id=2`, 2000ms: The user continues typing. Another event occurs while the timer is still counting down. The timer is reset to 2000ms.
3. `id=3`, 2500ms: The user is still typing, but this time there's less than 2500ms left on the timer. The timer is reset to 2500ms.
4. `id=4`, 3000ms: The user has stopped typing, and there's no further event triggering. The timer reaches 0 after 3000ms of inactivity. At this point, the `debouncedSendNetworkRequest` function is finally executed since there hasn't been any activity for the duration of the timeout.
**Explanation of Code:**
Let's use the provided code example to explain how this works in JavaScript:
```javascript
// Debounce function for network requests
function debounceNetworkRequest(func, delay) {
let timeoutId;
return function() {
clearTimeout(timeoutId);
timeoutId = setTimeout(func, delay);
};
}
// Function to send network request
function sendNetworkRequest() {
// Code to send the network request
console.log("Network request sent");
}
// Create a debounced version of the network request function
const debouncedSendNetworkRequest = debounceNetworkRequest(sendNetworkRequest, 1000);
// Simulate user typing events
console.log("User starts typing...");
debouncedSendNetworkRequest(); // id=1, 1000ms
debouncedSendNetworkRequest(); // id=2, 2000ms
debouncedSendNetworkRequest(); // id=3, 2500ms
console.log("User stops typing...");
debouncedSendNetworkRequest(); // id=4, 3000ms
```
As the code simulates user typing events and the associated debounced function calls, you'll notice that the actual network request is only sent when the user stops typing or pauses for at least 3000ms (the highest delay among the simulated events). This demonstrates how debouncing effectively postpones the execution of the function until the user's input has settled.
## Throttling
Throttling is another technique used in web development to control the rate at which a function is executed, particularly in response to frequent events. Unlike debouncing, where the function is delayed until a certain period of inactivity occurs, throttling ensures that the function is executed at a steady rate, but not more frequently than a defined interval.
In your example, let's explain throttling using the provided IDs and time intervals: `id=1` with `1000ms`, `id=2` with `2000ms`, `id=3` with `2500ms`, and `id=4` with `3000ms`.
**Scenario and Explanation:**
Imagine the same user interface with a search bar, and the user is typing to perform a search. In this case, we want to ensure that the network request is sent at a controlled rate, regardless of how quickly the user types. This helps to prevent excessive network requests and maintain a smoother user experience.
Here's how the events play out using throttling:
1. `id=1`, 1000ms: The user starts typing. The `throttledSendNetworkRequest` function is triggered. Since there's been more than 1000ms since the last execution, the network request is sent immediately.
2. `id=2`, 2000ms: The user continues typing. An event occurs within 2000ms of the last execution. However, the throttling mechanism prevents the `throttledSendNetworkRequest` function from executing immediately. The function will be queued to execute after 1000ms (the throttle interval).
3. `id=3`, 2500ms: The user is still typing, and an event occurs within the 1000ms throttle interval. However, the function execution is still delayed, as it hasn't been 1000ms since the last execution.
4. `id=4`, 3000ms: The user has stopped typing, and an event occurs after 3000ms since the last execution. The `throttledSendNetworkRequest` function is triggered again, as the throttle interval has passed.
**Explanation of Code:**
Here's how the throttling code example works in JavaScript:
```javascript
// Throttle function for network requests
function throttleNetworkRequest(func, delay) {
let lastExecution = 0;
return function() {
const now = Date.now();
if (now - lastExecution >= delay) {
func();
lastExecution = now;
}
};
}
// Function to send network request
function sendNetworkRequest() {
// Code to send the network request
console.log("Network request sent");
}
// Create a throttled version of the network request function
const throttledSendNetworkRequest = throttleNetworkRequest(sendNetworkRequest, 1000);
// Simulate user typing events
console.log("User starts typing...");
throttledSendNetworkRequest(); // id=1, 1000ms
throttledSendNetworkRequest(); // id=2, 2000ms (delayed)
throttledSendNetworkRequest(); // id=3, 2500ms (delayed)
console.log("User stops typing...");
throttledSendNetworkRequest(); // id=4, 3000ms
```
In this code, the `throttleNetworkRequest` function is used to create a throttled version of the network request function. The function execution time is compared to the last execution time, and if the specified delay has passed since the last execution, the function is executed immediately. Otherwise, it's delayed until the delay interval has passed.
This mechanism ensures that the network request is sent at a controlled rate, respecting the throttle interval, and prevents excessive requests while still providing timely results to the user.
Here's a tabular comparison between debouncing and throttling:
| Aspect | Debouncing | Throttling |
|---------------------|--------------------------------------------------|------------------------------------------------|
| Purpose | Delay function execution until inactivity | Limit function execution rate |
| Execution | Executes after a pause in events | Executes at a steady rate |
| Event Resets Timer? | Yes, resets the timer on each event during delay | No, maintains a steady execution interval |
| Frequency Handling | Reduces function calls during rapid events | Limits function calls to a set interval |
| Use Cases | Typing (search suggestions), resizing events | Scrolling, mouse movement, rate-limited APIs |
Remember that the choice between debouncing and throttling depends on the specific use case and the desired behavior for handling frequent events in your application.

View File

@@ -0,0 +1,505 @@
# Introduction to Document Object Model
#### Definition
Dom is a tree structure in which every HTML element is arranged in heirarchical order.
#### Example
```htmlembedded
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<div>
<h1>This is heading 2</h1>
</div>
<div>
<h1>This is heading 1</h1>
<p>This is Paragraph</p>
</div>
</body>
</html>
```
#### Output
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/258/original/upload_364afb8e43132cd223c90b39c021e52a.png?1695145205)
#### DOM tree visualization of above example
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/259/original/upload_604c9f192818c3459c15d376d0569b83.png?1695145257)
**Note:**
* We will be uisng javascript to put the interactivity in our web apps or websites.
* JavaScript is used to put interactivity in DOM elements.
#### Question
On clicking the button append hello to the page.
#### Solution
**Step 1:** Slecting html element
To select oe identify a particular html element we have methods.
* getElementById
* QuerySelector
* QuerySelectorAll
**Code:** Using getElementById
```htmlembedded
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<button id = "btn - 1"> Say Hello </button>
<script>
// on clicking the button append hello to the page
let btn = document.getElementById('btn-1')
console.log(btn)
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/260/original/upload_a6d7d3c5b18e7b6ed529ca4088ae1dee.png?1695145319)
**Code:** Using getElementsByClassName
```htmlembedded
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<button class = "btn - 1"> Say Hello </button>
<script>
// on clicking the button append hello to the page
let btn = document.getElementsByClassName('btn - 1')
console.log(btn)
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/261/original/upload_a6d7d3c5b18e7b6ed529ca4088ae1dee_%281%29.png?1695145383)
**Code:** Using querySelector by ID
```htmlembedded
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<button id = "btn - 1"> Say Hello </button>
<script>
// on clicking the button append hello to the page
let btn = document.querySelector('#btn - 1')
console.log(btn)
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/262/original/upload_7fbdad793912e0939837795b61709f36.png?1695145503)
**Code:** Using querySelector by class
```htmlembedded
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<button id = "btn - 1"> Say Hello </button>
<button class = "btn - 1"> Say Bye </button>
<script>
// on clicking the button append hello to the page
let btn = document.querySelector('.btn-1')
console.log(btn)
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/265/original/upload_4718ed7f5720b3cee58f8644ee6a62d2.png?1695145596)
**Code:** Using querySelector by elements
The document method querySelector() returns the first element within the document that matches the specified selector, or group of selectors. If no matches are found, null is returned.
```htmlembedded
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<button id = "btn - 1"> Say Hello </button>
<script>
// on clicking the button append hello to the page
let btn = document.querySelector('button')
console.log(btn)
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/266/original/upload_a6d7d3c5b18e7b6ed529ca4088ae1dee_%282%29.png?1695145709)
**Step 2:** hello should get appended
**What is an event?**
Anything that happens depending on some other thins is an event. Let's say you are clicking on button then something will happen (hello is getting printed).
**Method - addEventListener:** We can add any event to any of our elements with using addEventListener method.
```javascript
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<button id = "btn - 1"> Say Hello </button>
<script>
// on clicking the button append hello to the page
let btn = document.querySelector('#btn-1')
console.log(btn)
btn.addEventListener('click', function(e){
// console.log(e)
let divElem = document.createElement('div')
divElem.innerText = 'Hello'
let body = document.querySelector('body')
body.appendChild(divElem)
})
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/267/original/upload_c04b7917d2c7dd94fad263612d3bae02.png?1695145825)
#### Append Hello DOM Tree
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/270/original/upload_604c9f192818c3459c15d376d0569b83.png?1695145889)
#### Question
Fix the list by inserting the missing element using querySelectorAll and insertBefore
#### Solution
**Step 1:** creating node list
**Note:** Node list is an array like structure which will have your elements in indexed form stored.
```javascript
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
// Fix the list by inserting the missing element using querySelectorAll and insertBefor
let allItems = document.querySelectorAll('li');
console.log(allItems);
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/271/original/upload_fc8a7cf791547f64129ac0eef13aa66e.png?1695146016)
**Step 2:** adding element
```javascript
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script>
// Fix the list by inserting the missing element using querySelectorAll and insertBefore
let ourList = document.querySelector('ul');
console.log(ourList);
let allItems = document.querySelectorAll('li');
console.log(allItems);
let indexThatHas8 = allItems[6];
let sevenElement = document.createElement('li')
sevenElement.innerText = '7'
ourList.insertBefore(sevenElement, indexThatHas8)
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/273/original/upload_9cda233672dc980dc39259973628c350.png?1695146091)
#### Question
Fix the mathmatical problem using JS
#### Solution
```javascript
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<!-- Q. Fix the mathmatical problem usng JS <br> -->
<p>2 + 2 = 22</p>
<script>
let para = document.querySelector('p')
para.innerText = `2 + 2 = 4`
</script>
</body>
</html>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/274/original/upload_7e599ac46343deed76441a6ec8a415e8.png?1695146150)
#### Question
Write a script which fetches the data-color attribute of the card and double clicking on them and attahces the fetched class to that card and also changes the data-color attribute to "used"
#### Solution
**classList:** An element can have multiple classes attached to it and all of those classes are collected in a structure called classList.
Example:
```javascript
<div class = "card test blue"> Our card </div>
```
The classList for this div will be - [card, test, blue]
```javascript
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8" />
<meta http - equiv = "X - UA - Compatible" content = "IE = edge" />
<meta name = "viewport" content = "width = device - width, initial - scale = 1.0" />
<title>Document</title>
<style>
* {
box-sizing: border-box;
}
body {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
padding-top: 5rem;
}
.blue {
background-color: blue;
box-shadow: 0px 0px 6px 5px;
}
.green {
background-color: green;
box-shadow: 0px 0px 6px 5px;
}
.red {
background-color: red;
box-shadow: 0px 0px 6px 5px;
}
.card {
border: 1px solid;
height: 10rem;
width: 10rem;
margin: 2rem;
}
</style>
</head>
<body>
<div class = "card" data-color = "blue"></div>
<div class = "card" data-color = "red"></div>
<div class = "card" data-color = "blue"></div>
<div class = "card" data-color = "red"></div>
<div class = "card" data-color = "red"></div>
<div class = "card" data-color = "blue"></div>
<div class = "card" data-color = "green"></div>
<div class = "card" data-color = "blue"></div>
<div class = "card" data-color = "green"></div>
<div class = "card" data-color = "blue"></div>
<script>
// Q Write a script which fetches the data-color attribute of the card on
//double clicking on them and attaches the fetched class to that card.
// Also changes the data-color attribute to "used"
let cardsNodeList = document.querySelectorAll('.card')
console.log(cardsNodeList)
for(let i = 0 ; i < cardsNodeList.length; i ++ ){
cardsNodeList[i].addEventListener('dblclick' , function(e){
console.log(e.currentTarget)
let classTobeAttached = e.currentTarget.getAttribute('data-color')
console.log(classTobeAttached)
e.currentTarget.classList.add(classTobeAttached)
e.currentTarget.setAttribute('data-color' , 'used')
})
}
</script>
</body>
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/276/original/upload_108dfa145e3eef14525de6578e2be0f0.png?1695146343)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,742 @@
# Dynamic DOM (Consume and work with an API)
## Agenda
* Create a weather app using an API.
* We will be using the "weather API" for getting the data of real-time weather and use that data in our app.
* Pre-requisites required for this app include **JSON**, **API** which will be covered in this lecture itself.
### JSON
* It stands for JavaScript Object Notation which is a data representation format.
* Let us consider an example to understand. Suppose, you have a farm and you sell products like vegetables, fruits, eggs etc. When a customer comes, everything is organized in different sections. You won't be storing everything inside a single container. It will be segregated like this-
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/288/original/upload_ec216d48c7ab4156e45c07960e9bb9c4.png?1695148844)
When a customer demands a product, that particular product will be given to them. In the same way, an API also works.
**API**
* Stands for **Application Programming Interface**.
* It is an interface where all the data and protocols can be stored in a section-wise manner.
* Just like the above example in the way we served the request to the customers, the data will be saved in an interface called as API
* Consider that apart from the customers, the you also cater to the needs of a restaurant by providing them with the products that they need.
* In this way, the demands of both customers and restaurant gets their demands fulfilled.
* These customers are basically **clients**. The shops are nothing but **servers**.
> Ask the students if they are familiar with the concept of client and server.
* The data is being requested from an external factor. Whatever the client needs, he will seek that from the external source.
* There are different use cases of various data and it is impossible to store it in a single place.
**Key points**
* API acts as a bridge between client and server.
* It has huge sets of data that can be used for applications.
* API not only has data, but also has protocols, method implementations etc.
#### Example
Consider you want to order a burger from Zomato or Swiggy. You decide to order it from Burger King. For that, Zomato will need to use the API of burger king to get the data of Burger king.
* This data is coming from the API.
* The API is stored in JSON format.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/289/original/upload_53dd165cb742742e9f97796f9941eaf0.png?1695148884)
> Ask students if they have heard about APIs, then mention that some of the experienced folks might have heard about this. Earlier, REST APIs and SOAP APIs were used to store data and write APIs. Now JS devs thought about introducing JSON which is similar to the JS Syntax.
**Key points**
* JSON stands for JavaScript Object Notation.
* It has a format similar to that of objects in JS. It has key-value pairs.
* The value can be obtained by using a key.
* The JSON file should end with '.json' extension.
>Open VSCode and create a Farm.json file.
Coming to the previous example, a farm can have name, registration number, number of goods etc. There are many farms as such. **We want to create a dataset of all the farms where we can see data of each individual farm.**
#### Code
First start with this code, and tell the students to ensure that the **keys** are enclosed in **double quotes** so that it does not throw an error.
```javascript
[
{
"name":"Farm 1",
"products":["Eggs","Milk","vegetables"],
"registrationNo":1234,
"Employees":70,
"isOperating": true
}
]
```
This is the first object. Whenever we are creating different objects within the same JSON file, ensure that the **keys remain the same** for all the objects. They should **not change**.
The code for 3 farms is-
```javascript
{
{
"name":"Farm 1",
"products":["Eggs","Milk","vegetables"],
"registrationNo":1234,
"Employees":70,
"isOperating": true
},
{
"name":"Farm 2",
"products":["Eggs","Milk","Cheese"],
"registrationNo":12345,
"Employees":60,
"isOperating": false
},
{
"name":"Farm 1",
"products":["vegetables"],
"registrationNo":12346,
"Employees":30,
"isOperating": true
}
}
```
> Show some examples of APIs to the students. Tell them that we will be creating a weather app today and open it.
> https://www.weatherapi.com/
> Inform that they need to sign-in.
* Find the API key and paste it under the **API explorer** tab.
* Select the protocol as **HTTPS**.
* Click on show response. You can see the JSON format in the response section.
Proceeding to our code, create an `index.html` file. Inside the script tag, paste the above JSON code.
```javascript
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
<title>Document</title>
</head>
<body>
<script>
let data = [
{
"name":"Farm 1",
"products":["Eggs","Milk","vegetables"],
"registrationNo":1234,
"Employees":70,
"isOperating": true
},
{
"name":"Farm 2",
"products":["Eggs","Milk","Cheese"],
"registrationNo":12345,
"Employees":60,
"isOperating": false
},
{
"name":"Farm 1",
"products":["vegetables"],
"registrationNo":12346,
"Employees":30,
"isOperating": true
}
]
console.log(data)
</script>
</body>
</html>
```
You will see an array of objects. This is not in the JSON format. To obtain the JSON format, we will enclose the objects in backtick.
**Key points**
* To convert JSON to JavaScript readable code, we use the **JSON.parse()** method
* The data received from a web server is a string, so to convert we use the JSON.parse() method to convert to a JavaScript object.
* When working with APIs, we get everything in string, so to parse that JSON, we use the JSON.parse() method.
>Brief the students about the steps to sign up on the https://www.weatherapi.com/. Ask them to do it accordingly.
* After logging in, there will be an API key generated which will be unique for everyone. Copy that key first.
* Under **API Response Fields**, you can check all the fields that the API is providing. There are many options available.
* Under **API Explorer**, paste the key. You will see the API Call as follows.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/292/original/upload_b1ce9393b8bd6d8ad8c11d66a121acac.png?1695149012)
Here the "q" stands for **query** and here we are querying for London. The call is basically our URL and inside this we also have our API key and the search query.
* Apart from the current data, you can also get the Forecast, Future data as provided by the API.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/293/original/upload_04a31b2878e550d5e6bf91f446d29778.png?1695149064)
> Inform the students that For this project, we will be focusing on the JS part, so the CSS part has already been created and we just have to paste it.
> Guide the students about the final interface before starting the code.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/294/original/upload_29da4001046dbfb1550deb5c6acdfa5b.png?1695149088)
**Step 1**
Create `index.html`, `style.css` and `index.js` files in VSCode.
**Step 2**
Inside the `index.html` file, we will be creating various divisions for various parameters like temperature, location, time and date etc.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/295/original/upload_a9c73804d050f6a0f077c520dcb3203b.png?1695149109)
In the other division for weather condition, we will display an emoji along with the weather condition.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/296/original/upload_89a96e16ee00ec649a4e80c5b3f90cc8.png?1695149128)
**Step 3**
Create a form which contains an input field and a button also.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/297/original/upload_7ba43e5dbf022aa0a217332ecc9afad7.png?1695149155)
> Write a step-by-step code for each step.
**Code**
```javascript
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta http-equiv = "X - UA - Compatible" content = "IE = edge">
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
<title>Weather app Class</title>
<link rel = "stylesheet" href = "style.css">
</head>
<body>
<div class = "container">
<div class = "weather">
<div class = "temp">20</div>
<div class = "time_location">
<p>Location</p>
<span>Random time and Date</span>
</div>
<div class = "weather_condition">
<p><img src = "" alt = ""></p>
<span>Condition</span>
</div>
</div>
</div>
<nav>
<form>
<input type = "text" placeholder = "Search_location" class = "searchField">
<button type = "submit">Search</button>
</form>
</nav>
</body>
<script src = "index.js"></script>
</html>
```
Execute the above code, the output is
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/298/original/upload_d0356ec9757a7bebdf0714ff51d749dd.png?1695149252)
This is a simple HTML code, we will have to add CSS to style it.
> Inform the students that we will not focus on the CSS part as it is simple. We will just paste the code.
**Style.css**-
```css
@import url("https://fonts.googleapis.com/css2?family=Economica&family=Grape+Nuts&family=Roboto:wght@100;300;400;700;900&display=swap");
* {
margin: 0%;
padding: 0;
font-family: "Roboto", sans-serif;
}
.container {
width: 100%;
height: 100vh;
background-color:#01161E;
display: flex;
justify-content: center;
align-items: center;
}
.weather {
z-index: 2;
display: flex;
align-items: center;
color: white;
}
.weather > div {
margin: 0.625rem;
}
.weather1 {
font-size: 4rem;
}
.weather p {
font-size: 2rem;
}
.weather span {
font-size: 0.75rem;
}
.weather3 span {
margin: 0.3rem;
}
.weather3 img {
width: 2rem;
}
nav {
height: 100px;
padding: 1rem 0;
position: absolute;
bottom: 0%;
width: 100%;
background-color: rgba(180, 177, 177, 0.202);
display: flex;
justify-content: center;
align-items: center;
}
nav form {
width: 70%;
grid-template-columns: 5fr 1fr;
display: grid;
}
.searchField {
font-size: 1.1rem;
outline: none;
color: white;
background-color: transparent;
padding: 1rem 0;
border: none;
border-bottom: 2px solid white;
width: 98%;
}
nav form button {
background-color:#4ECDC4;
font-size: 1.1rem;
color: white;
outline: none;
border: none;
cursor: pointer;}
```
After executing the CSS code, the output is
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/299/original/upload_f0509cd60bb58737e5373d70d48b4648.png?1695149282)
**Index.js**-
We will see how to get data from an API. We will use the **fetch API**. The **fetch()** method will be used here.
> Type fetch method in the browser and just show the docs as a glimpse.
The URL will be passed to the fetch() method as a parameter. We will understand how to use the fetch() method inside our JS code.
We will be coming across various terms like async, await but they will be covered in depth in the later lectures.
Before diving deep into the fetch() method, we will understand the try and catch block which is a pre-requisite for this.
The try block is used to execute the code that might encounter an error. An error can lead to unexpected termination of the program. Hence we put it inside the try block and any error that might be encountered by the try block is sent to the catch block. This is the exception handling concept.
In the try block, we will define the URL which is the API call.
```javascript
let url = `https://api.weatherapi.com/v1/current.json?key=35af7ff606db422880d141328231305&q=${target}&aqi=no`
```
Here we have wrapped the url in backticks so that we can use the string template literals which denoted bt the `${}` symbol.
* Let us understand using a real-life analogy about asynchronous programming.
* Suppose you are teaching a batch of 100 students. One student asks doubt and it takes 5 minutes to resolve it. Another student asks a doubt which takes another 5 minutes. One more student asks a doubt and it takes 5 minutes. To solve the doubts of 3 students, 97 students has to waste their 15 minutes.
> Ask the students what could a better approach for this?
* A better approach is to teach first and in the last few minutes of the lecture, take up the doubts of the students. In this way, the time of the other students is not wasted.
* Now just replace students with functions. The first approach is the synchronous programming where everything happens in a sequence.
* In synchronous programming, important instructions get blocked due to some previous instructions, which causes a delay in the user interface. **Asynchronous** code execution allows to execution next instructions immediately and **doesn't block the flow** because of previous instructions.
**Key points**
* In APIs, we will be using the asynchronous JS.
* We will be using **async** and **await** keywords which are important.
* The **async** keyword needs to be added before the function which tells the function that it is asynchronous.
* The **await** keyword tells that it needs wait till the data comes.
```javascript
const response = await fetch(url)
```
Now let's run the code till now.
```java
let target = "Pune"
async function fetchData(target){
try {
let url = `https://api.weatherapi.com/v1/current.json?key=8b6d5f63a04a485fa5351525232908&q=${target}&aqi=no`
const response = await fetch(url)
console.log(response)
}
catch(error){
console.log(error)
}
}
fetchData(target)
```
We will obtain this
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/300/original/upload_f06df9d4a4683805954bb656a39fe8f5.png?1695149320)
This is not in the JSON format. We will have to convert this to JSON by using the **.json() method**.
We will add this line-
```javascript
const data = await response.json()
console.log(data)
```
We will receive the data in JS object format.
Now we will **extract dynamic values from** the object.
Look into the format, to obtain temperature, we will go under `current` and then in `temp_c`
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/301/original/upload_894097c9f4d2225e2a1f65d7e0100f5b.png?1695149343)
Let us write the required values.
```java
let currentTemp = data.current.temp_c
let currentCondition = data.current.condition.text
let locationName = data.location.name
let localTime = data.location.localtime
let conditionEmoji = data.current.condition.icon
```
We will print these values on the console to see if they have been fetched properly or not. Output is
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/302/original/upload_a8add666735fe1907f2e69614eac16b5.png?1695149375)
To print and display the data on the website, we will use DOM. The data has to be changed dynamically and for that, DOM has to be used. We will use the classes earlier written in HTML to target and dynamically set values. The code for DOM part-
```javascript
const temperatureField = document.querySelector(".temp");
const cityField = document.querySelector(".time_location p");
const dateField = document.querySelector(".time_location span");
const emojiField = document.querySelector(".weather_condition img");
const weatherField = document.querySelector(".weather_condition span");
const searchField = document.querySelector(".searchField");
const form = document.querySelector("form");
```
> Just explain in short about the variable and which class it is targeting. Eg- `temperatureField` variable is targeting the `temp` class.
Now, we want to make our search bar work. We will add an event listener to it. Whenever there is an event performed, the event listener will execute the callback function passed as a parameter.
The search function changes the value of the target(the target city) to the value that we have entered. After typing the city and pressing the submit button, the form will get submitted and the "submit" event will get invoked.
The `fetchData` function will be passed the value of the target that we have entered.
```javascript
form.addEventListener('submit' , search )
//search- callback function
function search(){
target = searchField.value
fetchData(target)
}
```
The code till now is
```javascript
const temperatureField = document.querySelector(".temp");
const cityField = document.querySelector(".time_location p");
const dateField = document.querySelector(".time_location span");
const emojiField = document.querySelector(".weather_condition img");
const weatherField = document.querySelector(".weather_condition span");
const searchField = document.querySelector(".searchField");
const form = document.querySelector("form");
let target = "Pune"
async function fetchData(target){
try {
let url = `https://api.weatherapi.com/v1/current.json?key=8b6d5f63a04a485fa5351525232908&q=${target}&aqi=no`
const response = await fetch(url)
const data = await response.json()
form.addEventListener('submit' , search )
function search(){
target = searchField.value
fetchData(target)
}
console.log(data)
let currentTemp = data.current.temp_c
let currentCondition = data.current.condition.text
let locationName = data.location.name
let localTime = data.location.localtime
let conditionEmoji = data.current.condition.icon
console.log(currentTemp ,currentCondition ,locationName , localTime , conditionEmoji )
}
catch(error){
console.log(error)
}
}
fetchData(target)
```
After we run the code, we see that even after entering the name of the city in the search bar, the results are not getting updated. It is because, the form is submitting the value somewhere and page is getting refreshed. This is the nature of the form that after it is submitted, the page is refreshed.
For that, we use the **preventDefault()** method. Our code for the `search()` function becomes-
```javascript
function search(e){
e.preventDefault()
target = searchField.value
fetchData(target)
}
```
Now, when we search for a city, the output is obtained on the console.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/303/original/upload_07b20aa1d296c7ce114e051640ac2a33.png?1695149418)
Now, we will update the data on our page. A function `updateDOM()` will be created which will contain the values to be updates as parameters.
>First demonstrate this example. We will use the **innerText()** method to set the value inside the HTML.
```javascript
function updateDOM(temp , locationName , time , emoji , condition){
temperatureField.innerText = temp
cityField.innerText = locationName
emojiField.src = emoji
weatherField.innerText = condition
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/304/original/upload_3086c0907f207f7aecf7c97855c347e4.png?1695149448)
**ADD DAY**
We can also add day along with the date. Here, **date objects** will be used.
> console.log(time) then ask the students whether the date or the time is useful to get the day. The answer is date.
Since the date and time is separated by a space, we will use the **split()** method to separate them.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/305/original/upload_1ca3475c0f4e577805e4d08339373c48.png?1695149470)
The split() method splits the value at the delimiter provided and returns an array. Here. after splitting the above value, we get the date at 0th position and time at 1st position.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/306/original/upload_6dcd1d672b2d81019b045800199a9104.png?1695149492)
```javascript
const exactTime = time.split(" ")[1]
const exactdate = time.split(' ')[0]
```
To convert date to day, we follow-
```java
const exactDate = new Date(exactdate).getDay()
```
The output is a number which indicate the day in form of a number.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/307/original/upload_7cbfda536fac2d05ec6f13b7ac54321f.png?1695149519)
We will write another function that converts the day number to day.
```javascript
function getDayFullName(num) {
switch (num) {
case 0:
return "Sunday";
case 1:
return "Monday";
case 2:
return "Tuesday";
case 3:
return "Wednesday";
case 4:
return "Thursday";
case 5:
return "Friday";
case 6:
return "Saturday";
default:
return "Don't Know";
}
}
```
Our final code is
```javascript
const temperatureField = document.querySelector(".temp");
const cityField = document.querySelector(".time_location p");
const dateField = document.querySelector(".time_location span");
const emojiField = document.querySelector(".weather_condition img");
const weatherField = document.querySelector(".weather_condition span");
const searchField = document.querySelector(".searchField");
const form = document.querySelector("form");
let target = 'Pune'
form.addEventListener('submit' , search )
function search(e){
e.preventDefault()
target = searchField.value
fetchData(target)
}
async function fetchData(target){
try {
let url = `https://api.weatherapi.com/v1/current.json?key=35af7ff606db422880d141328231305&q=${target}&aqi=no`
const response = await fetch(url)
const data = await response.json()
console.log(data)
let currentTemp = data.current.temp_c
let currentCondition = data.current.condition.text
let locationName = data.location.name
let localTime = data.location.localtime
let conditionEmoji = data.current.condition.icon
console.log(currentTemp ,currentCondition ,locationName , localTime , conditionEmoji )
updateDOM(currentTemp , locationName ,localTime ,conditionEmoji , currentCondition)
} catch (error) {
console.log(error)
}
}
function updateDOM(temp , locationName , time , emoji , condition){
console.log(time)
const exactTime = time.split(" ")[1]
const exactdate = time.split(' ')[0]
const exactDay = getDayFullName(new Date(exactdate).getDay())
console.log(exactDay)
temperatureField.innerText = temp
cityField.innerText = locationName
dateField.innerText = `${exactTime} ${exactDay} ${exactdate}`
emojiField.src = emoji
weatherField.innerText = condition
}
function getDayFullName(num) {
switch (num) {
case 0:
return "Sunday";
case 1:
return "Monday";
case 2:
return "Tuesday";
case 3:
return "Wednesday";
case 4:
return "Thursday";
case 5:
return "Friday";
case 6:
return "Saturday";
default:
return "Don't Know";
}
}
fetchData(target)
```

View File

@@ -0,0 +1,462 @@
## Agenda
**Topics to cover in Javascript:**
Certainly, here are the headings for the provided topics:
1. **Kanban Board**
2. **Create and Update Task**
3. **Lock and Unlock Tasks**
4. **Colors**
5. **Pop-up Dialog Box**
6. **Delete**
8. **Local Storage**
We will try to cover most of these topics in today's sessions and the remaining in the next.
So let's start.
## Demo of the project:
Initially showing the demonstartion of the adding task, setting the colors, unlock/lock feature, filtering based on the colors and delete feature.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/293/original/upload_a9d95787724a2eefbc93ff4c186e60f4.png?1695962045)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/294/original/upload_13e69cd676c1c6d6d59196e64feca84f.png?1695962069)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/295/original/upload_17f12377711408328263574d51b2f1d2.png?1695962111)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/296/original/upload_be9923603755a2c965d7de8883361c37.png?1695962174)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/297/original/upload_072c08517a1a2e792b109cd3e694555b.png?1695962206)
Discussing about the local storage and the crud operation and also try to cover the drag-drop functionalities, accessibility.
On the highest level, mainly there are two components, to mark those will be using the two divs namely toolbox-container and maincontainer.
Inside the toolbox container first there are different tags for colors which we ca have and after that these two boxes of + and X. Apart fro that we can those ticket also.
### WireFrame of the Kanban Board
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/298/original/upload_ad6e9829fa6c28b5099af52832588dac.png?1695962233)
In toolbox container there are two container namely toolbox-priority-cont and action-btn-cont.
* Inside the toolbox-priority-cont we are having the four divs of colors(pink, blue, purple and green).
* In the toolbox-priority-cont having the two divs of add-btn and remove-btn. Alaso adding the font from the cdn font library.
Opening the live server and just seeing the effects of the added code till now and nothing is visible till now since no css has been added.
```htmlembedded
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF - 8">
<meta http-equiv = "X - UA - Compatible" content = "IE = edge">
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
<title>KanbanBoard</title>
<link rel = "stylesheet" href = "./style.css">
<link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw == " crossorigin = "anonymous" referrerpolicy = "no-referrer" />
</head>
<body>
<div class = "toolbox-cont">
<div class = "toolbox-priority-cont">
<div class = "color red"></div>
<div class = "color blue"></div>
<div class = "color green"></div>
<div class = "color black"></div>
</div>
<div class = "action-btn-cont">
<div class = "add-btn">
<i class = "fa-solid fa-plus fa-xl"></i>
</div>
<div class = "remove-btn">
<i class = "fa-solid fa-trash fa-lg"></i>
</div>
</div>
</div>
<div class = "main-cont">
<!-- <div class = "ticket-cont">
<div class = "ticket-color red"></div>
<div class = "ticket-id">#qu45</div>
<div class = "task-area" contenteditable = "true">Some Task</div>
<div class = "lock-unlock"><i class = "fa-solid fa-lock"></i></div>
</div> -->
</div>
<div class = "modal-cont">
<textarea class = "textarea-cont" placeholder = "Enter Your Task"></textarea>
<div class = "priority-color-cont">
<div class = "priority-color red"></div>
<div class = "priority-color blue"></div>
<div class = "priority-color green"></div>
<div class = "priority-color black active"></div>
</div>
</div>
<script src = "https://cdn.jsdelivr.net/npm/short-unique-id@latest/dist/short-unique-id.min.js"></script>
<script src = "./script.js"></script>
</body>
</html>
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/299/original/upload_e30d878377b9bd9cf474cb1301fc205a.png?1695962642)
Nothing is visible as no css has been added.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/300/original/upload_c2936d373f1e1a5f3d8ab5201bec1447.png?1695962669)
Let's start creating the css file namely style.css, and adding the code for the colors first by creating the css variables for the colors like pink, green, purple etc.
```css
/* create css variables */
:root{
--ligthBackground: #F9F5EB;
--background:#E4DCCF;
--red:#EA5455;
--blue:#002B5B;
--green:#245953;
}
*{
box-sizing: border-box;
}
body{
margin: 0;
padding: 0;
background-color: var(--background);
}
```
Now adding the css for the toolbox container starting with height, background-color and then adding the toolbox-priority-container by providing it with height, background-color. Same for the action-btn also same color and height.
```css
/* styling starts from here */
/* ************toolbox_cont **********/
.toolbox-cont{
height: 5rem;
background-color: var(--blue);
display: flex;
align-items: center;
position: fixed;
top: 0;
width: 100vw;
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/301/original/upload_d4f9f4cf35c5bfa09c037dd33e66d59f.png?1695962712)
Now adding the display: flex in the parent conatiner namely in the toolbox-container in the css code. Add margin-left in the toolbox-priority-cont and margin-left in the action-btn-cont. Also change the background-color of the action-btn-cont to the light.
Now add the color blocks in the toolbox-priority-cont. Providing the height and width to the color block. Also add the display:space-evenly and align-items:center to the parent class.
```css
.toolbox-priority-cont{
height: 3rem;
width: 18rem;
background-color:var(--ligthBackground);
margin-left: 5rem;
display: flex;
align-items: center;
justify-content: space-evenly;
}
.action-btn-cont{
height: 3rem;
width: 10rem;
background-color:var(--ligthBackground);
margin-left: 5rem;
display: flex;
align-items: center;
justify-content: space-around;
}
.color{
height: 1.5rem;
width: 3rem;
}
.red{
background-color: var(--red);
}
.blue{
background-color: var(--blue);
}
.green{
background-color: var(--green);
}
.black{
background-color: black;
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/302/original/upload_42e73b6053c5fad1c385afff28524242.png?1695962753)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/303/original/upload_3a3b8bfceafc27620ccce6c37351288b.png?1695962778)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/304/original/upload_f85dbc2e4cc9ebf27377b4e604430cbd.png?1695962805)
Provide the font size to the both add-btn and remove-btn and also add the display:flex, align-item:center and justify-content:space-evenly in the action-btn-cont.
```css
.add-btn,.remove-btn{
font-size: 1.25rem
}
.action-btn-cont{
height: 3rem;
width: 10rem;
background-color:var(--ligthBackground);
margin-left: 5rem;
display: flex;
align-items: center;
justify-content: space-around;
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/305/original/upload_d2b4ce1974ce2ded96f913efa5888f58.png?1695962865)
Now let's add the hover effect to the button of add-btn and remove-btn as follows:
```css
.add-btn:hover{
background-color: #4BB543;
}
.remove-btn:hover{
background-color: #4BB543;
}
```
**Asking Question?**
Can you add the hover effect to these color boxes in the nav bar?
**Ans:** You can try by themself.
Now lets start creating the modal with its html and css structure as follows:
```htmlembedded
<div class = "modal-cont">
<textarea class = "textarea-cont" placeholder = "Enter Your Task"></textarea>
<div class = "priority-color-cont">
<div class = "priority-color red"></div>
<div class = "priority-color blue"></div>
<div class = "priority-color green"></div>
<div class = "priority-color black active"></div>
</div>
</div>
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/306/original/upload_b5fb49c924748828c00aa7fcd3b96aec.png?1695962964)
```css
.modal-cont{
height: 50vh;
width: 45vw;
display: flex;
background-color: lightsalmon;
position: absolute;
top:30%;
left: 27%;
display: none;
}
.textArea-cont{
height: 100%;
width: 75%;
resize: none;
outline: none;
border: none;
background-color: #dfe4ea;
font-size: 2rem;
color: black;
}
.priority-colors-container{
height: 100%;
width: 25%;
display: flex;
flex-direction: column;
background-color: #4b4b4b;
align-items: center;
justify-content: space-around;
}
.priority-color{
height: 3rem;
width: 5rem;
}
.active{
border: 5px solid lightsalmon;
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/307/original/upload_5cc60a83b1501db2f03ac1db28e1f9aa.png?1695963048)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/308/original/upload_6897796c561e5883f5e5d43bcf818ec0.png?1695963073)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/309/original/upload_b649635bc4877058c29f45833a32b7f9.png?1695963095)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/310/original/upload_918fb74d1dda35302a77b8d06711e3d5.png?1695963117)
This CSS code defines styles for a modal container and its components:
1. **.modal-cont:**
- Defines the modal's dimensions and appearance.
- Positioned absolutely with a specific top and left offset.
- Initially hidden with `display: none`.
2. **.textArea-cont:**
- Represents a text area within the modal.
- Takes 75% of the modal's width.
- Has specific styling for background color, font size, and no borders.
3. **.priority-colors-container:**
- A container for priority color elements.
- Takes 25% of the modal's width.
- Aligns items vertically and adds a background color.
4. **.priority-color:**
- Represents priority color elements.
- Fixed dimensions for each element.
5. **.active:**
- Adds a border to elements with this class, using a lightsalmon color.
Overall, these styles are likely used for a modal interface, with a text area and priority color options.
Now we will be working for the buttons of + and X by giving the event listener to them. Here is a basic structure of the event Litener for them as follows:
```htmlembedded
Eventlistener.(click):
let flag=false
flag=true
if(flag=true){
modal visible
}
```
```javascript!
let addBtn = document.querySelector('.add-btn')
let modalCont = document.querySelector('.modal-cont')
let addTaskFlag = false
addBtn.addEventListener('click' , function(){
// Display the model
addTaskFlag = !addTaskFlag
if(addTaskFlag == true){
modalCont.style.display = 'flex'
}
else{
modalCont.style.display = 'none'
}
})
```
This JavaScript code adds functionality to a button:
1. `addBtn` and `modalCont` variables are defined to select HTML elements with specific classes.
2. An event listener is added to `addBtn` for a click event.
3. When the button is clicked, a flag (`addTaskFlag`) toggles between true and false.
4. If `addTaskFlag` is true, the modal container (`modalCont`) is displayed by changing its `display` style property to 'flex'.
5. If `addTaskFlag` is false, the modal container is hidden by setting its `display` property to 'none'.
This code toggles the visibility of the modal when the button is clicked.
Now lets create the structure of the task ticket as follows:
```htmlembedded
<!-- Task Ticket -->
<div class = "main-cont">
<!-- <div class = "ticket-cont">
<div class = "ticket-color"></div>
<div class = "ticket-id">12345</div>
<div class = "task-area" contenteditable = "false">Random Task</div>
<div class = "ticket-lock">
<i class = "fa-solid fa-lock"></i>
</div>
</div> -->
```
Now also give the styling to the task ticket:
```javascript
.modal-cont{
height: 50vh;
width: 45vw;
display: flex;
background-color: lightsalmon;
position: absolute;
top:30%;
left: 27%;
display: none;
}
.ticket-cont{
height: 12rem;
width: 15rem;
background-color: coral;
}
.ticket-color{
height: 1rem;
}
.ticket-id{
background-color: yellow;
height: 2rem;
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/311/original/upload_39d93710a1f82436e4b11ae641ab6240.png?1695963264)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/312/original/upload_9a008ba1c7965ffce4d7f4ea2c2e34e2.png?1695963295)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/313/original/upload_2d430278f46b2c037ad120ae71d7a62c.png?1695963341)
This CSS code defines styles for a modal container and its associated ticket elements:
1. **.modal-cont:**
- Defines the modal's dimensions and appearance.
- Positioned absolutely with specific top and left offsets.
- Initially hidden with `display: none`.
- Has a flex display and a light salmon background color.
2. **.ticket-cont:**
- Represents a ticket container.
- Fixed height and width for each ticket.
- Coral background color.
3. **.ticket-color:**
- Represents a color strip on the ticket.
- Fixed height and width, typically used for visual categorization.
4. **.ticket-id:**
- Represents the ID section of the ticket.
- Has a yellow background color and a specific height.
These styles appear to be used for creating a modal with tickets, each with a color strip and an ID section.

View File

@@ -0,0 +1,292 @@
# Full Stack LLD & Projects: JavaScript-8: Kanban Board-2(DOM Implementation & Manipulation)
**Agenda of this Lecture:**
* Ticket Generation
* Adding Task, colour, ID to generated ticket
* Ticket Removal
* Locking Mechanism
* Editing Ticket Content
### Explanation
We'll begin by implementing the ticket generation feature, which involves creating a function to dynamically generate new task tickets. These tickets will then be manipulated and moved across the columns using DOM (Document Object Model) manipulation.
In the file `script.js` we will add this function:
```javascript
function createTicket() {
// Create a new ticket container element
let ticketCont = document.createElement('div');
}
```
Now we will add **class** to this particular div using the `setAttribute` :
```javascript
function createTicket() {
// Create a new ticket container element
let ticketCont = document.createElement('div');
// Set the class attribute of the ticket container
ticketCont.setAttribute('class', 'ticket-cont'); //
}
```
Whenever this function is called, a new ticket will be created with class `ticket-cont`.
As `ticketCont` contains 3 more divs inside, we will create them inside this function using `innerHTML` function
```javascript
function createTicket() {
// Create a new ticket container element
let ticketCont = document.createElement('div');
ticketCont.setAttribute('class', 'ticket-cont');
// Create the HTML content for the ticket container
ticketCont.innerHTML = `
<div class="ticket-color"></div>
<div class="ticket-id">12345/div>
<div class="task-area">Random Task</div>`
mainCont.appendChild(ticketCont)
```
So we are passing the HTML codes here, so whenever a ticket is created , these 3 divs will also be there.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/613/original/upload_ed6901a16250a577c26b8e2795b1f89d.png?1695229185)
### Explanation
The `addEventListener` method is used to attach an event listener to a DOM element, allowing you to respond to specific events like clicks, key presses, mouse movements, etc.
We add an event listener to the `modalCont` element for the 'keydown' event. This event occurs when a key on the keyboard is pressed and then released.
```javascript
modalCont.addEventListener('keydown', function(e) {
let key = e.key;
if (key === 'Shift') {
createTicket(); // Call the createTicket function to create a new ticket
modalCont.style.display = 'none'; // Hide the modal
textArea.value = ''; // Clear the textarea's content
}
})
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/624/original/upload_55a8528debc5766a05e91dfe21a5cad8.png?1695232320)
### Explanation
As of now everything like Task, color and ID of the created task is static. In this we will be making it dynamic.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/625/original/1.png?1695232401)
So we can choose color, and the ticket will come with a randomly generated ID.
To identify and select these priority color divs, we will use `querySelectorAll` method
We want to select all elements with the class name 'priority-color' using `querySelectorAll` and then iterate through each of these elements using the forEach method. Here's how you can do that:
```javascript
let allPriorityColors = document.querySelectorAll('.priority-color');
allPriorityColors.forEach(function(colorElem) {
colorElem.addEventListener('click', function() {
// Remove 'active' class from all priority colors
allPriorityColors.forEach(function(priorityColorElem) {
priorityColorElem.classList.remove('active');
});
// Add 'active' class to the clicked colorElem
colorElem.classList.add('active');
// Implement additional logic to assign the selected color to a task
// For example, you can use this space to perform your task color assignment
});
});
```
In this code, when a color element with the class 'priority-color' is clicked, the event listener:
* Iterates through all `allPriorityColors` and removes the 'active' class from each element.
* Adds the 'active' class to the clicked `colorElem`.
* Implements additional logic to assign the selected color to a task
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/626/original/2.png?1695232419)
So right now we have implemented such that we can select this color, but now we want to get the value of the particular color.
Now we will have a **color array**.
We define an array of colors and updates the modalPriorityColor variable based on the selected color when a color element is clicked.
```javascript
let colors = ["lightpink", "lightgreen", "lightblue", "black"];
let modalPriorityColor = colors[colors.length - 1]; // Default to black
let allPriorityColors = document.querySelectorAll('.priority-color');
allPriorityColors.forEach(function(colorElem) {
colorElem.addEventListener('click', function() {
// Remove 'active' class from all priority colors
allPriorityColors.forEach(function(priorityColorElem) {
priorityColorElem.classList.remove('active');
});
// Add 'active' class to the clicked colorElem
colorElem.classList.add('active');
modalPriorityColor = colorElem.classList[0]; // Update modalPriorityColor
});
});
```
In this code:
* You define an array colors with color names.
* `modalPriorityColor` is initially set to the last color in the array ('black') as the default.
* The event listener loop iterates through each color element and adds a click event listener.
* When a color element is clicked, the 'active' class is toggled as before.
* Additionally, the `modalPriorityColor` is updated to match the class name of the clicked color element, indicating the selected color.
**Passing ticketColor to createTicket Function:**
In the `createTicket` function, you need to add a parameter `ticketColor` to the function signature. This parameter is intended to hold the selected color for the ticket. When calling the `createTicket` function inside the `modalCont` event listener, you're passing the `modalPriorityColor` as an argument to this function.
This change allows you to set the ticket color dynamically based on the selected priority color. You can use the ticketColor parameter to apply the selected color to the appropriate part of the ticket's HTML content.
```javascript
function createTicket(ticketColor) {
// Create a new ticket container element
let ticketCont = document.createElement('div');
ticketCont.setAttribute('class', 'ticket-cont');
// Create the HTML content for the ticket container
ticketCont.innerHTML = `
<div class="ticket-color" style="background-color: ${ticketColor};"></div>
<div class="ticket-id">12345</div>
<div class="task-area">Random Task</div>
`;
// Append the ticket container to the main container
mainCont.appendChild(ticketCont);
}
// Event listener for 'Shift' key press in modalCont
modalCont.addEventListener('keydown', function(e) {
let key = e.key;
if (key === 'Shift') {
createTicket(modelPriorityColor); // Create a new ticket with the selected color
modalCont.style.display = 'none'; // Hide the modal
textArea.value = ''; // Clear the textarea's content
}
});
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/628/original/4.png?1695232451)
Now we need to update the task details
* The `createTicket` function accepts two parameters: `ticketColor` for the color of the ticket and `ticketTask` for the content of the ticket's task.
* The `ticketTask` parameter is used to dynamically insert the task content into the task-area div element.
* In the modalCont event listener, the content of the `textAreaCont` element is retrieved using .value and assigned to taskContent.
* When the 'Shift' key is pressed, a new ticket is created with the selected color and task content, and then the modal is hidden.
* The content of the `textAreaCont` element is cleared for the next input.
```javascript
function createTicket(ticketColor, ticketTask) {
// Create a new ticket container element
let ticketCont = document.createElement('div');
ticketCont.setAttribute('class', 'ticket-cont');
// Create the HTML content for the ticket container
ticketCont.innerHTML = `
<div class="ticket-color" style="background-color: ${ticketColor};"></div>
<div class="ticket-id">12345</div>
<div class="task-area">${ticketTask}</div>
`;
// Append the ticket container to the main container
mainCont.appendChild(ticketCont);
}
// Event listener for 'Shift' key press in modalCont
modalCont.addEventListener('keydown', function(e) {
let key = e.key;
if (key === 'Shift') {
let taskContent = textAreaCont.value; // Get the content from the textarea
createTicket(modelPriorityColor, taskContent); // Create a new ticket with the selected color and task content
modalCont.style.display = 'none'; // Hide the modal
textAreaCont.value = ''; // Clear the textarea's content
}
});
```
Now we need to uniquely generate ID for each task created:
We will be using an external library **shortID** for this
```javascript
function createTicket(ticketColor, ticketID, ticketTask) {
// Create a new ticket container element
let ticketCont = document.createElement('div');
ticketCont.setAttribute('class', 'ticket-cont');
// Create the HTML content for the ticket container
ticketCont.innerHTML = `
<div class="ticket-color" style="background-color: ${ticketColor};"></div>
<div class="ticket-id">${ticketID}</div>
<div class="task-area">${ticketTask}</div>
`;
// Append the ticket container to the main container
mainCont.appendChild(ticketCont);
}
// Event listener for 'Shift' key press in modalCont
modalCont.addEventListener('keydown', function(e) {
let key = e.key;
if (key === 'Shift') {
let taskContent = textAreaCont.value; // Get the content from the textarea
let ticketID = shortid(); // Generate a unique ticket ID
createTicket(modelPriorityColor, ticketID, taskContent); // Create a new ticket with the selected color, ticket ID, and task content
modalCont.style.display = 'none'; // Hide the modal
textAreaCont.value = ''; // Clear the textarea's content
}
});
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/629/original/5.png?1695232474)
To remove the task, we can do similarly to what we did for adding task:
```javascript
let removeTaskFlag = false;
let removeBtn = document.querySelector('.remove-btn'); // Replace with the actual class or ID selector
removeBtn.addEventListener('click', function() {
removeTaskFlag = !removeTaskFlag; // Toggle the removeTaskFlag when the button is clicked
if (removeTaskFlag) {
alert('Delete button is activated.');
}
});
```
---

View File

@@ -0,0 +1,172 @@
# Full Stack LLD & Projects: JavaScript-9: Kanban Board -3(Bussiness Logics & Local Storage)
**Agenda of this Lecture:**
* Locking Mechanism
* Changing the Priority color of the Task
* Filtering out Task with using the priority color filter
* Showing All Tasks on db click
### Explanation
Currently we have just implemented the project, but we dont have any lock in the project.
Hence, we will be implementing the lock in this section
We can use the **font-awesome** and get a lock icon for our tasks.
```javascript
function createTicket(ticketColor, ticketID, ticketTask) {
// Create a new ticket container element
let ticketCont = document.createElement('div');
ticketCont.setAttribute('class', 'ticket-cont');
// Create the HTML content for the ticket container
ticketCont.innerHTML = `
<div class="ticket-color" style="background-color: ${ticketColor};"></div>
<div class="ticket-id">${ticketID}</div>
<div class="task-area">${ticketTask}</div>
<div class="ticket-lock"><i class="fa-solid fa-lock"></i></div>
`;
// Append the ticket container to the main container
mainCont.appendChild(ticketCont);
handleRemoval(ticketCont);
}
```
* We have added an additional `div` element with the class `ticket-lock` to represent the lock icon for each ticket.
* Inside the `ticket-lock` div, you're using Font Awesome's icon syntax to include the lock icon using the fa-lock class from the `fa-solid` style.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/638/original/1.png?1695233411)
Now we have added the lock, but we need to make it functional now:
```javascript
let lockClose = 'fa-lock';
let lockOpen = 'fa-lock-open';
function handleLock(ticket) {
let ticketLockElem = ticket.querySelector('.ticket-lock');
let ticketLockIcon = ticketLockElem.children[0];
ticketLockIcon.addEventListener('click', function() {
console.log('Lock Selected'); // Added single quotes around the log message
if (ticketLockIcon.classList.contains(lockClose)) {
ticketLockIcon.classList.remove(lockClose);
ticketLockIcon.classList.add(lockOpen);
} else {
ticketLockIcon.classList.remove(lockOpen);
ticketLockIcon.classList.add(lockClose);
}
});
}
```
Now to make the content editable inside the task section whenever the lock is open, we will make the following changes:
```javascript
let lockClose = 'fa-lock';
let lockOpen = 'fa-lock-open';
function handleLock(ticket) {
let ticketLockElem = ticket.querySelector('.ticket-lock');
let ticketLockIcon = ticketLockElem.children[0];
let ticketTaskArea = ticket.querySelector('.task-area'); // Corrected selector
ticketLockIcon.addEventListener('click', function() {
console.log('Lock Selected');
if (ticketLockIcon.classList.contains(lockClose)) {
ticketLockIcon.classList.remove(lockClose);
ticketLockIcon.classList.add(lockOpen);
ticketTaskArea.setAttribute('contenteditable', 'true'); // Changed 'contenteditable', 'true'
} else {
ticketLockIcon.classList.remove(lockOpen);
ticketLockIcon.classList.add(lockClose);
ticketTaskArea.setAttribute('contenteditable', 'false'); // Changed 'contenteditable', 'false'
}
});
}
```
* Corrected the selector for `ticketTaskArea` to use '.task-area'.
* Changed the `contenteditable` attribute value to 'true' or 'false' to correctly toggle the editable state of the task area based on the lock state.
### Explanation
In this section we will handle the color of the tasks
```javascript
function handleColor(ticket) {
let ticketColorBand = ticket.querySelector('.ticket-color'); // Corrected selector
ticketColorBand.addEventListener('click', function() {
let currentColor = ticketColorBand.classList[0]; // Changed index to 0
let currentColorIdx = colors.findIndex(function(color) {
return currentColor === color;
});
currentColorIdx++; // Increment the index
let newTicketColorIdx = currentColorIdx % colors.length; // Corrected variable name
let newTicketColor = colors[newTicketColorIdx]; // Corrected variable name
ticketColorBand.classList.remove(currentColor); // Corrected spelling
ticketColorBand.classList.add(newTicketColor); // Corrected spelling
});
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/639/original/2.png?1695233433)
### Explanation
In this feature, we need to filter the task according to the priority color.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/640/original/3.png?1695233456)
```javascript
let toolboxColors = document.querySelectorAll('.color');
for (let i = 0; i < toolboxColors.length; i++) {
toolboxColors[i].addEventListener('click', function() {
let selectedToolboxColor = toolboxColors[i].classList[0];
let allTickets = document.querySelectorAll('.ticket-cont'); // Corrected selector
for (let j = 0; j < allTickets.length; j++) {
allTickets[j].remove(); // Removed square brackets and added j to index
let filteredTickets = ticketsArr.filter(function(ticket) {
return selectedToolboxColor === ticket.ticketColor;
});
filteredTickets.forEach(function(filteredTicket) {
createTicket(
filteredTicket.ticketColor,
filteredTicket.ticketTask,
filteredTicket.ticketID
);
});
}
});
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/641/original/4.png?1695233480)

View File

@@ -0,0 +1,309 @@
In the previous session, we covered topics such as ticket creation, deletion, locking, and unlocking, as well as dynamically changing the priority color. We comprehensively explored the implementation of these functionalities.
However, a new issue has arisen. Upon initial usage, the filter buttons function as intended. Yet, upon subsequent clicks, a problem arises where duplicate tickets of the selected color are generated.
What might be the underlying cause of this problem?
To address this issue, we're planning to implement a validation process using unique identifier to prevent the occurrence of duplicate tickets.
This is the code that we have implemented till now for filtering tickets.
We've executed the loop that iterates through the toolbox colors, covering every index. For each color dip, we've attached corresponding event listeners. When a click event occurs, our first step is to determine which color dip or filter was clicked for instance, selecting black would mean retrieving tasks labeled with a black priority color.
Following the color selection from the toolbox, we proceed to match that color with the colors associated with each ticket. We apply a filtering process to narrow down the array of tickets to only include those that match the selected color. The result is an array of filtered tickets, where each ticket object contains information like color values, task details, and IDs.
At this juncture, we remove the default set of tickets and replace them with the newly filtered array. This is the approach we discussed in the previous session. However, an issue arises when we repeatedly select a color, leading to the duplication of arrays.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/630/original/1.png?1695232771)
#### Pseudocode
```javascript
for (let i = 0; i < toolboxColors.length; i++) {
toolboxColors[i].addEventListener('click', function() {
let selectedToolBoxColor = toolboxColors[i].classList[0];
let filteredTickets = ticketsArr.filter(function(ticket) {
return selectedToolBoxColor === ticket.ticketColor;
});
let allTickets = document.querySelectorAll('.ticket-cont');
for (let i = 0; i < allTickets.length; i++) {
allTickets[i].remove();
}
filteredTickets.forEach(function(filteredTicket) {
createTicket(filteredTicket.ticketColor, filteredTicket.ticketTask, filteredTicket.ticketId);
});
});
}
```
To tackle this issue, we're planning to implement a validation mechanism that utilizes unique identifier to ensure that duplicate tickets aren't generated. This way, the filtered ticket array will only consist of distinct tickets corresponding to the selected color.
So, using the unique IDs associated with each ticket is a great way to prevent duplicates. This way, we can ensure that the same ticket isn't added to the filtered array more than once.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/631/original/2.png?1695232789)
In which part of the code should you integrate this ID-checking mechanism to effectively prevent duplicate tickets from being included in the filtered array?
Within the createTicket method, we'll implement the following logic: if a ticket possesses an existing ID, it will be used; otherwise, a new unique ID will be generated during its initial creation. It's essential to note that the ticket will only be pushed to the array if it lacks an ID, ensuring avoidance of duplication.
#### Pseudocode
```javascript
function createTicket(ticketColor, ticketTask, ticketId) {
// Adding an identifier
let id = ticketId || shortid();
}
```
Prior to adding the ID to the array, we will perform a validation to ascertain its existence, and only if it indeed exists, will it be appended within the createTicket method.
#### Pseudocode
```javascript
function createTicket(ticketColor, ticketTask, ticketId) {
// Adding an identifier
let id = ticketId || shortid();
// Other code
if (!ticketId) {
ticketArr.push({ ticketColor, ticketTask, ticketId: id });
}
}
```
---
title: What is local storage?
description: Gaining Insight into Local Storage and How to Utilize It
duration: 600
card_type: cue_card
---
> Note to instructor - Address any questions that the students might have.
What kind of ID will the shortid library generate?
It will produce a random ID for the ticket.
Upon accessing your browser, you'll encounter the inspect page, which offers a variety of elements to explore. Within this inspect interface, navigate to the application tab. Here, you'll be presented with numerous sections such as local storage, session storage, and cookies. For today's discussion, our focus will be on local storage.
What exactly is local storage? What are your thoughts on the ideal function of local storage?
The issue lies in data loss when a page is refreshed. We intend for the application to retain data even after a refresh that's the core purpose of having this feature.
This occurrence arises from our utilization of local storage as the repository for all our data. Now, let's delve into the concept of local storage. In essence, it offers the capacity to store up to 5mb of information, enabling your browser to retain such data. Imagine a scenario where you're crafting an application and the need for a database isn't there. In situations where a 5-megabyte threshold suffices, local storage serves as a viable means to preserve and manage the data.
What is a Windows object?
Within JavaScript, resides the window object. This object grants access to an array of properties, methods, and functionalities offered by the Document Object Model (DOM).
#### Pseudocode
```htmlembedded
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<!-- The content of the document... -->
</body>
<script>
console.log(window);
</script>
</html>
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/632/original/3.png?1695232812)
We can use numerous methods and properties within the browser including local storage feature. Access to local storage is facilitated through the window object. Within local storage, you can engage actions like setting an item and getting an item. The "setItem" function is utilized to store data, while "getItem" serves to retrieve stored data. This data is maintained in a JavaScript-oriented key-value structure.
Moreover, there's a "removeItem" capability that permits the deletion of specific entries from local storage. Should the need arise to completely erase all data, the "localStorage.clear" command accomplishes that task.
Now, let's see how we are gonna implement local storage
In which scenarios will the requirement for local storage arise?
Let's review all our functionalities:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/633/original/4.png?1695232834)
a. Creating a ticket involves storing them within local storage.
b. Updating a ticket, whether altering its color or task, entails updating these modified values in local storage.
c. Deleting a ticket results in its removal from local storage.
Notably, if you refrain from deletion and subsequently refresh the page, local storage will retain the instance of that particular ticket, so we need to remove that from local storage to reflect the data.
Whenever a ticket creation occurs, we have the option to store it in local storage using the "setItem" method. This involves providing the name of the array as a parameter. Here, during the code insertion within the createTicket method, we will employ the setItem function of localStorage:
#### Pseudocode
```javascript
function createTicket(ticketColor, ticketTask, ticketId) {
if (!ticketId) {
ticketArr.push({ ticketColor, ticketTask, ticketId: shortid() });
localStorage.setItem('tickets', JSON.stringify(ticketArr));
}
}
```
It's important to note that local storage accommodates data in string format. Thus, any data you intend to store should be converted into string form before insertion.
> Note to instructor - Test to determine whether the local storage has been successfully established or not.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/634/original/5.png?1695232854)
After refreshing the page, you'll notice that the data resides within local storage, yet the paradox emerges despite setting the data, we encounter a loss of the data itself. The situation unfolds as we've successfully stored the data, yet the process of retrieving the values proves elusive.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/635/original/6.png?1695232878)
We employ the stringify method to establish the data, while the reverse, JSON.parse, allows us to retrieve the data back in JSON format.
As we initiate the application, our first step is to retrieve all the tickets stored within local storage. If we come across any items designated as "tickets," our subsequent approach revolves around displaying or generating these tickets accordingly. For each of these identified tickets, the createTicket method will be invoked.
#### Pseudocode
```javascript=
// local storage
if (localStorage.getItem('tickets')) {
ticketsArr = JSON.parse(localStorage.getItem('tickets'));
ticketsArr.forEach(function(ticket) {
createTicket(ticket.ticketColor, ticket.ticketTask, ticket.ticketId);
});
}
```
> Note to instructor - Test this by refreshing the page again; you'll observe that nothing is removed.
> Note to instructor - Feel free to address any questions that students might have.
Initially, let's construct a function that facilitates the retrieval of the ticket's index using its corresponding ID. The ID will help in identifying the ticket that requires updating.
#### Pseudocode
```javascript
function getTicketIdx(id) {
let ticketIdx = ticketArr.findIndex(function(ticketObj) {
return ticketObj.ticketId === id;
});
return ticketIdx;
}
```
Within the handleLock function, whenever a lock is unlocked through a click event, it becomes imperative to retrieve the index of the corresponding ticket. This enables us to determine the specific location where this action occurred.
#### Pseudocode
```javascript
function handleLock(ticket) {
let ticketLockElem = ticket.querySelector('.ticket-lock');
let ticketLockIcon = ticketLockElem.children[0];
let ticketTaskArea = ticket.querySelector('.task-area');
ticketLockIcon.addEventListener('click', function() {
let ticketIdx = getTicketIdx(id);
// Other code
// Updated task
ticketsArr[ticketIdx].ticketTask = ticketTaskArea.innerText;
localStorage.setItem('tickets', JSON.stringify(ticketsArr));
});
}
```
Henceforth, it will be retained with an updated task.
Now, let's proceed to explore the color feature.
#### Pseudocode
```javascript
function handleColor(ticket) {
let ticketColorBand = ticket.querySelector('.ticket-color');
ticketColorBand.addEventListener('click', function() {
// getting index
let ticketIdx = getTicketIdx(id);
let currentColor = ticketColorBand.classList[1];
let currentColorIdx = colors.findIndex(function(color) {
return currentColor === color;
});
currentColorIdx++;
let newTicketColorIdx = currentColorIdx % colors.length;
let newTicketColor = colors[newTicketColorIdx];
ticketColorBand.classList.remove(currentColor);
ticketColorBand.classList.add(newTicketColor);
// Updated task
ticketsArr[ticketIdx].ticketColor = newTicketColor;
localStorage.setItem('tickets', JSON.stringify(ticketsArr));
});
}
```
Now, let's evaluate the final feature, which involves deleting data from local storage.
#### Pseudocode
```javascript
function handleRemoval(ticket) {
ticket.addEventListener('click', function() {
if (!removeTaskFlag) return;
let idx = getTicketIdx(id);
ticket.remove(); // UI removal
let deletedElement = ticketsArr.splice(idx, 1);
localStorage.setItem('tickets', JSON.stringify(ticketsArr));
});
}
```

View File

@@ -0,0 +1,407 @@
## Agenda
- What is Event Propagation?
- Concept of Bubbling
- Concept of capturing
- Machine coding question
- Star Rating Component
- Counter Component
We will try to cover most of these topics in today's sessions and the remaining in the next.
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## Event Propagation
Event propagation refers to the process of how events are dispatched and processed in the Document Object Model (DOM) hierarchy of elements in web development. There are two main phases of event propagation: capturing phase and bubbling phase. During these phases, events can propagate through the DOM tree from the root to the target element (capturing) or from the target element back up to the root (bubbling).
### Bubbling
Event bubbling is one of the phases of event propagation in the DOM. When an event occurs on a DOM element, it first triggers the event handlers on the target element itself, then it bubbles up to its parent elements in the DOM hierarchy. This allows for the creation of more general event listeners that can be applied to parent elements to handle events from multiple child elements.
### Capturing
Event capturing is the other phase of event propagation in the DOM. During the capturing phase, events start from the root element and propagate down to the target element. This phase occurs before the bubbling phase and can be useful when you want to capture events on parent elements before they reach their child elements.
Certainly, let's use an example to explain event bubbling and capturing with three nested `<div>` elements: `#grandparent`, `#parent`, and `#child`. Suppose you have the following HTML structure:
```html
<div id="grandparent">
<div id="parent">
<div id="child"></div>
</div>
</div>
```
Now, let's say we attach a click event listener to each of these `<div>` elements and observe how event propagation works.
```javascript
const grandparent = document.querySelector('#grandparent');
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
grandparent.addEventListener('click', function() {
console.log('Grandparent clicked');
});
parent.addEventListener('click', function() {
console.log('Parent clicked');
});
child.addEventListener('click', function() {
console.log('Child clicked');
});
```
Here's how the event propagation works during both the capturing and bubbling phases when you click on the `#child` element:
1. **Capturing Phase:**
When an event occurs, it starts from the root element (`<html>`) and goes down the DOM tree. In this phase, the event handlers are triggered in the following order:
1. `#grandparent` capturing
2. `#parent` capturing
3. `#child` capturing (target phase)
However, we don't have capturing event listeners in this example, so nothing will be logged during this phase.
2. **Target Phase:**
This is where the event reaches the target element, which is `#child`. The event handler for `#child` is triggered, and you will see the message "Child clicked" logged to the console.
3. **Bubbling Phase:**
After the target phase, the event bubbles up through the DOM tree in the opposite direction. The event handlers are triggered in the following order:
1. `#child` bubbling
2. `#parent` bubbling
3. `#grandparent` bubbling (root phase)
As a result, you will see the messages "Parent clicked" and "Grandparent clicked" logged to the console, in that order.
So, the output in the console when you click on the `#child` element will be:
```
Child clicked
Parent clicked
Grandparent clicked
```
This example demonstrates the sequence of event propagation during both the capturing and bubbling phases in the context of nested `<div>` elements. The capturing and bubbling phases allow you to handle events at different levels of the DOM hierarchy.
Let's explore the event propagation using the `useCapture` parameter set to both `true` and `false` for the same example with the three nested `<div>` elements: `#grandparent`, `#parent`, and `#child`.
```javascript
const grandparent = document.querySelector('#grandparent');
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
grandparent.addEventListener('click', function() {
console.log('Grandparent clicked (Bubbling)');
}, false);
parent.addEventListener('click', function() {
console.log('Parent clicked (Bubbling)');
}, false);
child.addEventListener('click', function() {
console.log('Child clicked (Bubbling)');
}, false);
grandparent.addEventListener('click', function() {
console.log('Grandparent clicked (Capturing)');
}, true);
parent.addEventListener('click', function() {
console.log('Parent clicked (Capturing)');
}, true);
child.addEventListener('click', function() {
console.log('Child clicked (Capturing)');
}, true);
```
In this example, we've added event listeners with both capturing and bubbling phases for each of the three elements. The `useCapture` parameter is set to `true` for capturing and `false` for bubbling.
**Scenario 1: useCapture set to `false` (Bubbling)**
When you click on the `#child` element, the event will propagate in the bubbling phase. The order of event handling will be:
1. `#child` clicked (Bubbling)
2. `#parent` clicked (Bubbling)
3. `#grandparent` clicked (Bubbling)
The output in the console will be:
```
Child clicked (Bubbling)
Parent clicked (Bubbling)
Grandparent clicked (Bubbling)
```
**Scenario 2: useCapture set to `true` (Capturing)**
When you click on the `#child` element, the event will propagate in the capturing phase. The order of event handling will be:
1. `#grandparent` clicked (Capturing)
2. `#parent` clicked (Capturing)
3. `#child` clicked (Capturing)
The output in the console will be:
```
Grandparent clicked (Capturing)
Parent clicked (Capturing)
Child clicked (Capturing)
```
In both scenarios, the event propagation follows the sequence based on the capturing or bubbling phase, as determined by the `useCapture` parameter. Keep in mind that the capturing phase occurs before the bubbling phase and that events propagate through the DOM hierarchy accordingly.
### Event Propagation Cycle
The event propagation cycle refers to the sequence of phases through which an event travels within the Document Object Model (DOM) hierarchy. There are two main phases in the event propagation cycle: the capturing phase and the bubbling phase. Here's an overview of the cycle:
1. **Capturing Phase:**
- The event starts at the root of the DOM tree (typically the `<html>` element).
- The event travels down the DOM tree through each ancestor element of the target element.
- During this phase, event handlers registered with the capturing phase (`useCapture` set to `true`) are triggered on each ancestor element in the order they appear in the hierarchy from the root to the target.
- The event reaches the target element.
2. **Target Phase:**
- The event reaches the target element for which the event was triggered.
- Event handlers registered on the target element are executed.
3. **Bubbling Phase:**
- After the target phase, the event travels back up the DOM tree in reverse order.
- Event handlers registered with the bubbling phase (`useCapture` set to `false`) are triggered on each ancestor element in the reverse order from the target to the root.
- The event eventually reaches the root of the DOM tree.
This cycle allows developers to define event listeners that respond to events at different levels of the DOM hierarchy. By using capturing and bubbling, you can efficiently manage and handle events on multiple elements without needing to attach individual listeners to each element.
Certainly, let's modify the example to demonstrate how to stop event propagation after the click event on the `#child` element using the `stopPropagation` method. Here's the updated code:
```javascript
const grandparent = document.querySelector('#grandparent');
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
grandparent.addEventListener('click', function(e) {
console.log('Grandparent clicked (Bubbling)');
});
parent.addEventListener('click', function(e) {
console.log('Parent clicked (Bubbling)');
});
child.addEventListener('click', function(e) {
console.log('Child clicked (Bubbling)');
e.stopPropagation(); // Stop propagation after clicking the child element
});
```
In this example, when you click on the `#child` element, the event propagation will be stopped after the event handler for the `#child` element executes. As a result, the event will not continue to bubble up to the parent and grandparent elements. Only the message "Child clicked (Bubbling)" will be logged to the console.
If you remove the line `e.stopPropagation();`, you'll see the standard bubbling behavior where the event continues to propagate, and you'll see all three messages in the console: "Child clicked (Bubbling)", "Parent clicked (Bubbling)", and "Grandparent clicked (Bubbling)".
Remember that stopping propagation can impact the expected behavior of event handling, so it should be used with caution and only when necessary.
Here are the problem statements for each of the machine coding round problems you mentioned:
1. **Star Rating Component:**
Design a star rating component that allows users to rate something using stars. The component should display a visual representation of the rating using filled and empty stars. Users can click on the stars to select a rating.
2. **Counter Component:**
3. **Nested Comment System:**
Build a nested comment system where users can leave comments on a post. Each comment can have replies, creating a nested structure. Users should be able to reply to comments and collapse/expand comment threads.
4. **Product Card Component:**
Design a product card component that displays information about a product. It should include details like the product name, image, price, and a "Add to Cart" button. Users can click the button to add the product to their cart.
5. **OTP Input Section:**
Implement an OTP (One-Time Password) input section where users receive an OTP via SMS or email and need to enter it to verify their identity. The component should provide a field for each digit of the OTP and handle the validation process.
For each problem, you'll need to design and implement a solution using the programming language of your choice. Make sure to consider the user experience, error handling, and any other relevant aspects. Depending on the context of the coding round, you might be required to write modular and efficient code, handle edge cases, and possibly interact with a user interface (if applicable).
### Creating a counter component
Firstly we will be discussing the problem statement of Creating a counter component that displays a number and has buttons to increment and decrement the number. The user should be able to click the buttons to increase or decrease the displayed number.
Certainly, here's a simple implementation of a counter app using HTML, CSS, and JavaScript that includes the functionalities you mentioned:
**HTML (index.html):**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Counter App</title>
</head>
<body>
<div class="counter">
<button class="btn" id="decrement">-</button>
<span id="count">0</span>
<button class="btn" id="increment">+</button>
<button class="btn" id="reset">Reset</button>
</div>
<script src="script.js"></script>
</body>
</html>
```
**CSS (styles.css):**
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.counter {
display: flex;
align-items: center;
}
.btn {
padding: 10px 15px;
font-size: 18px;
background-color: #3498db;
color: #fff;
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
.btn:hover {
background-color: #2980b9;
}
```
**JavaScript (script.js):**
```javascript
const decrementButton = document.getElementById('decrement');
const incrementButton = document.getElementById('increment');
const resetButton = document.getElementById('reset');
const countDisplay = document.getElementById('count');
let count = 0;
decrementButton.addEventListener('click', () => {
if (count > 0) {
count--;
countDisplay.textContent = count;
}
});
incrementButton.addEventListener('click', () => {
count++;
countDisplay.textContent = count;
});
resetButton.addEventListener('click', () => {
count = 0;
countDisplay.textContent = count;
});
```
In this implementation, we have an HTML structure with buttons for decrementing, incrementing, and resetting the counter. The JavaScript code adds event listeners to these buttons to handle their respective functionalities. The counter value is displayed and updated in the `countDisplay` element. The CSS styles provide a basic look for the counter app.
Copy and paste the HTML, CSS, and JavaScript code into separate files (index.html, styles.css, script.js) in the same directory, and then open the index.html file in a web browser to see and interact with the counter app.
### Star Rating Component
Firstly we will be discussing the problem statement of Designing a star rating component that allows users to rate something using stars. The component should display a visual representation of the rating using filled and empty stars. Users can click on the stars to select a rating..
Absolutely, here's an explanation for the star rating component that includes comments in the JavaScript code to help you understand how it works:
**index.html:**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Star Rating Component</title>
</head>
<body>
<div class="rating">
<!-- Display five stars, each with a data-value attribute representing its rating value -->
<span id="stars">
<span class="star" data-value="1">&#9733;</span>
<span class="star" data-value="2">&#9733;</span>
<span class="star" data-value="3">&#9733;</span>
<span class="star" data-value="4">&#9733;</span>
<span class="star" data-value="5">&#9733;</span>
</span>
<!-- Display the current rating -->
<p>Rating: <span id="rating">0</span>/5</p>
</div>
<script src="script.js"></script>
</body>
</html>
```
**script.js:**
```javascript
// Get all star elements
const stars = document.querySelectorAll('.star');
// Get the rating display element
const ratingDisplay = document.getElementById('rating');
// Add a click event listener to each star
stars.forEach(star => {
star.addEventListener('click', () => {
// Get the value from the data-value attribute
const value = parseInt(star.getAttribute('data-value'));
// Update the rating display and stars based on the clicked value
updateRating(value);
});
});
// Function to update the rating display and filled stars
function updateRating(value) {
stars.forEach(star => {
// Get the value from the data-value attribute
const starValue = parseInt(star.getAttribute('data-value'));
// Toggle the 'filled' class based on whether the star's value is less than or equal to the selected value
star.classList.toggle('filled', starValue <= value);
});
// Update the rating display text content
ratingDisplay.textContent = value;
}
```
**styles.css:**
```css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.rating {
text-align: center;
}
.star {
font-size: 24px;
cursor: pointer;
transition: color 0.3s ease;
}
.star.filled {
color: gold;
}
```
In this star rating component, users can click on stars to indicate their rating. The JavaScript code adds a click event listener to each star, and the `updateRating` function toggles the "filled" class on stars based on their value compared to the selected rating. The CSS styles provide visual feedback by changing the color of filled stars to gold.

View File

@@ -0,0 +1,269 @@
## Agenda
- What is Event Delegation?
- Relation with concept of Bubbling
- Machine coding question
- Star Rating Component
- Counter Component
We will try to cover most of these topics in today's sessions and the remaining in the next.
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## Event delegation
Event delegation and event bubbling are closely related concepts that work hand-in-hand to optimize event handling in web development. When applied together, they provide an efficient way to manage interactions on a large number of elements.
Imagine you're browsing a website like Amazon, and on the product listing page, there are multiple product cards, each containing an "Add to Cart" button. Here's how event delegation and event bubbling come into play:
1. **Event Delegation:**
Event delegation involves attaching a single event listener to a common ancestor element (in this case, the container holding the product cards). Instead of placing event listeners on each "Add to Cart" button individually, you attach one listener to the container.
2. **Event Bubbling:**
Event bubbling refers to the natural propagation of an event through the DOM hierarchy. When an event occurs on a deeply nested element, it first triggers the event handler on that element and then "bubbles up" through its ancestors.
Now, let's see how these concepts work together:
- When a user clicks the "Add to Cart" button on a product card, the event starts at the button (target) and then bubbles up through its parent elements.
- Since you've attached a single event listener to the container holding the product cards, the event bubbles up to the container.
- The event listener captures the event at the container level and checks whether the clicked element (the button) matches certain criteria (e.g., having the class `add-to-cart`).
- If the criteria are met, the listener knows that an "Add to Cart" action is intended and can extract information about the specific product from the event's context
Let's go through an example with code to demonstrate event delegation and event bubbling using different categories of products (headphones, laptops, mobiles) on a web page:
**HTML Structure:**
```html
<div id="categories">
<div class="category" id="headphones">
<h2>Headphones</h2>
<div class="product">Product A</div>
<div class="product">Product B</div>
</div>
<div class="category" id="laptops">
<h2>Laptops</h2>
<div class="product">Product X</div>
<div class="product">Product Y</div>
</div>
<div class="category" id="mobiles">
<h2>Mobiles</h2>
<div class="product">Product P</div>
<div class="product">Product Q</div>
</div>
</div>
```
**JavaScript:**
```javascript
const categoriesContainer = document.getElementById('categories');
categoriesContainer.addEventListener('click', (event) => {
const clickedElement = event.target;
// Check if the clicked element is a product
if (clickedElement.classList.contains('product')) {
const category = clickedElement.closest('.category').querySelector('h2').textContent;
const product = clickedElement.textContent;
console.log(`Clicked on ${product} in the ${category} category.`);
// Handle the click action for the product here
}
});
```
In this example:
- The `categoriesContainer` element is the common ancestor for all categories and products.
- The event listener is attached to the `categoriesContainer` to capture clicks on any of its child elements.
- When a product is clicked, the event bubbles up through the category section, reaching the `categoriesContainer`.
- The listener checks if the clicked element has the class `product`. If it does, it extracts the category and product information and performs the necessary action.
- This code efficiently handles clicks on products within any category, demonstrating the combined usage of event delegation and event bubbling.
With this setup, regardless of the number of categories or products, you only need one event listener to handle all clicks, making your code more maintainable and efficient.
Let's create an example where event delegation is used to change the background color of elements by clicking on them. In this example, we'll create a set of colored boxes, and clicking on any box will change its background color using event delegation.
**HTML Structure:**
```html
<div id="colorPalette">
<div class="color-box" style="background-color: red;"></div>
<div class="color-box" style="background-color: green;"></div>
<div class="color-box" style="background-color: blue;"></div>
</div>
```
**JavaScript:**
```javascript
const colorPalette = document.getElementById('colorPalette');
colorPalette.addEventListener('click', (event) => {
const clickedElement = event.target;
// Check if the clicked element is a color box
if (clickedElement.classList.contains('color-box')) {
const color = clickedElement.style.backgroundColor;
document.body.style.backgroundColor = color;
}
});
```
In this example:
- The `colorPalette` element is the common ancestor for all color boxes.
- The event listener is attached to the `colorPalette` to capture clicks on any of its child elements.
- When a color box is clicked, the event bubbles up to the `colorPalette`.
- The listener checks if the clicked element has the class `color-box`. If it does, it extracts the background color of the clicked color box.
- The background color of the `body` element is then set to the extracted color, effectively changing the page's background color.
This example demonstrates how event delegation can be used to efficiently manage interactions across a set of elements, in this case, color boxes. By using event delegation, you handle all color box clicks with a single event listener, making the code cleaner and more maintainable.
Here are the problem statements for each of the machine coding round problems you mentioned:
1. **Star Rating Component:**
Design a star rating component that allows users to rate something using stars. The component should display a visual representation of the rating using filled and empty stars. Users can click on the stars to select a rating.
2. **Counter Component:**
3. **Nested Comment System:**
Build a nested comment system where users can leave comments on a post. Each comment can have replies, creating a nested structure. Users should be able to reply to comments and collapse/expand comment threads.
4. **Product Card Component:**
Design a product card component that displays information about a product. It should include details like the product name, image, price, and a "Add to Cart" button. Users can click the button to add the product to their cart.
5. **OTP Input Section:**
Implement an OTP (One-Time Password) input section where users receive an OTP via SMS or email and need to enter it to verify their identity. The component should provide a field for each digit of the OTP and handle the validation process.
### Nested Comment System
Firstly we will be discussing the problem statement of building a nested comment system where users can leave comments on a post. Each comment can have replies, creating a nested structure. Users should be able to reply to comments and collapse/expand comment threads.
**Nested Comment System: Overview**
A nested comment system allows users to leave comments on a post, and each comment can have replies, forming a threaded or hierarchical structure. Users should also be able to collapse and expand comment threads for better readability. This is commonly seen on social media platforms and discussion forums.
- **Nested Structure:** Comments are organized hierarchically, where each comment can have zero or more child comments (replies).
- **Collapse/Expand:** Users can collapse or expand comment threads to show or hide replies, improving readability for lengthy discussions.
- **Event Delegation:** Since comments can be added dynamically, event delegation is useful for handling interactions like replying and collapsing.
**HTML Structure:**
```<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Nested Comments Example</title>
</head>
<body>
<div id="comments">
<div class="comment">
<p>User A: This is a great post!</p>
<button class="reply-btn">Reply</button>
<button class="collapse-btn">Collapse</button>
<div class="replies">
<div class="comment">
<p>User B: I agree!</p>
<button class="reply-btn">Reply</button>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
```
**CSS:**
```css
.comment {
margin: 10px;
border: 1px solid #ccc;
padding: 10px;
}
.reply-btn,
.collapse-btn {
margin-right: 10px;
}
.all-comment:not(:first-child) {
margin-left: 4rem;
}
```
**JavaScript:**
```javascript
const commentsContainer = document.getElementById('comments');
commentsContainer.addEventListener('click', (event) => {
const clickedElement = event.target;
// Handle replying to comments
if (clickedElement.classList.contains('reply-btn')) {
const comment = clickedElement.closest('.comment');
const replyBox = document.createElement('div');
replyBox.className = 'comment reply';
replyBox.innerHTML = `
<p><input type="text" placeholder="Your reply..."></p>
<button class="submit-btn">Submit</button>
`;
comment.querySelector('.replies').appendChild(replyBox);
}
// Handle collapsing/expanding comment threads
if (clickedElement.classList.contains('collapse-btn')) {
const comment = clickedElement.closest('.comment');
const replies = comment.querySelector('.replies');
replies.style.display = replies.style.display === 'none' ? 'block' : 'none';
}
});
commentsContainer.addEventListener('click', (event) => {
const clickedElement = event.target;
// Handle submitting replies
if (clickedElement.classList.contains('submit-btn')) {
const replyInput = clickedElement.closest('.comment.reply').querySelector('input');
const replyText = replyInput.value;
const newReply = document.createElement('div');
newReply.className = 'comment';
newReply.innerHTML = `<p>User: ${replyText}</p>`;
clickedElement.closest('.comment.reply').appendChild(newReply);
replyInput.value = '';
}
});
```
**Explanation:**
- The HTML structure represents a simple comment system with reply and collapse buttons.
- The CSS provides basic styling for comments and buttons.
- The JavaScript code uses event delegation on the `commentsContainer`.
- When the "Reply" button is clicked, a new reply box is dynamically added under the corresponding comment.
- When the "Collapse" button is clicked, the replies are toggled to show or hide.
This example demonstrates a basic nested comment system. Depending on your requirements, you can expand this system to include user authentication, data storage, and more advanced features.
Assuming you integrate the provided HTML, CSS, and JavaScript code and open the HTML file in a web browser, here's what you can expect:
1. The page will display a comment with a "Reply" button and a "Collapse" button.
2. Clicking the "Reply" button will create a new reply box beneath the comment where users can input their replies.
3. Clicking the "Collapse" button will toggle the visibility of the replies.
For the sake of visualization, imagine that you see a page with a comment, and when you click the "Reply" button, a new reply box appears under the comment. If you click the "Collapse" button, the replies will be hidden, and clicking it again will show them again.
Please note that you'll need to create an HTML file and include the provided HTML, CSS, and JavaScript code within appropriate sections (HTML, `<style>` for CSS, and `<script>` for JavaScript) for this to work as intended.
In this example, the CSS rule .all-comment:not(:first-child) targets all elements with the class .all-comment except for the first one, and applies a left margin of 4rem to create an indentation effect. However, I noticed that your HTML structure uses the class .comment for comments, so I've updated the rule in the CSS accordingly.
``` css
.all-comment:not(:first-child){
margin-left:4rem;
}
```
You can place this CSS rule in your styles.css file to achieve the desired indentation for nested comments. Just ensure that the structure of your HTML matches the example provided.

View File

@@ -0,0 +1,478 @@
## Agenda
* JS refresher
* TypeOf Operator
* Objects and JSON
* JS Code Execution - Hoisting and Execution Context
* Hoisting
* Execution Context
* let, var, and const:
* Shadowing: legal and illegal
---
JavaScript is a dynamically typed, high-level programming language commonly used in web development. It employs the V8 engine, written in C++, for high-performance execution. Despite its name, JavaScript is distinct from Java. Its dynamic nature allows variables to change types during runtime, offering flexibility in coding.
## Datatypes in JS
Certainly, here's the information about JavaScript data types:
**Primitive Data Types:**
1. Number
2. String
3. Null
4. Undefined
5. Boolean
**New Primitive Types:**
1. BigInt
2. Symbol
**Non-Primitive Types (Reference Types):**
1. Object
2. Functions
3. Arrays
**New Non-Primitive Types:**
1. Map
2. Set
3. WeakMap
4. WeakSet
Sure, here are the code snippets with explanations and their respective outputs:
1. **Numbers:**
```javascript
console.log(5 / 2); // Output: 2.5
```
Explanation: JavaScript performs the division operation, and the result is `2.5`, which is a floating-point number.
2. **Strings:**
```javascript
let age = 25;
let str1 = 'I am ' + age + " years old ";
console.log(str1); // Output: I am 25 years old
let templateString = `I am ${age} years old`;
console.log(templateString); // Output: I am 25 years old
```
Explanation: In the first part, string concatenation is used to create `str1`. In the second part, a template string with variable interpolation is used to create `templateString`, both resulting in the same output.
3. **Null and Undefined:**
```javascript
let myNull = null;
let myUndefined;
console.log(myNull); // Output: null
console.log(myUndefined); // Output: undefined
```
Explanation: `myNull` is explicitly set to `null`, while `myUndefined` is declared but not assigned a value, resulting in `undefined`.
4. **typeof Operator:**
```javascript
var a = 10;
console.log(typeof a); // Output: number
a = "string";
console.log(typeof a); // Output: string
a = { "name": "Jasbir" };
console.log(typeof a); // Output: object
```
Explanation: The `typeof` operator is used to determine the data type of the variable `a`. It returns `"number"`, `"string"`, and `"object"` based on the assigned value.
5. **typeof null and Array Check:**
```javascript
console.log(typeof null); // Output: object
let arr = [1, 2, 3, 4];
console.log(Array.isArray(arr)); // Output: true
```
Explanation: `typeof null` returns `"object"` (historical quirk), and `Array.isArray()` accurately checks whether `arr` is an array and returns `true`.
These code snippets demonstrate JavaScript's handling of data types and the use of the `typeof` operator and `Array.isArray()` to determine types and check arrays.
---
## Non Primitive
### function
Certainly, here's the code snippet related to functions and a brief explanation:
```javascript
// Function Definition
function fn(param1) {
console.log("Hello world!", param1);
return "Returned value";
}
// Function Call
let rVal = fn();
console.log("Return value:", rVal);
```
**Explanation:**
1. `function fn(param1)`: This is a function definition named `fn` that takes one parameter `param1`. Inside the function, it logs "Hello world!" along with the value of `param1` and then returns the string "Returned value."
2. `let rVal = fn();`: This line calls the `fn` function without passing any arguments. Since the function expects a parameter, `param1` inside the function will be `undefined`. It also captures the return value of the function in the variable `rVal`.
3. `console.log("Return value:", rVal);`: Finally, it logs the string "Return value:" along with the value of `rVal`, which is "Returned value."
Please note that calling `fn` without passing a value for `param1` will not result in an error, but `param1` will be `undefined` inside the function. If you want to pass a value when calling the function, you should do so like this: `fn("SomeValue");`.
### Objects and JSON
Here's the code snippet related to JavaScript objects and a brief explanation:
```javascript
// Object Definition
let cap = {
name: "Steve",
age: 34,
isAvenger: true,
key: "hello"
}
// Loop through Object Properties
for (let key in cap) {
console.log(key, " ", cap[key], " ", cap.key);
}
```
**Explanation:**
1. An object in JavaScript is a collection of key-value pairs where the key can be a number or a string, and the value can be any valid JavaScript data type.
2. `cap` is an object that represents an entity, possibly a character named "Steve." It contains properties like `name`, `age`, `isAvenger`, and `key`.
3. The `for...in` loop is used to iterate through the properties of the `cap` object. Inside the loop, `key` represents each property name, and `cap[key]` retrieves the value associated with that property.
4. The `console.log` statement within the loop logs the `key`, the corresponding value accessed using `cap[key]`, and `cap.key`. However, note that `cap.key` accesses the property named "key" specifically, whereas `cap[key]` dynamically accesses the property corresponding to the current `key` in the loop.
5. In JavaScript, you can access object properties using the dot notation (e.g., `cap.name`) or square brackets (e.g., `cap['last Name']`).
6. JSON (JavaScript Object Notation) is a widely used data interchange format that is based on the structure of JavaScript objects. JSON uses a similar syntax for key-value pairs but is typically used for data communication between systems.
This code snippet demonstrates the creation of a JavaScript object, accessing its properties using a loop and dot notation, and the dynamic nature of accessing properties using square brackets.
---
## JS Code Execution
### Call Stack:
The call stack is a data structure used in JavaScript to keep track of the currently executing function(s). It follows the Last-In-First-Out (LIFO) principle, which means that the most recently called function is the first one to complete. As functions are called, they are added to the stack, and as they complete, they are removed from the stack.
### Execution Context:
An execution context is a conceptual container that holds all the information related to the execution of a piece of code. Each function call creates a new execution context, which includes information like the function's variables, parameters, references to outer scopes, the value of `this`, and other internal details.
### Global Area:
The global area, also known as the global scope, is where global variables and functions are defined. It's the outermost scope in JavaScript. Code outside of any function is executed in the global scope. The global area has its own execution context.
### Breakdown of Code Execution:
Certainly, here's the code snippet you provided along with an explanation of the code's execution and expected output:
```javascript
let a = 10;
function fn() {
console.log("I am fn");
function inner() {
console.log("I am inner");
}
inner();
}
fn();
```
**Explanation:**
1. `let a = 10;`: This declares a variable `a` and assigns it the value `10`.
2. `function fn() { ... }`: This defines a function named `fn`. Inside `fn`, there's another function definition named `inner`.
3. `console.log("I am fn");`: When the `fn` function is called, it logs "I am fn" to the console.
4. `function inner() { ... }`: This defines an inner function named `inner` within the `fn` function.
5. `inner();`: Inside the `fn` function, `inner()` is called. This means the "I am inner" message will be logged to the console.
6. `fn();`: Finally, the `fn` function is called, which in turn calls `inner()`. So, both "I am fn" and "I am inner" messages will be logged to the console.
**Expected Output:**
```
I am fn
I am inner
```
This code demonstrates the concept of nested functions. The `fn` function contains an inner function `inner`, and when `fn` is called, it executes both its own code and the code within `inner`, resulting in both log messages being displayed in the console.
---
### JS Code Execution Quiz
Consider the following JavaScript code:
```javascript
function real() {
console.log("I am real. Always run me");
}
function real() {
console.log("No I am real one ");
}
real();
function real() {
console.log("You both are wasted");
}
```
What will be the output when the code is executed?
- [ ] "No I am real one"
- [x] "You both are wasted"
- [ ] Error
### Explanation
In JavaScript, when you declare multiple functions with the same name, only the last one defined will be retained due to hoisting. Therefore, in this code:
1. The first two function declarations are overwritten by the last one.
1. The real() function is called, and the last version of the function's body is executed, which logs "You both are wasted" to the console.
Hence, the correct answer is B) "You both are wasted".
---
## Execution of the code in JS
It seems like you're describing the code execution process in JavaScript and how execution contexts (EC) work. Here's a breakdown of your explanation:
1. **Code Execution Process:**
- Code in JavaScript is executed within execution contexts (ECs).
2. **Global Code Execution (GEC):**
- The global code is executed in the Global Execution Context (GEC). It's the top-level context where the entire script starts.
3. **Inside a Function (Function Execution Context):**
- When a function is called, its code is executed within its own Function Execution Context.
4. **Execution Context Creation (EC Creation):**
- During EC creation, JavaScript performs hoisting, which involves memory allocation for variables (initialized with `undefined`) and function declarations (fully allocated).
- It also sets up the outer scope (if any) and determines the value of the `this` keyword.
5. **Global Object (Browser/Node.js):**
- In a browser environment, the global object is `window`. In Node.js, it's `global`. These objects contain global variables and functions.
6. **Outer Scope:**
- Each execution context has access to variables and functions in its outer (enclosing) scope.
7. **`this` Keyword:**
- The `this` keyword is determined based on the context in which a function is executed. Its value varies depending on how the function is called.
8. **Execution of Code:**
- After EC creation, the code within the context is executed. Variables and functions are accessible, and the program flows according to the code logic.
This explanation outlines the sequence of events when JavaScript code is executed, starting with EC creation, hoisting, and variable/function allocation, and then proceeding with the actual code execution. Understanding execution contexts is essential to grasp how JavaScript manages variable scope and function execution.
**Code Snippet 1:**
```javascript
var a=10;
real();
function real() { console.log("I am real. Always run me"); }
```
**Explanation:**
1. `var a=10;`: Declares a variable `a` and assigns it the value `10`.
2. `real();`: Calls the function `real()`.
3. `function real() { console.log("I am real. Always run me"); }`: Defines a function `real()` that logs a message to the console.
**Expected Output:**
```
I am real. Always run me
```
In this case, the function `real()` is defined before it is called, so it executes without any issues.
**Code Snippet 2:**
```javascript
let a = 10;
function fn() {
console.log("a", a);
}
fn();
```
**Explanation:**
1. `let a = 10;`: Declares a variable `a` using `let` and assigns it the value `10`. This variable is in the outer/global scope.
2. `function fn() { console.log("a", a); }`: Defines a function `fn()` that logs the value of `a` to the console. It captures the value of `a` from its outer scope.
3. `fn();`: Calls the function `fn()`.
**Expected Output:**
```
a 10
```
In this case, `fn()` accesses the variable `a` from its outer scope (the global scope) and logs its value, which is `10`.
---
## Let, Var and Const
Certainly, let's break down the provided code snippet step by step and explain the output:
```javascript
let a = 10;
console.log("line number 2", a);
function fn() {
let a = 20;
console.log("line number 4", a);
a++;
console.log("line number 7", a);
if (a) {
let a = 30;
a++;
console.log("line number 11", a);
}
console.log("line number 13", a);
}
fn();
console.log("line number 16", a);
```
**Explanation:**
1. `let a = 10;`: Declares a variable `a` in the outer/global scope and assigns it the value `10`.
2. `console.log("line number 2", a);`: Logs the value of `a` in the global scope, which is `10`.
3. `function fn() { ... }`: Defines a function named `fn()`.
4. Inside the `fn` function:
- `let a = 20;`: Declares a new variable `a` in the function scope (local to the function) and assigns it the value `20`.
- `console.log("line number 4", a);`: Logs the value of the local `a`, which is `20`.
- `a++;`: Increments the local `a` to `21`.
- `console.log("line number 7", a);`: Logs the updated local `a`, which is `21`.
5. Inside the `if` block:
- `let a = 30;`: Declares a new variable `a` with block scope (inside the `if` block) and assigns it the value `30`.
- `a++;`: Increments the block-scoped `a` to `31`.
- `console.log("line number 11", a);`: Logs the block-scoped `a`, which is `31`.
6. After the `if` block, but still within the function:
- `console.log("line number 13", a);`: Logs the function-scoped `a`, which is `21`.
7. `fn();`: Calls the `fn()` function.
8. `console.log("line number 16", a);`: Logs the global `a` value, which is `10`.
**Expected Output:**
```
line number 2 10
line number 4 20
line number 7 21
line number 11 31
line number 13 21
line number 16 10
```
![](https://hackmd-prod-images.s3-ap-northeast-1.amazonaws.com/uploads/upload_7f5b2092f952c2202a13120d67117094.png?AWSAccessKeyId=AKIA3XSAAW6AWSKNINWO&Expires=1695283757&Signature=iN9PTmJ8GTyZ0k4JH4%2BSWH%2F5Y4I%3D)
This code demonstrates variable scoping in JavaScript, including global, function, and block scope. Variables with the same name declared in different scopes do not interfere with each other. Block-scoped `let` variables are confined to the block in which they are declared, and the variable declared inside the `if` block doesn't affect the function-scoped or global `a`.
---
## Shadowing
**Variable Shadowing** occurs when a variable declared within a certain scope has the same name as a variable declared in an outer scope. This can lead to confusion and unexpected behavior, especially when accessing or modifying the variables. Let's explore legal and illegal cases of variable shadowing in more detail.
Let's go through the provided code snippets step by step and explain the output for each one:
**Code Snippet 1:**
```javascript
let fruits = "apple";
console.log(fruits); // apple
{
console.log(fruits); // ReferenceError: Cannot access 'fruits' before initialization (Temporal Dead Zone - TDZ)
let fruits;
console.log(fruits); // undefined
fruits = "orange";
{
console.log(fruits); // orange
}
console.log(fruits); // orange
}
console.log(fruits); // apple
```
**Explanation:**
1. A variable `fruits` is declared and initialized with the value `"apple"` in the global scope.
2. Inside a block, a `console.log(fruits)` statement attempts to log the value of `fruits` before it's declared within that block. This results in a `ReferenceError` because of the Temporal Dead Zone (TDZ) for `let` variables.
3. A new `let` variable named `fruits` is declared within the block. It's initially in the TDZ, so `console.log(fruits)` logs `undefined`.
4. `fruits` is assigned the value `"orange"`.
5. Inside a nested block, `console.log(fruits)` logs `"orange"` because it refers to the block-scoped `fruits`.
6. Outside the innermost block, `console.log(fruits)` still logs `"orange"` because it refers to the most recent block-scoped `fruits`.
7. Finally, outside the block, `console.log(fruits)` logs the global `fruits` value, which is `"apple"`.
**Expected Output:**
```
apple
ReferenceError: Cannot access 'fruits' before initialization (Temporal Dead Zone - TDZ)
undefined
orange
orange
apple
```
**Code Snippet 2:**
```javascript
var fruits = "apple";
console.log("21", fruits); // apple
{
let fruits;
fruits = "orange";
console.log("25", fruits); // orange
{
let fruits;
console.log("28", fruits); // undefined
}
console.log(fruits); // orange
}
console.log(fruits); // apple
```
**Explanation:**
1. A variable `fruits` is declared and initialized with the value `"apple"` using `var` in the global scope.
2. Inside a block, a new `let` variable named `fruits` is declared, shadowing the global `fruits`. It is assigned the value `"orange"` within this block.
3. Inside a nested block, another `let` variable named `fruits` is declared. This variable shadows the outer block-scoped `fruits`. It is not initialized within this block, so it logs `undefined`.
4. Outside the innermost block, `console.log(fruits)` logs the block-scoped `fruits`, which is `"orange"`.
5. Finally, outside the block, `console.log(fruits)` logs the global `fruits`, which is `"apple"`.
**Expected Output:**
```
21 apple
25 orange
28 undefined
orange
apple
```
In this code, variable shadowing occurs when a variable with the same name is declared inside a nested block, shadowing variables from outer scopes. The behavior differs between `var` and `let` declarations, as demonstrated in the code snippets.

View File

@@ -0,0 +1,435 @@
# Agenda
- Arrays in depth
- Objects in depth
- Functions in depth
- Strings in depth
- More differences between let, var and const.
# Arrays
The array is a data structure to store multiple values of multiple data types. The elements are stored in an indexed manner.
## Accessing array element
Any array element can be accessed by an index. If we want to access the fifth element of an array named `arr`, then we have to simply write `arr[4]`, as the array works on 0-indexing.
```javascript=
let arr = [1, 'Scaler', true, undefined, null, [1, 2, 3]]
console.log(arr)
// access an element with index from an array
console.log(arr[4]) // print null
let d=arr[5]
console.log(d) // print [1, 2, 3]
console.log(d[0]) // print 1
```
## Changing an array element
We can change the value of any element by its index, if we want to change the value of the fourth element of an array named `arr`, then we need to simply write `arr[3]='new value'`
```javascript=
let arr = [1, 'Scaler', true, undefined, null, [1, 2, 3]]
console.log(arr) // print [1, 'Scaler', true, undefined, null, [1, 2, 3]]
// change an array element to a different value
arr[3] = 'Mrinal'
arr[4] = 700
console.log(arr) //print [1, 'Scaler', true, 'Mrinal', 700, [1, 2, 3]]
```
## Length of an array
Gives the length of the array, length means the total number of elements in an array.
```javascript=
let arr = [1, 'Scaler', true, undefined, null, [1, 2, 3]]
console.log(arr.length) // print 6 as there are a total of 6 elements in an array.
```
## Array methods
**Push Method:**
Inserting an element into an array at the end
```javascript=
let cars = ['swift', 'BMW', 'Audi']
console.log(cars) // print ['swift', 'BMW', 'Audi']
cars.push('Urus')
console.log(cars) // print ['swift', 'BMW', 'Audi', 'Urus']
```
**Pop Method:**
Delete the element from the end of the array
```javascript=
let cars = ['swift', 'BMW', 'Audi', 'Urus']
console.log(cars) // print ['swift', 'BMW', 'Audi', 'Urus']
cars.pop()
console.log(cars) // print ['swift', 'BMW', 'Audi']
```
Popped elements can also be stored in another variable.
```javascript=
let cars = ['swift', 'BMW', 'Audi', 'Urus']
var removedElement = cars.pop()
console.log(removedElement) // print Urus
```
**Unshift Method**
Insert an element at the start of an array(0th index).
```javascript=
let cars = ['swift', 'BMW', 'Audi']
console.log(cars) // print ['swift', 'BMW', 'Audi']
cars.unshift('Urus')
console.log(cars) // print ['Urus', 'Swift', 'BMW', 'Audi']
```
**Shift Method**
Remove the 0th index element of an array.
```javascript=
let cars = ['swift', 'BMW', 'Audi', 'Urus']
console.log(cars) // print ['swift', 'BMW', 'Audi', 'Urus']
cars.shift()
console.log(cars) // print ['BMW', 'Audi', 'Urus']
```
Shifted elements can also be stored in another variable.
```javascript=
let cars = ['swift', 'BMW', 'Audi', 'Urus']
var removedElement = cars.shift()
console.log(removedElement) // print swift
```
# Loops
Loops are used with arrays when we want to perform similar operations on all the elements of an array.
## Example
**Example 1:** To print the square of every element of an array.
```javascript=
let arr = [1, 2, 3, 4, 5]
for(let i=0;i<arr.length;i++){
console.log(arr[i]*arr[i])
}
// print 1 4 9 16 25
```
**Example 2:** Storing the square of every element of an array in another array.
```javascript=
let arr = [1, 2, 3, 4, 5]
let squareArr = []
for(let i=0;i<arr.length;i++){
squareArr.push(arr[i]*arr[i])
}
console.log(squareArr) // print [1, 4, 9, 16, 25]
```
# Functions
The function is an abstract body and inside that body particular logic is written and that function expects some values which are known as parameters of the function. At the time of invoking the function we need to pass those values as an argument.
**Example of simple function**
```javascript=
// function accepting parameters
function ServeBeverage(drink, quantity){
console.log('I want '+ quantity + " " + drink)
}
// calling function by passing arguments
serveBeverage('coffee',4) // print I want 4 coffee
```
## Ways of defining function in javascript
In JavaScript, we have multiple ways of defining functions.
### Traditional way of writing function
We can define functions in a way similar used in another programming language for function definition.
```javascript=
function sayHi(){
console.log('mrinal says hi')
}
// calling function
sayHi()
```
### Function as Expressions(First class citizens)
We can write functions inside the variable in JavaScript. That's why it is known as first-class citizens.
**Example**
```javascript=
// Function as Expressions
let sayHi=function(){
console.log('mrinal says hi')
}
// calling function
sayHi()
```
### Arrow function
We can write the arrow function even without using the function keyword.
**Example**
```javascript=
// Arrow function
let sayBye=()=>{
console.log('mrinal says bye')
}
// calling function
sayBye()
```
# Execution context in javascript
How the javascript code gets executed.
```javascript=
var a = 2
var b = 3
function add(num1, num2){
var ans = num1+num2
return ans;
}
var addition = add(4, 5)
console.log(addition) // print 9
add(addition, 4)
let add1 = addition(a, b)
let add2 = addition(5, 6)
console.log(add1) // print 5
console.log(add2) // print 11
```
Javascript code executes two phases:
1. **Memory Allocation:** Every particular variable is assigned with undefined value initially. Initially, no value is assigned to the variable, only memory is allocated. And every function will get its whole body in this phase.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/084/original/upload_85139e0f5170f5e8eece1dd1782b44b7.png?1695318758)
2. **Code Execution:** Code will get executed in this phase. Now the values of variables are assigned. When the function is called and it starts executing then another memory allocation, the code block is created. For every function calling, another execution context will be created. And execution context of the whole program is known as the global execution context.
Variables declared inside the function are written inside the execution context of that particular function.
Execution context is created when the function is called and the answer is stored in the add1 variable.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/085/original/upload_1a31012a1e876a8f7c95aa73d0eef002.png?1695318823)
After completing the function execution, a particular function execution context returns the value. And we do not need that function execution context further.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/086/original/upload_d3e7d682244f9386192f25604345466b.png?1695318849)
Now again execution context is created when the add() function is called again and the return value of the function is stored in add2.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/087/original/upload_5d5e478c9528b7fe00cc0b799debdc76.png?1695318906)
After the completion of function execution, there is no requirement for that function execution context.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/088/original/upload_80a8e7c9979b2c4b7e9a6129459cfeca.png?1695318933)
Now the execution of the program is completed, now global function execution context is also not required further.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/089/original/upload_e27ec9b99f9a4fc64569b634d07f2a30.png?1695318988)
**Execution context for the below code**
```javascript=
var n = 3
function square(num){
var ans = num*num
return ans
}
let square1 = square(n)
```
- Firstly global execution context will be created.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/090/original/upload_4af9552ee646a0fe31bae98443b75adf.png?1695319026)
- Then the value to the n will be assigned.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/091/original/upload_bdeb77ccd5eed38c0a9afe636098474e.png?1695319049)
- Now execution context is created for the square method as it is called.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/092/original/upload_e409b43de48b1647ec498a5bb8cd358c.png?1695319076)
- Variables declared inside the function are initialized by undefined values in the function execution context.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/093/original/upload_4ee9ea62766996fd53d0645bd52785b3.png?1695319103)
- Now the value will be assigned to the variables of the function execution context.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/094/original/upload_30b10d1997acbd7260a784b6f639b4c2.png?1695319173)
- After that function execution context returns value to the global execution context and the function execution context is not required further.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/095/original/upload_2c00f2a7212c3e93e02fa480dcf396c1.png?1695319197)
- Now the program is executed completely so we do not need global execution context further.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/096/original/upload_fe64e69af8a31efb89e43340574b7b9d.png?1695319220)
# More differences between let, var and const.
```javascript=
var a = 4
function printName(){
console.log('my name is mrinal')
}
console.log(a)
printName()
```
**Output**
```
4
my name is mrinal
```
```javascript=
console.log(a)
printName()
var a = 4
function printName(){
console.log('my name is mrinal')
}
```
**Output**
```plaintext
undefined
my name is mrinal
```
**Explanation**
First is the memory allocation phase of the program, and initially all the variables are initialized with undefined values before program execution. In the memory allocation phase, a = undefined and fn printName() will be created. After that program execution phase will start, in which first the value of a printed i.e. undefined after the function is called, function execution context will be created and then the value is assigned to the variable.
```javascript=
console.log(a)
printName()
var a = 4
function printName(){
console.log('my name is mrinal')
}
let printAge = function(){
console.log(24)
}
printAge()
```
**Output**
```plaintext
undefined
my name is mrinal
24
```
```javascript=
console.log(a)
printName()
printAge()
var a = 4
function printName(){
console.log('my name is mrinal')
}
let printAge = function(){
console.log(24)
}
```
But now this program will give an error that printAge is not defined. We are getting this error as we are working with function as expression and printAge does not have a function during initialization. It has an undefined value during initialization.
**let**
```javascript=
console.log(a)
printName()
let a = 4
function printName(){
console.log('my name is mrinal')
}
let printAge = function(){
console.log(24)
}
printAge()
```
This program will give an error at line console.log(a) that `cannot access 'a' before initialization`. This is because let will first initialise your variable with temporal dead zone. Whenever a variable is created with the `let`, then that variable can not be accessed before initialisation means until the code execution phase is not started. Before code execution, it is in a temporal dead zone.
# Temporal dead zone(TDZ)
When you declare a variable with let or const, then these variables can not be accessible before their initialization and at this moment they will be in a temporal dead zone.
||var|let|const|
|:-:|:-:|:-:|:-:|
|TDZ|&cross;|&check;|&check;|
# Objects
Objects are basically in which data is stored in the form of key-value pairs.
## Example
```javascript=
let person ={
name: 'Mrinal',
age: 24,
phone: 1234567
}
console.log(person)
// dot notation
console.log(person.age)
// bracket notation
console.log(person['phone'])
```
We can store any kind of value in an object. We can also write a function inside the object using `:`. Another object can also be created within the object.
```javascript=
let captainAmerica ={
name : 'Steve Rogers',
age : 102,
// Array
allies : ['Tony', 'bruce', 'bucky']
// function inside an object
sayHi : function(){
console.log('Captain says hi')
}
// nested object
address :{
country : 'USA',
city : {
name : 'Brokkly',
pincode : 12345
}
}
isAvenger : true
}
// accessing age from captainAmerica object
console.log(captainAmerica.age) // print 102
// accessing element of array allies from captainAmerica object
console.log(captainAmerica.allies[1]) // print bruce
// accessing element from the nested object
console.log(captainAmerica.address.city) // print complete city object
console.log(captainAmerica.address.city.pincode) // print 12345
// changing some values of an object
captainAmerica.isAvenger=false
// adding new key-value in object
captainAmerica.movies=['End Game', 'Age of Ultorn', 'Civil War']
//The above statement will create a key with movies if it is not available in the object otherwise it will update the previous value of movies.
// calling function defined within an object
captainAmerica.sayHi()
//Deleting key from an object
delete captainAmerica.age
```

View File

@@ -0,0 +1,360 @@
# Today's Content
- Callback functions
- Higher Order Functions
- Writing clean code with higher-order functions.
- Array Methods(Map, Filter, Reduce, etc.)
- Interview Questions
# Functional Programming
Following are the types of programming paradigms:
- Procedural Programming Paradigm(eg:- C, )
- Object Oriented Paradigms(eg:- Java, C++)
- Functional Programming Paradigm(eg:- javascript)
## Callback Functions
These are the functions that can be passed to another function as an argument.
### Example
```javascript
function printName(cb){
console.log('Shikhar')
// calling received callback function
cb()
}
function printLastName(){
console.log('Singh')
}
function printAge(){
console.log(24)
}
printName(printLastName)
printName(printAge)
```
**Output:**
Shikhar
Singh
Shikhar
24
**Explanation:**
In`printName(printLastName)` statement, we are passing `printLastName` as a callback, and it is accepted by the `printName` as a `cb` argument. And when `cb()` is executed then the callback function is called.
**We can also pass multiple callback functions**
```javascript
function printName(cb1, cb2, cb3){
console.log('Shikhar')
cb1()
cb2()
cb3()
}
function printLastName(){
console.log('Singh')
}
function printAge(){
console.log(24)
}
function printAddress(){
console.log('Delhi')
}
printName(printLastName, printAge, printAddress)
```
**Output:**
Shikhar
Singh
24
Delhi
We can also pass the callback function within another callback function
# Coding question
We are given an array, which has the radius of different circles, we need to find the area, circumference and diameter for all the radiuses.
## Approach
We will simply create a different function for calculating area, circumference and diameter and in every function we will simply iterate over an array, and then store the calculated value in the result array, and return that array.
## PseudoCode
```javascript
let myRadiusArray = [2, 3, 4, 5, 8]
function calculateArea(radiusArr){
let result = []
for(let i = 0 ; i < radiusArr.length ; i ++ ){
result.push(3.14 * radiusArr[i] * radiusArr[i])
}
return result
}
let finalAreas = calculateArea(myRadiusArray)
console.log('This is area array => ', finalAreas)
function calculateCircumference(radiusArr){
let result = []
for(let i = 0 ; i < radiusArr.length ; i ++ ){
result.push( 2 * Math.PI * radiusArr[i])
}
return result
}
let finalCircumferences = calculateCircumference(myRadiusArray)
console.log('This is Circumference array =>', finalCircumferences)
function calculateDiameter(radiusArr){
let result = []
for(let i = 0 ; i < radiusArr.length ; i ++ ){
result.push(radiusArr[i] * 2)
}
return result
}
let finalDiameters = calculateDiameter(myRadiusArray)
console.log('This is Diameter array =>', finalDiameters)
```
**Output**
```plaintext
This is area array => [ 12.56, 28.259999999999998, 50.24, 78.5, 200.96 ]
This is Circumference array => [
12.566370614359172,
18.84955592153876,
25.132741228718345,
31.41592653589793,
50.26548245743669
]
This is Diameter array => [ 1, 1.5, 2, 2.5, 4 ]
```
## Better Approach
Here we can see that every function has the same structure, and here we are violating the dry principle.
**Dry Principle** says that do not repeat yourself.
While writing a code just try to write simple code, so that you do not need to repeat the same structure again and again. Now we will try to generalize this code. Let us try to solve this problem using a higher-order function.
# Higher Order Function
Higher-order functions are those functions where the function is passed as an argument. This means that which callback function is passed as an argument.
## Example
Here `printName()` is the higher-order function
```javascript
function printName(cb){
console.log('Shikhar')
// calling received callback function
cb()
}
function printLastName(){
console.log('Singh')
}
printName(printLastName)
```
# Solution of coding question Using Higher Order Function
Below is the javascript for the above coding question using a higher-order function.
```javascript
let myRadiusArray = [2, 3, 4, 5, 8]
function circleArea(radius){
return Math.PI * radius * radius;
}
function circleCircumference(radius){
return 2 * Math.PI * radius;
}
function circleDiameter(radius){
return 2 * radius;
}
function calculateArea(radiusArr, logic){
let result = []
for(let i = 0 ; i < radiusArr.length ; i ++ ){
result.push(logic(radiusArr[i]))
}
return result
}
let finalAreas = calculateArea(myRadiusArray, circleArea)
console.log('This is area array => ', finalAreas)
let finalCircumferences = calculateArea(myRadiusArray, circleCircumference)
console.log('This is Circumference array =>', finalCircumferences)
let finalDiameter = calculateArea(myRadiusArray, circleDiameter)
console.log('This is Diameter array =>', finalDiameters)
```
# Functional Programming
- Never manipulate or change your original array.
# map
Let us suppose we are given an array of numbers and we want the square of every number of an array. We can solve it by creating a new array for the result and iterating over the original array, to find the square of every element and add it to the result.
**Code**
```javascript
let arr = [1, 2, 3, 4, 5]
let SquareArr = []
for(let i = 0 ; i < arr.length ; i ++ ){
SquareArr.push(arr[i] * arr[i])
}
console.log(SquareArr)
```
We can also write this code for finding squares within the function.
```javascript
let arr = [1, 2, 3, 4, 5]
function squareArrFn(arr){
let SquareArr = []
for(let i = 0 ; i < arr.length ; i ++ ){
SquareArr.push(arr[i] * arr[i])
}
return SquareArr;
}
let squareArrFinal = squareArrFn(arr)
console.log(squareArrFinal)
```
For these questions, where we want to apply operations on every element of an array, then we can use the map. **,ap is a higher-order function which will not change the original array**. There is an unbuilt loop in a `map` which will take array elements one by one.
```javascript
let arr = [1, 2, 3, 4, 5]
let squaredValues = arr.map(function(num){
return num * num;
})
console.log(squaredValues)
```
## Checking how the map works
Suppose we have an array `arr = [1, 2, 3, 4, 5]`, and we want a square of all the elements of an array. `map` first look at the array, then it will take callback function and traverse every element of an array and apply an operation on it.
We can see that long code can be easily converted into small code using `map`.
**Using a map to find an area for the given radiuses in the form of an array**
```javascript
let radiusArr = [1, 2, 3, 4]
let areaArr = radiusArr.map(function(num){
return Math.PI * num * num;
})
console.log(areaArr)
```
# Problem Statement
You are given a transaction array treat the transaction amount in rupees, and convert those amounts into dollars and conversion rate is also provided to us.
## Idea
We can use the `map` to apply similar operations to all the elements of an array.
## PseudoCode
```javascript
const transactions = [1000, 3000, 4000, 2000, - 898, 3800, - 4500];
const inrtToUsd = 80;
let conversionToDollars = transactions.map(function(amount){
return amount / inrtToUsd;
})
console.log(conversionToDollars)
```
# filter
`filter` is a higher-order function that will work based on a condition and will only have the values inside the result array for which the condition is satisfied.
The `filter` will work like a `map`, but in the `map`, we will operate all the elements of an array. But in the `filter`, we will apply some conditions to the elements of an array.
The `filter` will work in boolean(true/false) values.
## Example
We are given an array of numbers that contains both even and odd numbers and we need an array which only contains the even numbers of the input array.
**Solution:** If the remainder of the number on dividing it by 2 is zero(`element % 2 == 0`), then it is an even number. Here we can use the filter. Here we have created `evenArray` and used a filter on `myArr`, so the elements that satisfy the condition will only be added to the `evenArray`.
```javascript
let myArr = [1, 2, 5, 7, 8, 2, 6, 9, 13, 17]
let evenArray = myArr.filter(function(num){
return num % 2 == 0;
})
console.log(evenArray)
```
**Output:**
[2, 8, 2, 6]
# Problem Statement
You are given a transaction array, and use a `filter` to find the positive transaction amounts
## Solution
We want only positive values, so positive values are always greater than 0, so we will apply this condition using the `filter`.
## PseudoCode
```javascript
const transactions = [1000, 3000, 4000, 2000, - 898, 3800, - 4500];
let positiveValue = transactions.filter(function(amount){
return amount > 0;
})
console.log(positiveValue)
```
**Output:**
[1000, 3000, 4000, 2000, 3800]
# Problem Statement
You are given an array of numbers and you need to calculate the sum of all the elements of an array.
## Solution
We will define a variable `sum`, initialize it with 0, iterate over the array and add elements one by one to `sum` the variable.
```javascript
let arr = [1, 2, 3, 4, 5]
let sum = 0
for(let i = 0 ; i < arr.length ; i ++ ){
sum = sum + arr[i]
}
console.log(sum)
```
**Output:**
15
# reduce
Just like above we have reduced all the array elements into one value i.e. sum, so basically `reduce` is used to reduce multiple elements into a single one.
## Example
Suppose we want to solve the above question using `reduce`, which means we want to find the sum of all elements of an array using `reduce`.
```javascript
let arr = [1, 2, 3, 4, 5]
let totalSum = arr.reduce(function(acc, num){
acc = acc + num
return acc
},0)
console.log(totalSum)
```
**Output:**
15
**Explanation:**
Here 0 written after `}` is the initialising value of `acc`, this means `acc` will be initiated with 0, and `acc` is used to store the sum and `num` is the current element of an array at every iteration and at every iteration, `num` is added to `acc`.

View File

@@ -0,0 +1,392 @@
## Agenda
**Topics to cover in Javascript Oops**
* This keyword
* Constructor functions
* Classes & Classical Inheritance
* Prototype & Prototypal Inheritance
* Call ,Apply & Bind Methods
* Memory of objects (Reference Datatypes)
We will try to cover most of these topics in today's sessions and the remaining in the next.
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## This Keyword
Certainly! When covering Object-Oriented Programming (OOP) in JavaScript, the `this` keyword is a crucial concept to understand. Here's a breakdown of topics you can cover regarding the `this` keyword in JavaScript and its relevance to OOP:
1. **Introduction to `this` keyword:**
- Explain that `this` is a special keyword in JavaScript that refers to the current execution context or the current object.
- Mention that its value depends on how a function is called, rather than where it is defined.
2. **Global Context:**
- Describe that in the global context (outside of any function), `this` refers to the global object (e.g., `window` in browsers, `global` in Node.js).
3. **Function Context:**
- Explain that within a function, the value of `this` can change based on how the function is invoked.
- Discuss function invocation methods:
- Regular function invocation: `this` usually refers to the global object (in non-strict mode) or `undefined` (in strict mode).
- Method invocation: `this` refers to the object that the method is called on.
- Constructor invocation: `this` refers to the instance being created by the constructor function.
- `call()` and `apply()` methods: Explicitly set `this` for a function call.
- Arrow functions: `this` retains the value of the enclosing lexical context.
4. **Object-Oriented Programming (OOP) and `this`:**
- Explain how `this` is used in the context of OOP to refer to the instance of an object.
- Describe how methods within an object can access other properties and methods using `this`.
5. **Common `this` Pitfalls:**
- Describe issues that developers often face with `this`:
- Losing `this` context in nested functions.
- Using `this` in callbacks without proper binding.
- Caching `this` in a variable to ensure proper reference in nested scopes.
6. **Solving `this` Issues:**
- Introduce solutions to common `this` problems:
- Using `.bind()`, `.call()`, or `.apply()` to explicitly set `this`.
- Storing a reference to the outer `this` in a variable to use within nested functions.
- Using arrow functions to preserve the lexical scope's `this`.
7. **Examples and Demonstrations:**
- Provide code examples for each type of function invocation and how `this` behaves in each case.
- Show scenarios where `this` is used within object methods to access object properties.
8. **Best Practices:**
- Emphasize the importance of understanding the context in which `this` is used.
- Encourage using arrow functions in methods when you want to retain the outer scope's `this`.
- Suggest using modern ES6 features like classes to manage `this` more effectively.
9. **Browser vs. Node.js:**
- Mention the differences in the global object (`window` vs. `global`) and how they affect `this` behavior.
### `this` keyword in Node.js in non-strict mode
Certainly, let's break down the behavior of the `this` keyword in Node.js in non-strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: `fn` is a function defined globally, and `obj` is an object containing a method `fn`.
```javascript
// Scenario 1: Console.log(this)
console.log("Scenario 1:");
console.log(this); // Output: {}
// Scenario 2: Console.log(this) -> fn = global object
console.log("Scenario 2:");
function fnGlobal() {
console.log(this === global); // true
}
fnGlobal();
// Scenario 3: this -> obj -> fn = object itself
console.log("Scenario 3:");
var obj = {
fn: function () {
console.log(this === obj); // true
}
};
obj.fn();
// Scenario 4: this -> obj -> fn -> fn = global object
console.log("Scenario 4:");
var obj2 = {
fn: function () {
console.log(this === obj2); // true
var nestedFn = function () {
console.log(this === global); // true
};
nestedFn();
}
};
obj2.fn();
```
Now, let's summarize these scenarios in a tabular form:
| Scenario | Code | Output | Explanation |
|----------|-----------------------------------------|-------------------------------------------|----------------------------------------------------|
| 1 | `console.log(this);` | `{}` | In global context, `this` refers to the global object. |
| 2 | `function fnGlobal() {...}`<br>`fnGlobal();` | `true` (inside the function) | In a regular function, `this` refers to the global object. |
| 3 | `obj.fn = function() {...}`<br>`obj.fn();` | `true` (inside the method) | Inside an object method, `this` refers to the object itself. |
| 4 | `obj2.fn = function() {...}`<br>`obj2.fn();` | `true` (inside the method)<br>`true` (inside nested function) | Inside a nested function, `this` reverts to the global object. |
In scenarios 3 and 4, `this` refers to the object containing the method when that method is directly invoked. However, when a nested function is defined within the method and invoked within it, `this` inside the nested function refers to the global object (`global` in Node.js).
Understanding these behaviors helps in writing clean and predictable code, especially when dealing with methods and nested functions within objects.
### `this` keyword in Browser in non-strict mode
Certainly, let's break down the behavior of the `this` keyword in Browser in non-strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: `fn` is a function defined globally, and `obj` is an object containing a method `fn`.
Sure, let's break down each scenario and then summarize them in a tabular form.
**Scenario 1: `console.log(this)`**
```javascript
console.log(this); // Window Object
```
In this scenario, if you directly execute `console.log(this)` in the global context of a web browser (not within any function or object), the output will be the `Window` object. This is because `this` refers to the global object, which is the `Window` object in a browser environment.
**Scenario 2: `console.log(this)` inside a function**
```javascript
function exampleFunction() {
console.log(this);
}
exampleFunction(); // Window Object
```
In this case, when you call `exampleFunction()`, it's being invoked as a regular function. The `this` inside the function still refers to the global object (`Window` in the browser).
**Scenario 3: `this` inside an object method**
```javascript
var obj = {
prop: 'I am a property',
method: function() {
console.log(this.prop);
}
};
obj.method(); // "I am a property"
```
In this scenario, `obj` is an object containing a method named `method`. When you call `obj.method()`, the `this` inside the `method` refers to the `obj` itself. Therefore, `this.prop` accesses the `prop` property of the `obj` object.
**Scenario 4: `this` inside nested functions**
```javascript
var obj = {
prop: 'I am a property',
method: function() {
var nestedFunction = function() {
console.log(this.prop);
};
nestedFunction();
}
};
obj.method(); // undefined
```
Here, within the `nestedFunction`, `this` refers to the global object (`Window` in the browser). This is because the function `nestedFunction` is not a method of `obj`. As a result, `this.prop` will be `undefined` since the global object doesn't have a `prop` property.
Now, let's summarize these scenarios in a tabular form:
| Scenario | `this` Value | Explanation |
|---------------------------|--------------------------------|-------------------------------------------------------------------------------|
| `console.log(this)` | Window Object | Global context, `this` refers to the global object (`Window` in browser). |
| `console.log(this)` | Window Object | Inside a regular function, `this` still refers to the global object. |
| `this` in object method | `obj` (object itself) | Inside a method, `this` refers to the object on which the method is invoked. |
| `this` in nested function | Window Object | Inside a nested function, `this` refers to the global object. |
Understanding these scenarios is important for grasping how `this` behaves in different contexts within a browser environment.
### `this` keyword in Node.js in strict mode
Certainly, let's break down the behavior of the `this` keyword in Node.js in strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: `fn` is a function defined globally, and `obj` is an object containing a method `fn`. Here's what each scenario seems to be referring to:
1. **`console.log(this)` (Scenario 1)**:
```javascript
"use strict";
console.log(this); // Outputs an empty object ({})
```
In strict mode, when you log `this` in the global context, it will be an empty object `{}`. This is because strict mode prevents the default binding of `this` to the global object.
2. **`console.log(this)` with Undefined Function (Scenario 2)**:
```javascript
"use strict";
function myFunction() {
console.log(this);
}
myFunction(); // Outputs undefined
```
In strict mode, when you call a function without specifying its context (`this`), it's set to `undefined`. This is different from non-strict mode, where it would point to the global object.
3. **`this` Inside an Object Method (Scenario 3)**:
```javascript
"use strict";
var obj = {
prop: "I'm a property",
method: function() {
console.log(this.prop);
}
};
obj.method(); // Outputs "I'm a property"
```
When a method is invoked on an object, `this` within the method refers to the object itself. So, `this.prop` accesses the `prop` property of the `obj` object.
4. **`this` Inside Nested Object Methods (Scenario 4)**:
```javascript
"use strict";
var outerObj = {
innerObj: {
method: function() {
console.log(this);
}
}
};
outerObj.innerObj.method(); // Outputs the inner object
```
Inside nested object methods, `this` refers to the closest containing object. In this case, it points to `innerObj` when the `method` is invoked.
Now, let's summarize these scenarios in tabular form:
| Scenario | Example Code | `this` Value | Explanation |
|----------|---------------------------------------------|------------------------|------------------------------------------------------------------|
| 1 | `console.log(this);` | `{}` | In strict mode, `this` is an empty object in the global context. |
| 2 | `myFunction();` | `undefined` | Calling a function without context results in `undefined`. |
| 3 | `obj.method();` | `obj` object reference| `this` in an object method points to the object itself. |
| 4 | `outerObj.innerObj.method();` | `innerObj` object reference | In nested methods, `this` refers to the closest containing object. |
Understanding these scenarios and how `this` behaves in different contexts is crucial for writing reliable and maintainable code.
### `this` keyword in Browser in strict mode
Certainly, let's break down the behavior of the `this` keyword in Browser in strict mode for each of the scenarios you mentioned. After that, I'll provide a summary in tabular form.
Assumption: `fn` is a function defined globally, and `obj` is an object containing a method `fn`. Assuming you have the following setup in a browser environment:
```javascript
"use strict";
// Scenario 2
console.log("Scenario 2:");
console.log(this); // Output: undefined
// Scenario 3
var obj = {
fn: function () {
console.log("Scenario 3:");
console.log(this); // Output: obj
},
};
// Scenario 4
var fn = obj.fn;
// Scenario 1
console.log("Scenario 1:");
console.log(this); // Output: window
// Scenario 4 (contd.)
console.log("Scenario 4:");
fn(); // Output: undefined
```
Now, let's summarize these scenarios in a tabular form:
| Scenario | Code | `this` Value | Explanation |
|----------|--------------------------------------------------|----------------------|---------------------------------------------------------------------------------------------------|
| 1 | `console.log(this);` | `window` object | In the global context, `this` refers to the global object (`window` in browsers). |
| 2 | `"use strict"; console.log(this);` | `undefined` | In strict mode, `this` in the global context is `undefined`. |
| 3 | `obj.fn();` | `obj` object | When calling a method (`fn`) of an object (`obj`), `this` refers to the object itself (`obj`). |
| 4 | `var fn = obj.fn; fn();` | `undefined` | When a method (`fn`) is assigned to a variable (`fn`) and called, `this` is `undefined`. |
In summary:
1. In the global context, outside of any function, `this` refers to the global object (`window` in browsers).
2. In strict mode, in the global context, `this` is `undefined`.
3. When a function is a method of an object, `this` within the function refers to the object itself.
4. If a method is assigned to a variable and then invoked, `this` inside the method will be `undefined`.
Remember, the behavior of `this` can be a bit tricky, especially when dealing with nested functions, callbacks, and different contexts. Understanding these scenarios helps you write more predictable and maintainable code in JavaScript.
## Constructor Functions
In JavaScript, constructor functions are used to create and initialize objects. They serve as templates for creating objects with similar properties and methods. Constructor functions are typically written with an initial capital letter to distinguish them from regular functions.
A constructor function defines the structure of an object by setting its properties and methods using the `this` keyword. When you create an object using a constructor function with the `new` keyword, it creates a new instance of that object type and binds the instance to the `this` keyword within the constructor function.
**Car Company Example:**
Imagine you're building a car manufacturing company. Each car will have properties like the make, model, year, and color. You can create a constructor function called `Car` to represent a car object.
**Coding Example:**
Here's how you can implement the car company example using a constructor function:
```javascript
// Constructor function for Car
function Car(make, model, year, color) {
this.make = make;
this.model = model;
this.year = year;
this.color = color;
this.start = function() {
console.log(`Starting the ${this.make} ${this.model}`);
};
}
// Creating car instances
const car1 = new Car("Toyota", "Camry", 2022, "Blue");
const car2 = new Car("Ford", "Mustang", 2023, "Red");
// Using the car instances
console.log(car1); // Car { make: 'Toyota', model: 'Camry', year: 2022, color: 'Blue' }
console.log(car2); // Car { make: 'Ford', model: 'Mustang', year: 2023, color: 'Red' }
car1.start(); // Starting the Toyota Camry
car2.start(); // Starting the Ford Mustang
```
In this example:
- The `Car` constructor function defines the properties (`make`, `model`, `year`, `color`) and the `start` method for a car object.
- Instances of cars (`car1` and `car2`) are created using the constructor function.
- The `start` method is accessed and invoked on each car instance.
By using constructor functions, you can create multiple car instances with the same structure but different property values. This follows the fundamental concept of OOP, where you create objects based on blueprints (constructor functions) that define their structure and behavior.
### Constructor function with `this` example
```javascript
// Constructor function for Car
function Car(nameParam, colorParam, topSpeedParam) {
// Using 'this' to refer to instance-specific properties
this.name = nameParam;
this.color = colorParam;
this.topSpeed = topSpeedParam;
// Using 'this' to refer to instance-specific method
this.drive = function() {
console.log(`I am driving ${this.name}`);
};
}
// Creating car instances using 'new'
let car1 = new Car("Ferrari", "Red", '1000km/hr');
let car2 = new Car("8", 'white', '600km/hr');
// Using the car instances
car1.drive(); // Output: I am driving Ferrari
car2.drive(); // Output: I am driving 8
```
Explanation of `this` usage:
1. In the constructor function (`Car`):
- When creating a new instance using the constructor with `new`, the `this` keyword refers to the newly created instance.
- Properties (`name`, `color`, `topSpeed`) are assigned to the instance using `this` followed by the property name. For example, `this.name` refers to the `name` property of the specific instance being created.
- The `drive` method is assigned to each instance with `this.drive`. Inside the `drive` method, `this.name` refers to the `name` property of the instance that is calling the method. This allows each instance to have its own unique behavior.
2. When creating car instances:
- `let car1 = new Car("Ferrari", "Red", '1000km/hr');` creates an instance of `Car` named `car1`. Within the constructor, `this.name` becomes "Ferrari" for `car1`.
- `let car2 = new Car("8", 'white', '600km/hr');` creates another instance named `car2`. Inside the constructor, `this.name` becomes "8" for `car2`.
3. Using the car instances:
- Calling the `drive` method on `car1` (`car1.drive()`) prints "I am driving Ferrari". Within the method, `this.name` refers to the `name` property of `car1`.
- Calling the `drive` method on `car2` (`car2.drive()`) prints "I am driving 8". Within the method, `this.name` refers to the `name` property of `car2`.
The `this` keyword is essential in JavaScript's OOP to distinguish properties and methods of each individual instance when using constructor functions to create objects.

View File

@@ -0,0 +1,404 @@
## Agenda
**Topics to cover in Javascript Oops**
* This keyword
* Constructor functions
* Classes & Classical Inheritance
* Prototype & Prototypal Inheritance
* Call ,Apply & Bind Methods
* Memory of objects (Reference Datatypes)
We will try to cover most of these topics in today's sessions and the remaining in the next.
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## Classes and Constructors in OOP
The concept of classes and constructors in the context of Object-Oriented Programming (OOP) and the similarities between classes and constructor functions. Here's a more organized explanation of the concepts:
1. **Classes:** In modern JavaScript (ES6 and later), classes provide a way to define blueprints for creating objects. A class is a template for creating objects with shared properties and methods.
2. **Constructor Functions:** In pre-ES6 JavaScript, constructor functions were widely used to create objects with similar properties and methods. They serve as templates for creating instances of objects.
**Shared Properties and Methods:**
- Both classes and constructor functions allow you to create multiple objects (instances) that share the same properties and methods defined by the class or constructor.
**Constructor Method:**
- In both classes and constructor functions, the constructor method is used to initialize the properties of an object when it's created. It's automatically invoked when you create a new instance of the class or constructor.
**Using `this`:**
- Within the constructor method, the `this` keyword is used to refer to the current instance being created. It's used to set the initial values of the instance's properties.
**Example (Assuming Based on Context):**
Here's a restructured example based on the context you've provided:
```javascript
// Class (ES6+)
class Person {
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
}
// Creating instances of the Person class
const person1 = new Person("Adam", 24, "Male");
const person2 = new Person("Gage", 30, "Male");
// Constructor Function (Pre-ES6)
function Car(brand, model, color) {
this.brand = brand;
this.model = model;
this.color = color;
}
// Creating instances of the Car constructor
const car1 = new Car("Mercedes", "S-Class", "Blue");
const car2 = new Car("Jaguar", "XE", "Black");
// Accessing properties of instances
console.log(person1.name); // Output: "Adam"
console.log(car1.model); // Output: "S-Class"
```
In this example, both the `Person` class and the `Car` constructor serve as blueprints for creating instances. The `constructor` method (implicitly in classes, explicitly in constructors) sets the initial properties for the instances.
It's important to note that while the concepts and usage are similar, the syntax and capabilities of classes are more modern and flexible than traditional constructor functions. ES6 introduced classes as a more organized and syntactically convenient way to work with OOP concepts in JavaScript.
Certainly! It seems like you want to know about the structure of a class in JavaScript, including the constructor, prototype methods, and static methods. Here's a breakdown of each part:
```javascript
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// Prototype Method
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
// Static Method
static compareAges(person1, person2) {
if (person1.age > person2.age) {
return `${person1.name} is older than ${person2.name}.`;
} else if (person1.age < person2.age) {
return `${person2.name} is older than ${person1.name}.`;
} else {
return `${person1.name} and ${person2.name} are the same age.`;
}
}
}
```
Here's a breakdown of the components of the `Person` class:
1. **Constructor:**
- The constructor method is used to create and initialize instances of the class.
- It's called automatically when a new instance is created using the `new` keyword.
- In the example, the constructor takes `name` and `age` parameters and assigns them to the instance's properties using `this`.
2. **Prototype Method (`greet`):**
- Prototype methods are added to the class's prototype, which means they are shared among all instances of the class.
- In the example, the `greet` method is defined, which logs a message with the person's name and age.
- This method can be called on any instance of the `Person` class.
3. **Static Method (`compareAges`):**
- Static methods are called on the class itself, rather than on instances of the class.
- In the example, the `compareAges` static method is defined to compare the ages of two people and return a message.
- This method can be called on the `Person` class itself, without needing to create instances.
Here's how you would use the class:
```javascript
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
person1.greet(); // Output: Hello, my name is Alice and I am 30 years old.
person2.greet(); // Output: Hello, my name is Bob and I am 25 years old.
console.log(Person.compareAges(person1, person2)); // Output: Alice is older than Bob.
```
In this example, the class `Person` encapsulates the properties (name, age) and behaviors (greeting and age comparison) of a person. The constructor initializes the properties, the `greet` method is a shared behavior among instances, and the `compareAges` method is a static utility method to compare the ages of different persons.
## Inheritance in JavaScript
Inheritance is a fundamental concept in Object-Oriented Programming (OOP) that allows you to create a new class based on an existing class. The new class inherits properties and methods from the existing class, which is often referred to as the parent class or superclass. The new class is known as the subclass or child class.
**Example:**
Let's use an example to illustrate inheritance in JavaScript:
```javascript
// Parent class (Superclass)
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// Child class (Subclass)
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // Call the parent class constructor
this.grade = grade;
}
study() {
console.log(`${this.name} is studying hard for their exams.`);
}
}
// Creating instances of the subclasses
const person = new Person("Alice", 30);
const student = new Student("Bob", 18, "12th");
// Using inherited methods and subclass-specific methods
person.greet(); // Output: Hello, my name is Alice and I am 30 years old.
student.greet(); // Output: Hello, my name is Bob and I am 18 years old.
student.study(); // Output: Bob is studying hard for their exams.
```
In this example:
- The `Person` class serves as the parent class. It has a `constructor` and a `greet` method.
- The `Student` class is the child class that extends the `Person` class using the `extends` keyword. It adds a `grade` property and a `study` method.
- The `super()` method is used in the `Student` constructor to call the constructor of the parent class and pass the required parameters.
- Instances of both classes are created and can access inherited methods (`greet`) as well as subclass-specific methods (`study`).
By using inheritance, you can create a hierarchy of classes with shared behavior and properties, making your code more organized, reusable, and easier to maintain.
Let's create three classes: `Person`, `Student`, and `Teacher`, to demonstrate inheritance and the usage of `this` keyword. The `Person` class will be the parent class, and both `Student` and `Teacher` will be subclasses that inherit from `Person`. We'll also explore how to use the `super` keyword to call the parent class's constructor.
```javascript
// Parent class
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
// Subclass: Student
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // Call parent class's constructor using super()
this.grade = grade;
}
study() {
console.log(`${this.name} is studying.`);
}
}
// Subclass: Teacher
class Teacher extends Person {
constructor(name, age, subject) {
super(name, age);
this.subject = subject;
}
teach() {
console.log(`${this.name} is teaching ${this.subject}.`);
}
}
// Creating instances
const person = new Person("Alice", 30);
const student = new Student("Bob", 18, "12th");
const teacher = new Teacher("Eve", 40, "Math");
// Using methods and properties
person.introduce(); // Output: Hello, my name is Alice and I am 30 years old.
student.introduce(); // Output: Hello, my name is Bob and I am 18 years old.
student.study(); // Output: Bob is studying.
teacher.introduce(); // Output: Hello, my name is Eve and I am 40 years old.
teacher.teach(); // Output: Eve is teaching Math.
```
Explanation:
1. The `Person` class is the parent class. It has a constructor that initializes `name` and `age`, and an `introduce` method that uses the `this` keyword to access the instance properties.
2. The `Student` class is a subclass of `Person`. It uses the `super` keyword to call the parent class's constructor and sets the `grade` property. It also has a `study` method.
3. The `Teacher` class is another subclass of `Person`. It similarly calls the parent class's constructor using `super` and sets the `subject` property. It also has a `teach` method.
4. Instances of all three classes (`person`, `student`, and `teacher`) are created and methods from the parent and subclass are called.
In this example, the `super` keyword is used to invoke the constructor of the parent class, and the `this` keyword is used to access instance-specific properties and methods. Inheritance allows `Student` and `Teacher` to reuse properties and methods from the `Person` class, demonstrating the concept of code reusability in object-oriented programming.
---
title: Prototype & Prototypal Inheritance
description: Concept Prototype & Prototypal Inheritance in the OOP
duration: 2100
card_type: cue_card
---
## Prototype and Constructor Function
The concepts of prototype, constructor functions, and objects with an example of creating car objects with a shared prototype method.
In JavaScript, every function has a property called `prototype`, which is an object that can be used as a blueprint for creating new objects.
```javascript
// Constructor Function for Car
function Car(name, color) {
this.name = name;
this.color = color;
}
// Adding a shared prototype method
Car.prototype.drive = function() {
console.log(`${this.name} is driving.`);
};
```
### Creating Objects with Constructor Function
Objects created using a constructor function share properties and methods defined in the prototype.
```javascript
// Creating car objects using the constructor function
const car1 = new Car("Toyota", "Blue");
const car2 = new Car("Ford", "Red");
const car3 = new Car("Honda", "Silver");
// Using the shared prototype method
car1.drive(); // Output: Toyota is driving.
car2.drive(); // Output: Ford is driving.
car3.drive(); // Output: Honda is driving.
```
In this example:
- The `Car` constructor function defines properties `name` and `color`.
- We add a shared prototype method `drive()` to the `Car` constructor's prototype.
- Three car objects (`car1`, `car2`, `car3`) are created using the constructor function.
- All three car objects share the `drive()` method from the prototype.
### Prototype Chain
The prototype chain allows objects to inherit properties and methods from their prototypes.
```javascript
console.log(car1.hasOwnProperty("name")); // true
console.log(car1.hasOwnProperty("drive")); // false
```
- The `hasOwnProperty` method checks if a property is directly defined on the object.
- The `name` property is directly defined on the `car1` object, so `hasOwnProperty` returns `true`.
- The `drive` method is inherited from the prototype, so `hasOwnProperty` returns `false`.
Absolutely, I'll break down the concept of prototypes and how they relate to constructor functions using the example of three car objects with a shared `drive()` method.
**Example - Car Objects:**
```javascript
// Constructor function for Car
function Car(name, color) {
this.name = name;
this.color = color;
}
// Adding a shared method to the Car constructor's prototype
Car.prototype.drive = function() {
console.log(`${this.name} is driving.`);
};
// Creating car instances
const car1 = new Car("Toyota", "Blue");
const car2 = new Car("Honda", "Red");
const car3 = new Car("Ford", "Black");
// Using the shared method
car1.drive(); // Output: Toyota is driving.
car2.drive(); // Output: Honda is driving.
car3.drive(); // Output: Ford is driving.
```
Explanation:
- The `Car` constructor function is created with parameters `name` and `color`, which set the properties of each car object.
- The `drive` method is added to the `Car` constructor's prototype. This means all instances of `Car` will have access to the `drive` method through inheritance.
- Three car instances (`car1`, `car2`, and `car3`) are created using the `Car` constructor.
- The `drive` method is called on each car instance, and it uses the `this` keyword to refer to the specific instance.
In this example, the `drive` method is defined once on the prototype of the `Car` constructor, and all car instances share this method. This approach reduces memory usage and makes the code more efficient since the method isn't duplicated for each instance.
Remember, prototypes and constructor functions are at the core of how JavaScript implements inheritance and code reuse.
### Method shadowing
Method shadowing refers to the concept of overriding a method inherited from a parent object's prototype by defining a method with the same name in the child object. In your context of constructor functions and prototypes, let's explore method shadowing using the car example:
```javascript
// Constructor function for Car
function Car(name, color) {
this.name = name;
this.color = color;
}
// Adding a shared method to the Car constructor's prototype
Car.prototype.drive = function() {
console.log(`${this.name} is driving.`);
};
// Child constructor function for ElectricCar
function ElectricCar(name, color, batteryType) {
Car.call(this, name, color); // Call the parent constructor to set name and color
this.batteryType = batteryType;
}
// Inherit from Car prototype
ElectricCar.prototype = Object.create(Car.prototype);
// Adding a method specific to ElectricCar
ElectricCar.prototype.charge = function() {
console.log(`${this.name} is charging.`);
};
// Method shadowing in ElectricCar
ElectricCar.prototype.drive = function() {
console.log(`${this.name} is driving silently.`);
};
// Creating car instances
const car1 = new Car("Toyota", "Blue");
const eCar1 = new ElectricCar("Tesla", "Black", "Lithium-ion");
// Using the drive method
car1.drive(); // Output: Toyota is driving.
eCar1.drive(); // Output: Tesla is driving silently.
```
Explanation:
1. We have the `Car` constructor with a shared `drive` method.
2. We create a child constructor `ElectricCar` that inherits properties from `Car` using `Car.call(this, name, color)`.
3. We set up inheritance for the `ElectricCar` prototype using `Object.create(Car.prototype)`.
4. We add a specific method `charge` for `ElectricCar`.
5. We use method shadowing by defining a new `drive` method in `ElectricCar` prototype, which overrides the inherited `drive` method.
In the example, `ElectricCar` inherits the `drive` method from `Car`. However, by defining a new `drive` method in `ElectricCar`, the inherited method is shadowed or overridden. This is a way to provide specialized behavior for a method in a subclass while still utilizing the inheritance structure.
Remember, method shadowing allows you to override inherited methods, giving you flexibility to adapt and extend functionality in child classes.

View File

@@ -0,0 +1,440 @@
# Full Stack LLD & Projects: JavaScript - OOPS-3 : Call Apply Bind and Memory of Objects
**Agenda of this Lecture:**
* Call Method
* Apply Method
* Bind Method
* How memory of reference data type works
* Shallow copy
* Deep copy
Lets consider and example:
```javascript
let student = {
firstName: 'Adam',
lastName: 'Smith',
age: 25,
getEmail: function() {
console.log(`${this.firstName}.${this.lastName}@gmail.com`);
}
};
// Calling the getEmail function using the 'call' method
student.getEmail();
```
If we want to avoid repeating the code for the `getEmail` function for both the student and teacher objects by creating a shared function and using call to bind it to different objects.
```javascript
function getEmailFn() {
console.log(`${this.firstName}.${this.lastName}@gmail.com`);
}
let student = {
firstName: 'Adam',
lastName: 'Smith',
age: 25,
getEmail: getEmailFn
};
let teacher = {
firstName: 'Steve',
lastName: 'Rogers',
age: 35,
getEmail: getEmailFn
};
student.getEmail();
teacher.getEmail();
```
**Note:** This will return an error of `function not defined`. To solve this, we have three methods.
### Explanation
The `getEmail` property should hold a reference to the function` getEmailFn`, not its execution result. Also, the usage of `this.firstName` and `this.lastName` outside a function will not refer to the desired context. Here's the corrected version:
```javascript
function getEmailFn(firstName, lastName) {
console.log(`${firstName}.${lastName}@gmail.com`);
}
let student = {
firstName: 'Adam',
lastName: 'Smith',
age: 25,
getEmail: getEmailFn(this.firstName, this.lastName)
};
let teacher = {
firstName: 'Steve',
lastName: 'Rogers',
age: 35,
getEmail: getEmailFn(this.firstName, this.lastName)
};
student.getEmail;
teacher.getEmail;
```
**Note:** This both will retur `undefined.undefined@gmail.com`
**To solve this , we can use call method**
```javascript
let student = {
firstName: 'Adam',
lastName: 'Smith',
age: 25,
getEmail: function() {
console.log(`${this.firstName}.${this.lastName}@gmail.com`);
}
};
let teacher = {
firstName: 'Steve',
lastName: 'Rogers',
age: 35,
getEmail: function() {
console.log(`${this.firstName}.${this.lastName}@gmail.com`);
}
};
student.getEmail.call(student);
teacher.getEmail.call(teacher);
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/451/original/Screenshot_2023-09-20_150801.png?1695202699)
By using the `call` method, we attach a common `getEmailFn` function to both student and teacher objects, allowing it to be invoked with the correct context using call. This avoids repetition by dynamically binding the function to different objects and ensures accurate email address generation.
**We can pass some arguments as well to the call method:**
```javascript
function getEmailFn(subjects) {
return (${this.firstName}.${this.lastName}@gmail.com)`;
}
function chooseSubject(sub1, sub2) {
return `${sub1} and ${sub2}`;
}
let teacher1 = {
firstName: 'Steve',
lastName: 'Rogers',
age: 35,
getEmail: getEmailFn
};
let teacher2 = {
firstName: 'Tony',
lastName: 'Stark',
age: 40,
getEmail: getEmailFn
};
```
The `call` method in JavaScript allows you to invoke a function on an object while providing a specific context (this value) for that function to use, along with optional arguments that the function can accept. This enables dynamic context binding and the passing of specific values directly to the function being called.
### Explanation
**Apply** and **Call** method are almost same. The only difference is, when we are using apply method, we need to pass an **array** as the argument.
```javascript
function getEmailFn(subjects) {
return `${subjects} (${this.firstName}.${this.lastName}@gmail.com)`;
}
function chooseSubject(sub1, sub2) {
return `${sub1} and ${sub2}`;
}
function chooseBatches(batch1, batch2, batch3) {
return `${batch1} ${batch2} ${batch3}`;
}
let teacher1 = {
firstName: 'Steve',
lastName: 'Rogers',
age: 35,
getEmail: getEmailFn
};
let teacher2 = {
firstName: 'Tony',
lastName: 'Stark',
age: 40,
getEmail: getEmailFn
};
```
**Note:** This will give error as apply method takes array as argument. This is the corrected approach:
```javascript
function getEmailFn(subjects) {
return `${subjects} (${this.firstName}.${this.lastName}@gmail.com)`;
}
function chooseSubject(sub1, sub2) {
return `${sub1} and ${sub2}`;
}
function chooseBatches(batch1, batch2, batch3) {
return [batch1, batch2, batch3];
}
let teacher1 = {
firstName: 'Steve',
lastName: 'Rogers',
age: 35,
getEmail: getEmailFn
};
let teacher2 = {
firstName: 'Tony',
lastName: 'Stark',
age: 40,
getEmail: getEmailFn
};
let batches = chooseBatches('BatchA', 'BatchB', 'BatchC');
console.log("Retrieving teacher1's email:");
console.log(teacher1.getEmail.apply(teacher1, batches));
console.log("\nRetrieving teacher2's email:");
console.log(teacher2.getEmail.apply(teacher2, batches));
```
In this version, the apply method is called with the entire batches array as its second argument, as the function getEmailFn only expects a single argument (subjects). This will work correctly because the apply method will pass each element of the array as a separate argument to the function.
### Explanation
The `bind` method in JavaScript creates a new function that, when invoked, has a specific this value set to a provided value. It's useful for "binding" a function to a particular object as its context. The new function produced by bind can also have preset arguments if provided.
```javascript
let callLater = getEmail.bind(teacher1);
console.log(callLater());
```
**Explanation:**
* `bind` creates a new function callLater that will have the getEmail function's logic from teacher1 as its content.
* The `bind` method doesn't execute the function immediately; it prepares a new function with the specified context.
* To actually execute the callLater function and log the email address, you need to call it with parentheses like `callLater()`.
### Explanation
Lets consider few cases:
**Case-1:**
```javascript
let person1 = `Adam`;
let person2 = `Steve`;
console.log(person1);
console.log(person2);
```
```
Output:
Adam
Steve
```
**Case-2:**
```javascript
let person1 = `Adam`;
let person2 = `Steve`;
person2 = person1
console.log(person1);
console.log(person2);
```
```
Output:
Adam
Adam
```
**Case-3:**
```javascript
let person1 = {
name: `Adam`,
age: 25
}
let person2 = person1
console.log(person1);
console.log(person2);
```
```
Output:
{ name: 'Adam', age: 25 }
{ name: 'Adam', age: 25 }
```
**Case-4:**
```javascript
let person1 = {
name: `Adam`,
age: 25
}
let person2 = person1
person2.name = 'Steve'
console.log(person1);
console.log(person2);
```
```
Output:
{ name: 'Steve', age: 25 }
{ name: 'Steve', age: 25 }
```
Here's a concise explanation of each case in terms of data types:
**Case-1:**
Strings (Primitive Data Type)
Both person1 and person2 hold separate string values.
**Case-2:**
Strings (Primitive Data Type)
person2 is assigned the value of person1, so they both reference the same string value.
**Case-3:**
Objects (Reference Data Type)
person2 points to the same object as person1, resulting in both variables referencing the same object in memory.
**Case-4:**
Objects (Reference Data Type)
person2 references the same object as person1. Changing a property via one variable affects the shared object's property value.
**Heap and Stack Memory:**
* **Stack Memory:** It's used for function call data and local variables, managed automatically with Last-In-First-Out order. In cases 1-2, stack stores string values and references.
* **Heap Memory:** It's for dynamic memory allocation, managed manually, suitable for larger data structures. In cases 3-4, heap holds shared object data and object property changes.
**Pass by value and Pass by reference:**
* **Pass by Value:** When passing a primitive data type, a copy of the actual value is passed. In cases 1-2, strings are passed by value when assigned or copied.
* **Pass by Reference:** When passing a reference data type, a reference (memory address) to the actual data is passed. In cases 3-4, objects are passed by reference when shared between variables.
### Shallow Copy
A shallow copy creates a new object or array, but the contents within it remain references to the original data. It copies the top-level structure without recursively copying nested objects or arrays.
The **spread operator (...)** can be used to create shallow copies of arrays and objects. It creates a new array or object and copies the enumerable properties or elements from the original. However, for nested objects, only references to the inner objects are copied, not the inner objects themselves.
**Example:**
```javascript
let person1 = {
name: 'Adam',
age: 25,
address: {
city: 'New York',
country: 'USA'
}
};
let person2 = {
...person1, // Shallow copy top-level properties
name: 'Steve',
address: {
...person1.address, // Shallow copy nested address object
city: 'Delhi'
}
};
console.log(person1);
console.log(person2);
```
```
Output:
{
name: 'Adam',
age: 25,
address: { city: 'New York', country: 'USA' }
}
{ name: 'Steve', age: 25, address: { city: 'Delhi', country: 'USA' } }
```
### Deep Copy
A deep copy creates a completely independent copy of an object or array, including all nested objects or arrays. It ensures that changes made to the copied structure do not affect the original.
**Example:**
```javascript
let person1 = {
name: 'Adam',
age: 25,
address: {
city: 'New York',
country: 'USA'
}
};
let person2 = JSON.parse(JSON.stringify(person1));
person2.name = 'Steve';
person2.address.city = 'Delhi';
console.log(person1);
console.log(person2);
```
```
Output:
{
name: 'Adam',
age: 25,
address: { city: 'New York', country: 'USA' }
}
{ name: 'Steve', age: 25, address: { city: 'Delhi', country: 'USA' } }
```
---

View File

@@ -0,0 +1,463 @@
# Asynchronous Programming 2
#### Definition
**Promise:** In JavaScript, a promise is an object representing the eventual completion (or failure) of an asynchronous operation. It provides a way to handle asynchronous code more cleanly and manage the results or errors that may occur when the operation completes. Promises have three states: pending, resolved (fulfilled), or rejected, and they allow you to attach callback functions to handle these different outcomes.
*States of Promise*
* Made a Promise => Pending State
* Promise can be fulfilled => Resolved
* Promise can not be fulfilled => Rejected State
* Setled => Promise Executed
#### Syntax
```javascript
let myPromise = new Promise(function(resolve, reject){
})
console.log(myPromise)
```
**Output:**
![](https://hackmd.io/_uploads/rJDlN6WAh.png)
**Explanation:** Wherever there is no resolve and no reject condition so it always lies under the pending condition.
#### Question
Create resolve and reject states of Promise
#### Solution
**Reject State:**
```javascript
let myPromise = new Promise(function(resolve, reject){
const a = 4
const b = 5
if(a == b) {
resolve('Yes They are Equal')
}
else {
reject('No They are not Equal')
}
})
console.log(myPromise)
```
**Output:**
![](https://hackmd.io/_uploads/rJV47ZMR3.png)
**Explanation:** Whenever condition is not fulfilled or condition is not true it always lies under the rejected state
**Resolve States:**
```javascript
let myPromise = new Promise(function(resolve, reject){
const a = 4
const b = 4
if(a == b) {
resolve('Yes They are Equal')
}
else {
reject('No They are not Equal')
}
})
console.log(myPromise)
```
**Output:**
![](https://hackmd.io/_uploads/B1U1VbfR3.png)
**Explanation:** Whenever condition is fulfilled or condition is true it always lies under the resolved state.
#### Question
1. What is the use of then method and How to use then mehtod?
```javascript
let myPromise = new Promise(function(resolve, reject){
const a = 4
const b = 4
if(a == b) {
resolve('Yes They are Equal')
}
else {
reject('No They are not Equal')
}
})
// then method
myPromise.then(function(data){
console.log(data)
})
```
**Output:**
![](https://hackmd.io/_uploads/B1YzDZfR3.png)
**Explanation:** Whenever a promise is resolved the data inside the resolve passes to the then method.
2. What is the use of catch method and How to use catch mehtod?
```javascript
let myPromise = new Promise(function(resolve, reject){
const a = 4
const b = 4
if(a == b) {
resolve('Yes They are Equal')
}
else {
reject('No They are not Equal')
}
})
// then method
myPromise.then(function(data){
console.log(data)
})
```
**Output:**
![](https://hackmd.io/_uploads/H1iKeQ702.png)
**Explanation:** Whenever a promise is rejected the method will throw an error and to handle these errors we use a catch method.
#### Question
**How to read a file using Promises?**
#### Solution
First of we will se how to read file using javascript only
```javascript
const fs = require('fs')
fs.readFile('f1.txt', cb)
function cb(err, data) {
if(err) {
console.log(err)
}else {
console.log("This is File 1 data -> " + data)
}
}
```
**Output:**
![](https://hackmd.io/_uploads/BJueDXX03.png)
Now we will se how to read the file with using promises.
For Resolve State:
```javascript
const fs = require('fs')
let promiseReadFile = fs.promises.readFile('f1.txt')
promiseReadFile.then(function(data) {
console.log('This is file data -> ' + data)
})
promiseReadFile.catch(function(err) {
console.log('This is Your Error -> ' + err)
})
```
**Output:**
![](https://hackmd.io/_uploads/HkHSommCn.png)
For Reject State:
```javascript
const fs = require('fs')
let promiseReadFile = fs.promises.readFile('f5.txt')
promiseReadFile.then(function(data) {
console.log('This is file data -> ' + data)
})
promiseReadFile.catch(function(err) {
console.log('This is Your Error -> ' + err)
})
```
```f1.txt
I AM FILE 1 DATA
```
**Output:**
![](https://hackmd.io/_uploads/HJEe37Q0h.png)
**Explanation:** There is a in-built process of 'resolve' and 'reject' body which passes through the then and catch method. If the promise is fulfilled then it lies under the 'resolve' state using 'then' method. else it lies under the 'reject' state using 'catch' method.
#### Question
**How to read all files using promises?**
```javascript
const fs = require('fs')
let promiseReadFile1 = fs.promises.readFile('f1.txt')
let promiseReadFile2 = fs.promises.readFile('f2.txt')
let promiseReadFile3 = fs.promises.readFile('f3.txt')
// For File 1
promiseReadFile1.then(function(data) {
console.log('This is file 1 data -> ' + data)
})
promiseReadFile1.catch(function(err) {
console.log('This is Your Error -> ' + err)
})
// For File 2
promiseReadFile2.then(function(data) {
console.log('This is file 2 data -> ' + data)
})
promiseReadFile2.catch(function(err) {
console.log('This is Your Error -> ' + err)
})
// For File 3
promiseReadFile3.then(function(data) {
console.log('This is file 3 data -> ' + data)
})
promiseReadFile3.catch(function(err) {
console.log('This is Your Error -> ' + err)
})
```
```f1.txt
I AM FILE 1 DATA
```
```f2.txt
I AM FILE 2 DATA
```
```f3.txt
I AM FILE 3 DATA
```
**Output:**
![](https://hackmd.io/_uploads/ByPjJNQ02.png)
**Explanation:** Since we are using promises so the order can be changed every time becuase it's following parallel operation and parallel operations can only happens when the code is asynchronous.
**Optimized Solution**
For then method
```javascript
const fs = require('fs')
let f1p = fs.promises.readFile('f1.txt')
let f2p = fs.promises.readFile('f2.txt')
let f3p = fs.promises.readFile('f3.txt')
function readFileCallback(data) {
console.log('This is the data -> ' + data)
}
f1p.then(readFileCallback)
f2p.then(readFileCallback)
f3p.then(readFileCallback)
```
**Output:**
![](https://hackmd.io/_uploads/HJ8D7EX0n.png)
For catch method
```javascript
const fs = require('fs')
let f1p = fs.promises.readFile('f1.txt')
let f2p = fs.promises.readFile('f2.txt')
let f3p = fs.promises.readFile('f3.txt')
function readFileCallback(data) {
console.log('This is the data -> ' + data)
}
function handleError(err) {
console.log('This is my error -> ' + err)
}
f1p.then(readFileCallback)
f2p.then(readFileCallback)
f3p.then(readFileCallback)
f1p.catch(handleError)
f2p.catch(handleError)
f3p.catch(handleError)
```
**Output:**
![](https://hackmd.io/_uploads/HJFA4EmRn.png)
**Explanation:** Since we are using promises so the order can be changed every time becuase it's following parallel operation and parallel operations can only happens when the code is asynchronous.
#### Definition:
Asynchronous JavaScript is a programming paradigm that allows you to execute code concurrently without blocking the main execution thread.
* Call Stack
* Node APIs
* Callback Queue
* Event Loop
```javascript
const fs = require('fs')
console.log('Before')
let f1p = fs.promises.readFile('f1.txt')
let f2p = fs.promises.readFile('f2.txt')
let f3p = fs.promises.readFile('f3.txt')
function readFileCallback(data) {
console.log('This is the data -> ' + data)
}
function handleError(err) {
console.log('This is my error -> ' + err)
}
f1p.then(readFileCallback)
f2p.then(readFileCallback)
f3p.then(readFileCallback)
f1p.catch(handleError)
f2p.catch(handleError)
f3p.catch(handleError)
console.log('After')
```
**Output:**
![](https://hackmd.io/_uploads/HJGqwEm0h.png)
**Visualization**
![](https://hackmd.io/_uploads/SyaF_4QCh.png)
#### Example
```javascript
function logA() { console.log('A') }
function logB() { console.log('B') }
function logC() { console.log('C') }
function logD() { console.log('D') }
// Click the "RUN" button to learn how this works!
logA();
setTimeout(logB, 0);
Promise.resolve().then(logC);
logD();
```
**Output:**
![](https://hackmd.io/_uploads/r1wuFNm03.png)
**Explanation:** MicroTaks queue will be given the higher priority promisified code will run earlier than callback.
**Visulization:**
![](https://hackmd.io/_uploads/HyJBhVQ0h.gif)
**Promise Chaining:** Promise chaining in JavaScript is a technique for working with asynchronous code using Promises. It involves linking multiple asynchronous operations together, ensuring that one operation starts only after the previous one has completed successfully. This is typically achieved using the `.then()` method to handle the result of a Promise and return another Promise, allowing you to chain multiple operations together in a clean and sequential manner.
![](https://hackmd.io/_uploads/Sy4elHQC2.png)
#### Example
```javascript
const fs = require('fs')
console.log('Before')
let f1p = fs.promises.readFile('f1.txt')
function cb1(data) {
console.log('This is File 1 Data -> ' + data)
let f2p = fs.promises.readFile('f2.txt')
return f2p
}
function cb2(data) {
console.log('This is File 2 Data -> ' + data)
let f3p = fs.promises.readFile('f3.txt')
return f3p
}
function cb3(data) {
console.log('This is File 3 Data -> ' + data)
}
f1p.then(cb1).then(cb2).then(cb3)
console.log('After')
```
**Output:**
![](https://hackmd.io/_uploads/SJAXSrQ02.png)
**Explanation:** It's following serial operation and serial operations dosen't affect the order.

View File

@@ -0,0 +1,649 @@
# Asynchronous Programming 3
#### Definition
**Async Function:** An async function is declared with the async keyword. It allows you to write asynchronous code in a more synchronous-like manner. Inside an async function, you can use the await keyword to pause execution until a Promise is resolved.
**Await:** The await keyword is used inside an async function to wait for a Promise to resolve. It effectively pauses the execution of the function until the Promise resolves and returns its result.
**Promises:** Async/await is often used with Promises. Promises are objects that represent the eventual completion or failure of an asynchronous operation. They are a way to manage asynchronous code and are frequently returned by functions like fetch.
#### Question
Create resolve and reject states of Promise to place a order, then process the order and then generate a bill process.
#### Solution
![](https://hackmd.io/_uploads/S141UD7Rh.png)
**Step - 1:** Create a Promise method for placing/accepting the order.
1.1 Create resolve state if order is placed
```javascript
function placeOrder(drink) {
return new Promise(function(resolve, reject) {
if(drink === 'coffee') {
resolve('Order for Coffee Placed.')
}
else {
reject('Order can not be Placed.')
}
})
}
placeOrder('coffee').then(function(orderStatus) {
console.log(orderStatus)
})
```
**Output:**
![](https://hackmd.io/_uploads/rygdYvmA3.png)
1.2 Create reject state if order is not placed
```javascript
function placeOrder(drink) {
return new Promise(function(resolve, reject) {
if(drink === 'coffee') {
resolve('Order for Coffee Placed.')
}
else {
reject('Order can not be Placed.')
}
})
}
placeOrder('tea').then(function(orderStatus) {
console.log(orderStatus)
}).catch(function(error) {
console.log(error)
})
```
**Output:**
![](https://hackmd.io/_uploads/ByDBcPQR2.png)
**Step - 2:** Create a Promise method for process the order.
```javascript
function placeOrder(drink) {
return new Promise(function(resolve, reject) {
if(drink === 'coffee') {
resolve('Order for Coffee Placed.')
}
else {
reject('Order can not be Placed.')
}
})
}
function processOrder(orderPlaced) {
return new Promise(function(resolve) {
resolve(`${orderPlaced} and Served.`)
})
}
placeOrder('coffee').then(function(orderStatus) {
console.log(orderStatus)
return orderStatus
}).then(function(orderStatus) {
let orderIsProcessed = processOrder(orderStatus)
console.log(orderIsProcessed)
return orderIsProcessed
}).then(function(orderIsProcessed) {
console.log(orderIsProcessed)
})
```
**Output:**
![](https://hackmd.io/_uploads/ByszAwmC2.png)
**Step - 3:** Create a Promise method for generate the bill.
```javascript
function placeOrder(drink) {
return new Promise(function(resolve, reject) {
if(drink === 'coffee') {
resolve('Order for Coffee Placed.')
}
else {
reject('Order can not be Placed.')
}
})
}
function processOrder(orderPlaced) {
return new Promise(function(resolve) {
resolve(`${orderPlaced} and Served.`)
})
}
function generateBill(processedOrder) {
return new Promise(function(resolve) {
resolve(`${processedOrder} and Bill Generated with 200 Rs.`)
})
}
placeOrder('coffee').then(function(orderStatus) {
console.log(orderStatus)
return orderStatus
}).then(function(orderStatus) {
let orderIsProcessed = processOrder(orderStatus)
console.log(orderIsProcessed)
return orderIsProcessed
}).then(function(orderIsProcessed) {
console.log(orderIsProcessed)
return orderIsProcessed
}).then(function(orderIsProcessed) {
let BillGenerated = generateBill(orderIsProcessed)
return BillGenerated
}).then(function(BillGenerated) {
console.log(BillGenerated)
}).catch(function(err) {
console.log(err)
})
```
**Output:**
![](https://hackmd.io/_uploads/r1hZ-O70h.png)
**Explanation:** Firstly, we have create placeOrder function to place the order then if we pass 'coffee' then the promise is in resolve state. As soon as it is resolved we get the orderStatus and printing it. After that as soon as order is placed then we need to process the order this will be also in resolved state. After processing the order we need to generate the bill this should be also in resolved state, this process called the promise chaining method.
**Optimized Solution:** Using Async & Await
**Step - 1:** We will use async and await method to make code more clean and readable. to use async and await we need to create a function.
```javascript
function placeOrder(drink){
return new Promise(function(resolve , reject){
if(drink ==='coffee'){
resolve('Order for Coffee Placed')
}
else{
reject('Order cannot be Placed')
}
})
}
function processOrder(orderPlaced){
return new Promise(function(resolve){
resolve(`${orderPlaced} and Served`)
})
}
function genreateBill(processedOrder){
return new Promise(function(resolve){
resolve(`${processedOrder} and Bill generated with 200Rs`)
})
}
// Async and Await
// to use async await you need to create Functions
async function serveOrder(){
let orderstatus = await placeOrder('coffee')
console.log(orderstatus)
let processedOrder = await processOrder(orderstatus)
console.log(processedOrder)
let generatedBill = await genreateBill(processedOrder)
console.log(generatedBill)
}
serveOrder()
```
**Output:**
![](https://hackmd.io/_uploads/B10D6OQCh.png)
**Step - 2:** To Handle error we will use try and cacth method
```javascript
function placeOrder(drink){
return new Promise(function(resolve , reject){
if(drink ==='coffee'){
resolve('Order for Coffee Placed')
}
else{
reject('Order cannot be Placed')
}
})
}
function processOrder(orderPlaced){
return new Promise(function(resolve){
resolve(`${orderPlaced} and Served`)
})
}
function genreateBill(processedOrder){
return new Promise(function(resolve){
resolve(`${processedOrder} and Bill generated with 200Rs`)
})
}
// Async and Await
// to use async await you need to create Functions
async function serveOrder(){
try {
let orderstatus = await placeOrder('tea')
console.log(orderstatus)
let processedOrder = await processOrder(orderstatus)
console.log(processedOrder)
let generatedBill = await genreateBill(processedOrder)
console.log(generatedBill)
} catch (error) {
console.log(error)
}
}
serveOrder()
```
**Output:**
![](https://hackmd.io/_uploads/ry_bRO7A2.png)
#### Question
Create the Promise.all function in JavaScript that returns a single promise and the resolved value contains an array of values.
#### Solution
![](https://hackmd.io/_uploads/rJI-MKmR2.png)
```javascript
const p1 = Promise.resolve("pi"); // pi
//returns a promise of resolved value "pi"
const p2 = 3.14; // 3.14
const p3 = new Promise((resolve, reject) => {
//promise method to resolve or reject values
resolve("Maths");
//p3 contains a promise of resolving "maths" value
});
let returned_promise = Promise.all([p1 ,p2 ,p3]);
//checking fulfillment or rejection of any of the promises: p1,p2 and p3 passed as iterable in the function
returned_promise.then((array)=>{
//returned_promise will contain final returned value of Promise.all() method
console.log(array);
//checking and printing the value returned as promised by Promise.all() method in JS
})
```
**Output:**
![](https://hackmd.io/_uploads/B1GglYX03.png)
**Explanation:** As we can see, the `Promise.all` in JavaScript has returned a single promise and the resolved value contains an array of values.
#### Example
```javascript
let a = 12
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/r1mMQYm02.png)
These are the Falsy Values
* undefined
* null
* `0`
* `''`
* NaN
* false
#### Example 1
```javascript
let a = undefined
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/Hy4tNYX0n.png)
#### Example 2
```javascript
let a = null
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/Hy4tNYX0n.png)
#### Example 3
```javascript
let a = 0
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/Hy4tNYX0n.png)
#### Example 4
```javascript
let a = ''
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/Hy4tNYX0n.png)
#### Example 5
```javascript
let a = NaN
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/Hy4tNYX0n.png)
#### Example 6
```javascript
let a = false
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/Hy4tNYX0n.png)
Everything other than this will be truthy value
#### Example 7
```javascript
let a = 'Scaler'
if(a){
console.log('This is a truthy Value')
}else{
console.log('This is a Falsy Value')
}
```
**Output:**
![](https://hackmd.io/_uploads/HyZpBKQ0n.png)
---
title: Diffrence Between `==` and `===`
description:
duration: 900
card_type: cue_card
---
#### Question
What is the Diffrence Between `==` and `===`
#### Example 1
```javascript
let a = 2
let b = '2'
console.log(a==b) // loose checking //
// here in this case only the values are getting checked
```
**Output:**
![](https://hackmd.io/_uploads/SyQd_FQAh.png)
#### Example 2
```javascript
let a = 2
let b = '2'
console.log(a===b) // strict Checking
// Here in this case the value as well as the type is getting checked
```
**Output:**
![](https://hackmd.io/_uploads/ByDgFF7An.png)
#### Question
How to check typeof Operator
#### Example 1
```javascript
let c = 'Scaler'
console.log(typeof c)
```
**Output:**
![](https://hackmd.io/_uploads/Hyl8cFmRh.png)
#### Example 2
```javascript
let c = {name : 'Scaler'}
console.log(typeof c)
```
**Output:**
![](https://hackmd.io/_uploads/Byv6FK70n.png)
#### Example
```javascript
let c = [1, 2, 3]
// Array.isArray - Boolean Method
let checkArray = Array.isArray(c)
console.log(checkArray)
```
**Output:**
![](https://hackmd.io/_uploads/rJ1ioY702.png)
#### Example
```javascript
let d = 1/0
console.log(d)
```
**Output:**
![](https://hackmd.io/_uploads/By3jntQRn.png)
#### Example
```javascript
let d = 2 + ''
console.log(d)
```
**Output:**
![](https://hackmd.io/_uploads/ByyraK7R2.png)
#### Example 1
```javascript
let sqroot = Math.sqrt(-3)
console.log(sqroot)
```
**Output:**
![](https://hackmd.io/_uploads/HJDbRtmAh.png)
#### Example 2
```javascript
let out = 'Scaler'*10
console.log(out)
```
**Output:**
![](https://hackmd.io/_uploads/r1U9CYmCn.png)
#### Example 1
```javascript
let out = 'Scaler'*10
let checkIsNan = isNaN(out)
console.log(checkIsNan)
```
**Output:**
![](https://hackmd.io/_uploads/H1fKy5mAn.png)
#### Example 2
```javascript
let a = 2
let checkIsNan = isNaN(a)
console.log(checkIsNan)
```
**Output:**
![](https://hackmd.io/_uploads/SyQWl9XR3.png)
#### Example 1
```javascript
let sqroot = Math.sqrt(-3)
console.log(typeof sqroot)
```
**Output:**
![](https://hackmd.io/_uploads/SkcwZ9mR3.png)
```javascript
console.log(isNaN(null))
console.log(isNaN(undefined))
```
**Output:**
![](https://hackmd.io/_uploads/rJqnG5QCh.png)

View File

@@ -0,0 +1,400 @@
## Agenda
Today we are going to start with React.
Prerequisites that you should know:
* HTML/CSS
* Javascript Core (Understanding of how Javascript works).
* DOM (Vanilla JS)
* Modularity (We will know how to export and import different functions and properties from the code. While working with React we use lot of Modular code in terms of components. Everything is modular in React, that why we need to understand Modularity. )
Softwares you must install in System for react
* VS Code
* NodeJS (React is dependent on NodeJS)
* Git (Not necessary)
Before starting with React, we will first understand 1 topic and then we will move forward with React. That topic is Modularity.
whatever code you write shoud be written in form of modules so that you can specifically use, Read and Maintain your Codebase. Modularity refers to the practice of breaking down a larger software program into smaller, self-contained, and reusable modules. These modules encapsulate specific functionality, variables, or objects, making it easier to manage, maintain, and scale your JavaScript code.
**[Ask the learners]**
Suppose you have written all the modules you are using in NodeJS in a single file, is this a Good practice?
No, this is not a good practice because what modularity says is whatever feature you want to write, make separate module for it.
Suppose you want to code a calculator and you want to make modules for each function it will do. Just make 4 functions addition, subtraction, multiplication and division.
We can simply make 4 functions:
```javascript
function add(a, b){
console.log(a+b);
}
function sub(a, b){
console.log(a+b);
}
function mul(a, b){
console.log(a+b);
}
function div(a, b){
console.log(a+b);
}
add(2, 3)
sub(10, 5)
mul(3, 4)
div(10, 2)
```
Now we want to make this code modular, and that module, we should be able to use in different JS files. For that we have to import and export functions and properties of the code.
For exporting the function to new file, first lets create 1 file called ModuleTest.js where we will import all these functions that we have just created.
for exporting the functions:
```javascript
module.exports={
addition: add,
substraction : sub,
multiplication : mul,
division : div
}
```
**[Ask the learners]**
What would you do if you want to import these files?
Using Require, yes we can use require to import a file in other JS file.
So in this case:
```javascript
const calculator = require("./calc")
calculator.addition(2 , 3)
calculator.multiplication(2 ,3)
calculator.substraction(3 , 1)
calculator.division (10 , 5)
```
So we are trying to import a module named "calc" and use the functions addition, multiplication, subtraction, and division from that module.
Also we dont need to use console.log here because we are already logging the output in the the function definition.
Benefits of this are:
* Code Reusability: Modules are self-contained units of code that can be reused across your application or in other projects. This reduces the need to rewrite the same functionality, saving time and effort.
* Ease of Maintenance: Smaller, well-defined modules are easier to understand and maintain. Developers can focus on individual modules without needing to comprehend the entire codebase, which simplifies debugging and updates.
* Readability: Well-structured modules with clear interfaces and documentation enhance code readability. Other developers (including your future self) can easily understand how to use and interact with a module.
Lets understand module.exports object:
ModuleExports defines an object in which you can pass your functions in keys and you can use those keys whenever you are Importing your module and they will invoke the function associated with them Respetively.
It is used to define what a module should export to be used in other parts of your code. It allows you to encapsulate code in a module and expose specific functions, variables, or objects for use in other files or modules.
So this is all about Modularity.
Now we will look into React. We will be learning it from scratch.
React is the most popular and important library for Frontend Development. React is most in demand and companies want frontend developers who works on React.
React is JS library for frontend Development developed By Meta(facebook) and this actually is very fast performing tool and it follows Component Based Architecture.
Lets understand what is Component Based Architecture:
If you talk about Vanilla JS, DOM is a tree structure
![](https://hackmd.io/_uploads/rkRtn4zA3.png)
Doing Operations in a DOM is a heavy operations, So remove these Heavy Operations, React introduced Component Based Architecture. It says that anything you create can be hashioned into separate component, allowing you to work on each of them separately.
**[Ask the learners]**
What do I mean by a Component Based Architecture?
If you take a look at LinkedIn's interface, you'll notice several components, and each of these components functions as an independent entity.If you examine the particular section, you'll see that all the sections are distinct components and also operating of one another.
This is precisely what React does, it structures the architecture in a component-centric manner.
![](https://hackmd.io/_uploads/Skoqk6EAh.png)
If you currently have an application. React's library will partition each of the sections into distinct components, allowing you to work on them separately and independently.This is essentially the core functionality of React. This makes the code maintainable and it also enables you to reuse individual components repeatedly.
Consider LinkedIn as an example. You'll notice that various sections, such as the boxes, share a similar structure, even though they contain different data.
So let's create HTML file, index.html to do one task that is append Hello from HTML in an HTML page.
```javascript=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root">
<h1>Hello From HTML</h1>
</div>
</body>
</html>
```
We have created the boilerplate code. Now create a div with an ID root. Inside this root element, you can simply include an `<h1>` tag with the message "Hello from HTML."
**[Ask the learners]**
Your task is to display this message "Hello From JS" by using Vanilla JavaScript.
For doing that you want to create a script
```javascript=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root">
</div>
<script>
let greeting = document.createElement('h1')
greeting.innerHTML = 'Hello from JS'
let root = document.getElementById('root')
root.appendChild(greeting)
</script>
</body>
</html>
```
Now, what we will be doing is we will be doing the same thing using React. We will understand how React actually handles all of these things, right? Alright, so now let's move on to React. Before delving into React, we have already discussed what React is and its architecture, among other things.
Let's move to our browser and I'll just type React over here.
https://legacy.reactjs.org/docs/getting-started.html
So this is the link that you need to follow. So just open this link and you will see this particular website.
React is not inbuilt in your browser, right? So, first let me show you how you can use React with using CDN links. We'll understand this. Then we'll see what is the problem when we'll use React with CDN links. And then we'll move to creating a new React app.
As you will go to these CDN links, you will see all of these CDN scripts. You can just copy this and you can paste it in your HTML file.
```htmlembedded
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
```
**[Ask the learners]**
Now you must be wondering that why not just create a single link and you can input the React properties and Dom over there as well? Now why not to do that?
It's all about modularity. React is designed as separate modules because it serves as a comprehensive cross-platform framework, or I should say library, right? This means that in addition to React, we have React Native for mobile development. Furthermore, with React, we can create progressive web apps that can function across various platforms, including laptops, iOS, Android, and any other screen or device you might need, right?
So basically, to have all of those particular features in different particular links, we have these as separate modules. So these modules can be used anywhere. So that's why we have these two separate modules.
you'll have to inject these CDN links in your particular application. Only then you'll be able to use React. If you're not injecting these CDN links, you'll not be able to use React.
if I go to my browser and open console if I type React, now I'm able to access React and React Dom. And now you see if I press Enter, so we have an object. So whole react is just under an object. So now if I open this object, you will see different properties and different methods.
> Note to instructor - Now explain everything in that Object.
So now we'll move to our VS Code and We'll write some code
Thing that you must remember is whenever you are writing React code, just make sure that whenever you start writing React code, these CDN links should be at the top of the code.
Now let's see how we can use React.createElement() method by creating an element using this.
> Note to instructor - Explain code at each line.
```htmlembedded
<script>
let greeting = React.createElement ('h1' , {} , 'Hello From React')
console.log(greeting)
let root = ReactDOM.createRoot(document.getElementById('root'))
root.render(greeting)
</script>
```
The greeting element has been created from the render method. If I do not render this, then there will be no element inside the div.
If we want to pass attributes to the element that we want to create (let's say ID):
```htmlembedded
<script>
let greeting = React.createElement ('h1' , {id:'greeting', className:'greet'} , 'Hello From React')
console.log(greeting)
let root = ReactDOM.createRoot(document.getElementById('root'))
root.render(greeting)
</script>
```
So this is how basically, you can pass on your attributes if you want.
We have seen this that how to use create element, how to use create root, how to get your root, how to basically use the render method. We have done with all of these things.
Now Suppose I have an HTML Structure:
```htmlembedded
<div id='parent'>
<div id='child'>
<h1 id='content'>Hello from react</h1>
</div>
</div>
```
**[Ask the learners]**
Now, how can we create a similar Sturture, so can we create it?
You can follow this code
```javascript
let greeting = React.createElement(
"div",
{ id: "parent" },
React.createElement(
"div",
{ id: "child" },
React.createElement("h1", { id: "content" }, "Hello from React")
)
);
```
React.createElement("div", { id: "parent" }, ...) - This line creates a React element representing a ``<div>`` with an id attribute of "parent." The ... indicates that there are child elements inside this ``<div>``.
React.createElement("div", { id: "child" }, ...) - Inside the "parent" ``<div>``, another React element is created, representing a nested ``<div>`` with an id attribute of "child." Again, the ... indicates that there are child elements inside this nested ``<div>``.
React.createElement("h1", { id: "content" }, "Hello from React") - Inside the "child" ``<div>``, there's an h1 element with an id attribute of "content" and the text content "Hello from React."
You can clearly say that this should not be the way of writing a code, right? And to basically solve this problem, to solve this mess, to solve this hassle, we use something known as JSX and we'll be talking about JSX just after this.
Before we move forward, lets take a 1 more question.
**[Ask the learners]**
Now, If I want to add 1 more element in the same structure we have created, so can we create it?
```htmlembedded
<div id='parent'>
<div id='child'>
<h1 id='greeting'>Hello from react</h1>
<h2 id='greeting2'>Hello from react Heading 2</h1>
</div>
</div>
```
Now if we see we have two elements in same hierarchical order.
Here we can use an array.
```javascript
let greeting = React.createElement(
"div",
{ id: "parent" },
React.createElement(
"div",
{ id: "child" },
[React.createElement("h1", { id: "greeting" }, "Hello from React"),
React.createElement("h2", { id: "greeting2" }, "Hello from React Heading 2")]
)
);
```
So you can use an array and you can use these methods as each element of the Array.
But yes, this way of writing code is not recommended at all. This is very bad code. This should not be used. But for your knowledge, you must know all of these things.
I'll just create a folder and just call this as demo. So I've created a folder as demo app and in this particular folder we'll be initializing our react application.
We will now go to https://legacy.reactjs.org/docs/create-a-new-react-app.html and we'll be using Create React app moving forward .
If you want to use CDN, you'll have to make use of the Dom. You'll have to write all of these script tags, you'll have to go create different JavaScript files again and again and again. That is not recommended at all. So that's why we'll be using Create React app which will actually give you a whole boilerplate code of React and there you'll be able to work with each and everything that React has to offer.
**Ways To Initialize a React App**
* CDN Links: we talked about
* CRA (Create React App)
* Vite
We can use The following comamnd to make a React App:
```javascript
npx create-react-app demo
```
npx means node package executor, So now, if you see, this has started installing all of the things that we'll need. So I want every one of you to do this once. And after you have done, you will see all of these files that will be available inside this demo folder.
Let's move forward. So I'll just go to the Vs code and I'll just close all of these files my app is running.
Now you can see that here inside this demo folder, you have all of these files and folders.
* There is one folder as Node modules,
* There is one public folder.
* There's one source folder,
* There's a git ignore file
* There are two JSON files.
* There is one README.
All of these things are here inside this demo folder.
So first, let's start with the public folder. Let's start with the public. So let's go inside this public and ignore all of these things. They are all logos and favicons. You do not need to worry about this.
So if you go to this index.html file. So now you can see this is all your HTML code. This is all your HTML code. And here you have your root.
![](https://hackmd.io/_uploads/S1Zef8ICh.png)
So if you remember, just a while ago, we were doing this thing that everything that you were creating was going inside the root div only. Was going inside the root div only. So this is basically the starting point of your react app. Everything that you will do will go inside this root only, right? So this is that root.
So whenever you write react code and you will render all of those code, those code will be converted into elements and all of those elements will be created inside this root.
now if you see if you are clear with this root, now we'll go to the source folder and we'll see where this root has been selected.
Let's go to index.js and if you go to index.js, you see that there is a line that is actually getting the ID root.
![](https://hackmd.io/_uploads/Sk1W78IR2.png)
Third thing that you must pay your attention to is this particular render method.
![](https://hackmd.io/_uploads/B1CWmL802.png)
This says App. This App.js will be rendered inside the root, and this root will have all your HTML elements. I mean, render will convert this app JS file in the form of elements, and all of those elements will be rendered inside your root and you will have your HTML
I'll be deleting some files from this source folder as well and make sure that you delete them very carefully, because if you delete any important file, your app will be destroyed.
We can delete reportWebVitals, setupTest, appTest.js, logo.svg. These are the four files that are not needed right now.
As we have deleted reportWebVitals file, so we need to remove it from index.js else it will cause error.
![](https://hackmd.io/_uploads/B1JCNUU0h.png)
In app.js remove logo.svg.
Basically there's one command which is `npm start`.
Make sure you're on the same folder where your react app is. So it is inside this demo folder. So here you just have to write `npm start` and you just need to press Enter.
App has been started on localhost:3000.
We can update our app.js file to show some content:
![](https://hackmd.io/_uploads/HJS3SLIA2.png)
We are creating a function that is JavaScript. Whenever you create a function that is JavaScript inside this, you are returning a div which is HTML. So basically, when you use JavaScript and HTML together, that thing is known as JSX. The full form of JSX is JavaScript XML. You can just say that JSX is when you use JavaScript and HTML together, that particular thing is JSX.
And this kind of functions, this kind of functions are known as functional component. This is a functional component.
But for today, I would like to stop over here only. I don't want to confuse you more. I don't want to go in more depth today because we have already seen a lot of stuff today. So this was an introductory class to React, in which we understood that what react is.
So in the next class, we'll be seeing how components can be created. What are states, what are props, how can you work with props? And how can you basically work along with react?

View File

@@ -0,0 +1,408 @@
# Asynchronous Redux and Thunk-2
## Agenda
* Learn about Redux Thunk.
* Introduction to Redux life cycle.
* Introduction to Redux thunk life cycle.
* Modify the code done in the previous class according to the concepts taught in today's class.
## Redux
> Give a quick walk through of the previous code and the webpage designed till now.
> Revise the concepts taught in the previous class in brief.
* The principle of Redux is single source of truth.
* The state changes that are done in an application are governed by the **store**.
* Store will have the reducers that are required to manipulate the data.
Inside our products component, we are fetching our data from the below code. We use the `useEffect` hook and use `axios`.
```javascript
useEffect(()=>{
const getProducts = async ()=>{
const res = await axios.get('https://fakestoreapi.com/products')
console.log(res.data)
setProducts(res.data)
}
```
But, the data inside `useEffect` hook has to be accessed by multiple components.
> ASk the students, if it is a good way to define the data using `useEffect` hook for every use case? The answer is No.
* We can use a **Redux store** to perform these operations.
* We can do asynchronous tasks in Redux using **Redux Thunk**.
>Create a file called `productSlice.js` inside the `store` folder.
In `productSlice.js`, follow these steps.
**Step 1**- Import **`createSlice`** from redux toolkit.
```javascript
import { createSlice } from '@reduxjs/toolkit';
```
**Step 2**- We are creating a slice object `productSlice`. Inside that, there is `initialState` where we can have multiple options in the form of an objects. It is so because the API may not return success every time. There might be failures or other things received from an API.
```javascript
initialState:{
}
```
**Step 3**- The slice has the first parameter as the name which is `Product`. The `initialState` will be an object. Inside the `initialState`, the first key will be `data` which will be an array. The second key will be `status` which will show the status of page loading.
```javascript
const productSlice=createSlice({
name:'Product',
initialState:{
data:[],
status:STATUSES.SUCCESS
},
```
**Step 4**- We will define the different possibilities of `status` outside the `productSlice object`. There can be 3 possibilities-
* **Loading**- You are calling an API and it is loading.
* **Success**- The API has loaded successfully.
* **Error**- The API has encountered an error.
```javascript
const STATUSES={
SUCCESS:'success',
ERROR:'error',
LOADING:'loading'
}
```
It is a good practice to have the word `STATUSES` in capitals.
The value of `status` inside our `initialState` object will be SUCCESS.
```javascript
initialState:{
data:[],
status:STATUSES.SUCCESS
}
```
**Step 5**- We will now create reducers. This reducer will be setting a product and hence will be named as `setProduct`. It takes in two parameters- `state` and `action`. Now we will define the reducer, i.e, what it will do. The data that it will receive will be a payload entirely.
```javascript
reducers: {
setProducts(state,action){
state.data=action.payload
}
}
```
**Step 6**- To get the data, we will have to use **Thunks**. Thunks are generally functions only but they take asynchronous functions inside them and they process the reducers.
As thunks are nothing but functions, we will use the `function` keyword to define them. It returns an `async function` and a parameter names `dispatch`.
>Tell the students that there can be multiple possibilities of status which can also lead to an error. How can they be handled? Ans is by using a try and catch block.
**Step 7**- In the try block, we will get the data. The data is fetched from
```javascript
const res= await axios.get('https://fakestoreapi.com/products')
```
**Key points**
* The reason to use React Thunks is- The API calls are heavy and they take time.
* Whenever something takes a lot of time in JavaScript, that is tackled using asynchronous way.
The data needs to be dispatched to our reducer. First we need to export the `setProduct` reducer.
```javascript
export const {setProducts}=productSlice.actions
export default productSlice.reducer
```
Then we will add this line inside our try block.
```javascript!
dispatch(setProducts(res.data))
```
**Step 8**- The 2nd reducer is `setStatus`.
```javascript!
setStatus(state,action){
state.status=action.payload
}
```
First, the data is in the loading state, the status can be set as loading. It is written before the try block.
```javascript
dispatch(setStatus(STATUSES.LOADING))
```
Then, after the data is fetched, success status is returned. It is inside the try block after the data is dispatched.
```javascript
dispatch(setStatus(STATUSES.SUCCESS))
```
In the catch block, the error is caught.
```javascript
dispatch(setStatus(STATUSES.ERROR))
```
The function `fetchProducts` will be exported using the `export` keyword. Using it, we can use it anywhere in our code.
The code till now is
```javascript!
import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';
const STATUSES={
SUCCESS:'success',
ERROR:'error',
LOADING:'loading'
}
const productSlice=createSlice({
name:'Product',
initialState:{
data:[],
status:STATUSES.SUCCESS
},
reducers: {
setProducts(state,action){
state.data=action.payload
}
},
setStatus(state,action){
state.status=action.payload
}
})
export const {setProducts,setStatus}=productSlice.actions
export default productSlice.reducer
export function fetchProducts(){
return async function fetchProductThunk(dispatch){
dispatch(setStatus(STATUSES.LOADING))
try{
const res= await axios.get('https://fakestoreapi.com/products')
dispatch(setProducts(res.data))
dispatch(setStatus(STATUSES.SUCCESS))
}
catch(error){
console.log(error)
dispatch(setStatus(STATUSES.ERROR))
}
}
}
```
Now we will register our `productSlice` in `store.js`. It will be imported in `store.js` file.
```javascript
import productReducer from './productSlice'
```
And we will also use product
```javascript
const store = configureStore({
reducer : {
cart : cartReducer,
products:productReducer
}
})
```
Now we will check if our data is being reflected in our app. We will check in the inspect section under redux and refresh the page. We will see one slice.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/523/original/Screenshot_2023-09-20_182717.png?1695214750)
It is the store reducer.
In our products.js we need to import `fetchProducts`. The code-
```javascript
useEffect(()=>{
const getProducts = async ()=>{
const res = await axios.get('https://fakestoreapi.com/products')
console.log(res.data)
setProducts(res.data)
}
```
will be replaced with
```javascript
useEffect(()=>{
const getProducts = async ()=>{
dispatch(fetchProducts())
}
```
After dispatching, we will get all of the reducers.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/524/original/Screenshot_2023-09-20_182744.png?1695214775)
Now the state has been updated.
>ASk the students which hook will give the access to the whole state? Ans is `useSelector` hook. In Products.js, modify the import hook line.
```
import {useDispatch,useSelector} from 'react-redux'
```
We will need the product state as we are working on the products now. So, we will say- `useSelector` give me access to the state.
```javascript
const {data:products,status}= useSelector((state)=>state.products)
```
The names should be used from the store and not slices.
The following lines should be removed from our code. From the Products.js, we will remove the useState hook. We will also remove the import axios line. After saving, the app will look like this-
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/525/original/Screenshot_2023-09-20_182756.png?1695214791)
The data here is coming from Redux- Redux thunk.
> Give a quick recap of productSlice.js code which contains the thunk.
We are able to maintain any particular state for any component by using just the store.
### Redux Life Cycle
> Tell the students that we will be understanding the concepts of Redux by referring to the redux documentation. [Refer this](https://redux.js.org/tutorials/essentials/part-1-overview-concepts).
After scrolling down, there will be a figure-
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/526/original/Screenshot_2023-09-20_182810.png?1695214808)
Now we will understand it by comparing with our app. When we go on our app, it is our UI or User Interface. There are several buttons and thee buttons will **dispatch** actions.
So, when you click on the button- add to cart, it will trigger an event and it is dispatched as a action.
Let us see dispatch.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/527/original/Screenshot_2023-09-20_182821.png?1695214824)
* As soon as you dispatch, the action payload will go via the reducer.
* The reducer is defined inside the central store.
* The reducer will make changes to the state.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/528/original/Screenshot_2023-09-20_182829.png?1695214838)
* After the state has been updated, it will be be visible in the UI.
**This is the life cycle of Redux**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/529/original/Screenshot_2023-09-20_182838.png?1695214851)
* First step is the UI.
* Some event will occur and they will go inside the **event handler**.
>Show some examples of event handlers in the code.
* That event will be **dispatched** with the **action payload**. The **action** will be dispatched.
* The action will be going inside the **reducer**. The reducers are inside the central store where there are multiple reducers.
* These reducers will make changes to the state.
* The state will make changes to the UI.
### Redux Thunk life cycle
Just like Redux life cycle, there is Redux thunk life cycle also. The link is [here](https://redux.js.org/tutorials/essentials/part-5-async-logic).
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/530/original/Screenshot_2023-09-20_182848.png?1695214865)
* It will start from UI.
* An event will happen and it will go to the event handler.
* Action is dispatched here.
* Middleware is any function that is present inside the flow of the function. It does a particular task inside a sequence.
* As it is async, middleware will send a request to the API. The API will send a response to the middleware.
* As soon as the response is received, it will go to the reducer.
* So the reducer is making the changes to the state.
* The state is making changes to UI.
> Ask the students to relate the code with the Redux life cycle.
### Redux toolkit
This is the old way to create thunk in React.
```javascript!
export function fetchProducts(){
return async function fetchProductThunk(dispatch){
dispatch(setStatus(STATUSES.LOADING))
try{
const res= await axios.get('https://fakestoreapi.com/products')
dispatch(setProducts(res.data))
dispatch(setStatus(STATUSES.SUCCESS))
}
catch(error){
console.log(error)
dispatch(setStatus(STATUSES.ERROR))
}
}
}
```
Now as there is Redux toolkit, we can use a method provided to us by the redux toolkit. The method is **`createAsyncThunk`**.
* First import `createAsyncThunk` at the start.
* This method was created to reduce the length of the code and make it more concise.
```javascript
export const fetchProducts=createAsyncThunk('products',async()=>{
const res= await axios.get('https://fakestoreapi.com/products')
return res.data
})
```
Here, 'products' is the name of the thunk.
> Ask the students, if they wonder how the code is working if we are not sending any actions to the reducers.
There is also a change to the reducers part. We will not be defining the reducers in this way
```javascript
reducers: {
setProducts(state,action){
state.data=action.payload
}
},
setStatus(state,action){
state.status=action.payload
}
}
```
Instead, the code shown below will be used. We will use the `extraReducers` function. This function takes in `builders` as a parameter and within this function, we will define our cases.
The `builder` is similar to promises and has three cases- pending, fulfilled and rejected.
```javascript
extraReducers : (builder)=>{
builder.addCase(fetchProducts.pending, (state)=>{
state.status=STATUSES.LOADING
}).addCase(fetchProducts.fulfilled, (state,action)=>{
state.data=action.payload
state.status=STATUSES.SUCCESS
}).addCase(fetchProducts.rejected, (state)=>{
state.status=STATUSES.LOADING
})
}
```
Before running the code, import `fetchProducts` also.
Depending on the data, the callback function will return the status.
Additionally, in our products.js code, we can also see the current status by including the following
```javascript
if(status===STATUSES.LOADING){
return <h2>Loading..</h2>
}
if(status===STATUSES.ERROR){
return <h2>Something is wrong</h2>
}
```

View File

@@ -0,0 +1,310 @@
# Full Stack LLD & Projects: React-9: React Redux Interview Question 1
We will discuss some Advanced hooks which are generally asked in React Interview and are very Important to optimize your react
* Use Reducer
* Use Transition
### Explanation
In this existing project, we have made a TODO list application where if we enter the task and date, it will create a TODO task, and if we clicke on the tick option, it will mark the task as done by striking off the task.
To generate random task-ids, we have used **react-uuid** library, which can be installed easily by `npm install` command.
```javascript
const handleTask = (e) => {
e.preventDefault();
const key = e.target.name;
const value = e.target.value;
setTask({...task, [key]: value});
}
```
This above code handles user input in a TODO list project. It prevents default form submission, extracts input field name and value, and updates the task state, likely representing a new task's properties like name and description. This is a key part of adding tasks to the TODO list.
```javascript
const addTask = () => {
console.log(task);
console.log(uuid());
const updated = {...task, "id": uuid(), "isDone": false}
console.log(updated);
setList([...list, updated]);
}
```
This above code defines an `addTask` function that logs the current task state, generates a unique ID using `uuid()`, creates an updated task object with an ID and a default "isDone" status of false, logs the updated task, and finally updates the list state by appending the updated task to it. This function is typically used to add a new task to a TODO list.
```javascript
const markDone = (id) => {
console.log(`Task with ${id} is done!`);
const index = list.findIndex((task) => task.id === id);
const doneTask = [...list];
doneTask[index].isDone = true;
setList(doneTask);
}
```
This above code defines a `markDone` function that takes an id as a parameter. It logs a message indicating that a task with the specified id is marked as done. It then finds the index of the task with that id in the list array using `findIndex`, creates a copy of the list, updates the isDone property of the found task to true, and finally updates the list state with the modified task. This function is typically used to mark a task as completed in a TODO list.
```javascript
const deleteTask = (id) => {
console.log(`Task with ${id} to remove!`)
const filteredTask = list.filter((task) => task.id !== id);
setList([...filteredTask]);
}
```
This code defines a `deleteTask` function that takes an id as a parameter. It logs a message indicating that a task with the specified id is being removed. It then creates a new array filteredTask by filtering out the task with the given id from the list. Finally, it updates the list state with the filtered array, effectively removing the task with the specified id. This function is typically used to delete a task from a TODO list.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/533/original/Screenshot_2023-09-20_183518.png?1695215251)
### Explanation
As we can see, currently we are changing one particular state (ie setList) using three different functions.
In larger projects, managing state changes across multiple functions can lead to increased complexity, potential inconsistencies, and difficulties in debugging, making centralized state management solutions like Redux or context API more valuable for maintaining code quality and scalability.
So for this we can use **useReducer** hook.
Using the useReducer hook can be a beneficial approach to centralize state management in larger projects. It provides a structured way to manage complex state logic, reduce code duplication, and maintain consistency in state updates, making it a suitable choice for maintaining state in more organized and scalable manner.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/534/original/Screenshot_2023-09-20_183527.png?1695215271)
**Without useReducer:**
* **Events:** User interacts with the UI, triggering various events (e.g., clicks, inputs).
* **State:** Multiple state variables are scattered throughout the component to track different aspects of the application's data.
* **Updates:** Events directly modify these state variables, potentially leading to scattered and uncoordinated state changes.
**With useReducer:**
* **Events:** User interactions still trigger events as before.
* **Reducer:** A centralized reducer function defines how state changes in response to different actions.
* **Dispatch:** Events are dispatched with specific actions to the reducer.
* **State:** The reducer updates the state based on the action and returns a new state object.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/535/original/Screenshot_2023-09-20_183539.png?1695215335)
### Explanation
**Syntax:**
```javascript
const [state, dispatch] = useReducer(reducer, initialState);
```
**Anatomoy:**
* **State:** This represents the current state of your data, similar to state variables in `useState`. It's what you want to manage or update.
* **Initial State:** The initial value for your state before any actions are applied. It's specified when you call useReducer, like `useReducer(reducerFunction, initialState)`.
* **Dispatch Method:** A function provided by `useReducer` that you use to send actions to the reducer. Actions are objects that describe what should happen to the state.
* **Actions:** These are plain JavaScript objects that contain a type field (a string identifying the action) and optionally, a payload field carrying additional data. Actions describe what specific changes should be made to the state.
### Explanation
We will be now modifying our existing TODO list project using `useReducer`.
We will create a file `reducers.js` and start implementing the script for reducer.
```javascript
const taskReducers = (state , action) =>{
switch(action.type){
case "ADD_TASK" : {
const newTask = {...action.payload , "id" : uuid() , "isDone": false }
return [...state , newTask]
}
case "REMOVE_TASK" : {
const taskRemained = state.filter((task)=> task.id !== action.payload )
return [...taskRemained]
}
case "DONE_TASK" : {
const index = state.findIndex((task) => task.id === action.payload);
const doneTask = [...state];
doneTask[index].isDone = true;
return [...doneTask]
}
}
}
```
Here's an explanation of each reducer case in the `taskReducers` function:
1. **ADD_TASK:**
* When an action of type "ADD_TASK" is dispatched, it creates a new task object by spreading the payload (which should contain task details).
* It adds an "id" to the new task using a unique identifier (e.g., `uuid()`).
* It sets the "isDone" property to false for the new task.
* Finally, it returns a new state array by adding the new task to the existing state array.
2. **REMOVE_TASK:**
* When an action of type "REMOVE_TASK" is dispatched, it filters out the task with the specified ID from the state using `filter()`.
* It returns a new state array with the specified task removed.
3. **DONE_TASK:**
* When an action of type "DONE_TASK" is dispatched, it finds the index of the task with the specified ID in the state using `findIndex()`.
* It creates a copy of the state array.
* It updates the "isDone" property of the task at the found index to true.
* Finally, it returns a new state array with the updated task.
Similarly we will create reducers for other functions like `handleTask`.
```javascript
const formReducers = (state , action) => {
switch(action.type){
case 'HANDLE_TASK' : {
return {
...state,
[action.field] : action.payload
}
}
default :
return state
}
}
```
**HANDLE_TASK:**
* When an action of type "HANDLE_TASK" is dispatched, it updates the state.
* It creates a new state object by spreading the existing state.
* It sets a property in the new state object using the action.field as the key and action.payload as the value.
* This case is typically used to handle form input changes, where action.field represents the field name (e.g., "title" or "description") and action.payload contains the new value entered by the user.
Now we will make changes in our TODO List script. Lets name the new script as `TodoReduced.js`.
```javascript
const TodoReduced = () => {
const [state , dispatch] = useReducer(taskReducers , [])
const [task , dispatchForm] = useReducer(formReducers ,{
title: "",
by: ""
} )
```
* The first `useReducer` hook initializes the state variable to manage the TODO list items. It uses the taskReducers reducer function and an empty array [] as the initial state. This hook is responsible for managing the list of tasks, including adding, removing, and marking tasks as done.
* The second `useReducer` hook initializes the task variable to manage form input fields for creating new tasks. It uses the formReducers reducer function and an initial state object containing two fields: "title" and "by." This hook is responsible for handling changes in the form inputs where users can enter the title and creator of a new task.
Now we will modify our `handleTask` code.
```javascript
const handleTask = (e) => {
e.preventDefault();
dispatchForm({
type : 'HANDLE_TASK',
field : e.target.name,
payload : e.target.value
})
}
```
* **`e.preventDefault();`:** Prevents the default form submission behavior, ensuring that the page does not refresh when the form is submitted.
* **`dispatchForm({ type: 'HANDLE_TASK', field: e.target.name, payload: e.target.value });`:** Dispatches an action to the formReducers reducer. It specifies the action type as "HANDLE_TASK," along with the field (the name of the form input) and the payload (the new value entered by the user). This action is used to update the state related to the form inputs, like the task.
```javascript
return (
<>
<div>
<h1>My TodoList by using useReducer</h1>
<div>
I want to do <input type="text" name="title" onChange={handleTask}/> by{" "}
<input type="date" name="by" onChange={handleTask} />
<button className="wishBtn" onClick={()=> dispatch({type :"ADD_TASK" , payload : task })}>Add a Task</button>
</div>
<ul>
{state && state.length >0 && state.map((item) => (
<li key={item.id}>
<span style={{ textDecoration: item.isDone ? "line-through" : "" }}>
<strong>{item.title}</strong> is due by {item.by}</span>
<span><TiTick size={24} onClick={() => dispatch({type:"DONE_TASK" , payload :item.id})} /></span>
<span><TiTrash size={24} onClick={() => dispatch({type : "REMOVE_TASK" , payload : item.id})}/></span>
</li>
))}
</ul>
</div>
</>
);
};
```
* It renders the TODO list component, which includes an input field for the task title, an input field for the task due date, and a "Add a Task" button.
* The `onChange` event handlers (handleTask) are used to capture changes in the input fields and update the form-related state.
* It maps over the `state` array (representing the list of tasks) and displays each task with its title and due date.
* If a task is marked as done, it displays a line-through style, and there are icons to mark a task as done or delete it. These actions (`DONE_TASK` and `REMOVE_TASK`) are dispatched to update the list of tasks in the state.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/536/original/Screenshot_2023-09-20_183551.png?1695215357)
We will create a component which will take input, and print the input we gave.
```javascript
const List = () => {
const [input, setInput] = useState('')
const [list, setList] = useState([])
const [isPending , startTransition] = useTransition()
const LIST_SIZE = 11000
function handleChange(e) {
setInput(e.target.value)
startTransition(()=>{
const newList = []
for (let i = 0; i < LIST_SIZE; i++) {
newList.push(e.target.value)
}
setList(newList)
})
}
return (
<div>
<input type='text' value={input} onChange={handleChange}></input>
{
isPending? <div> Loadingg..</div> : list.map((item) => {
return <div>{item}</div>
})
}
</div>
)
}
```
This component, named `List`, renders an input field that captures user `input`. When the user types, it updates the input state. However, it also utilizes `startTransition` from the `useTransition` hook to optimize rendering for a large list (specified by `LIST_SIZE`). While `input` is being processed, it displays "Loading..." to improve user experience. After processing, it maps over the list state and displays the input value multiple times based on `LIST_SIZE`.
---

View File

@@ -0,0 +1,246 @@
## Full Stack LLD & Projects: React -10: React Redux Interview Question 2
**Agenda of this lecture:**
* Code splitting
* Lazy loading
* Higher order components
* Controlled and Uncontrolled components
### Explanation
Before jumping into code splitting we will first understand the concept of **bundling** in react.
A React application can have multiple components, each spread across different JavaScript files. When you build the application, you want to bundle these separate files into a single file for optimized delivery to the browser. Webpack is a popular tool used for this purpose. It takes all your application's code, along with its dependencies, and packages them into a single bundle file, which is efficient for loading and executing in the browser.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/537/original/Screenshot_2023-09-20_183604.png?1695215370)
Webpack helps with bundling in a React application by efficiently combining multiple JavaScript files and assets into a single bundle. It optimizes resource delivery, reduces HTTP requests, and enhances the application's loading performance.
### Code splitting
Code splitting is a technique that allows a React application to break its code into smaller, separate files or "chunks." Instead of loading the entire application code upfront, only the necessary code is loaded when a specific feature or route is accessed. This improves initial loading times and resource efficiency, enhancing the overall user experience.
Code splitting is like fetching ingredients from the fridge only when you need them to make a sandwich. In React, it means loading parts of your application's code on-demand, improving performance by reducing initial load times and resource usage.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/538/original/Screenshot_2023-09-20_183611.png?1695215389)
First lets see how data is split and then we will see how react components can be split:-
**Splitting of Data:**
The `data.js` looks like this:-
```javascript
export const moviesData = [{
id : '1',
name : 'Mission Impossible : Dead reckoning'
},
{
id : '2',
name : 'Oppenhiemer'
},
{
id : '3',
name : 'Barbie'
}
]
```
When you bundle your code, it combines all the imported modules, like data.js, into one optimized file. This makes it easier to manage and deploy your application because you only need to serve a single bundle file to the client browser.
In this the data is split using **dynamic importing** in JavaScript. Instead of loading the movie data immediately, the `getMovies` function uses the `import()` function to fetch the data from a separate file called data.js when needed. This is a form of code splitting, as it loads the data module only when required, which can help optimize performance by reducing the initial load time of your application.
### Explanation
Bundling of react components is done using **Lazy Loading**.
React components are bundled using Lazy Loading. Lazy Loading is a technique that allows you to load components only when they are needed, rather than upfront when the application initially loads.
Here's how it works:
* Each component (`Home`, `Products`, `Testimonials`, and `About`) is imported using the `lazy()` function, which dynamically imports the component files when they are required.
* This means that the code for these components is split into separate chunks and loaded on-demand, reducing the initial load time of your application.
* Lazy Loading helps optimize performance by loading only the necessary components when the user navigates to specific routes, improving the user experience and minimizing unnecessary network requests.
```javascript
const Home = lazy(()=> import('./pages/Home'))
const Products = lazy(()=> import('./pages/Products'))
const Testimonials = lazy(()=> import('./pages/Testimonials'))
const About = lazy(()=> import('./pages/About'))
```
Now we will wrap the routes:-
The lazy loading of components is related to how routes are wrapped using React's Suspense and React Router's Routes components.
the lazy loading of components is related to how routes are wrapped using React's Suspense and React Router's Routes components.
* **Suspense:** The `<Suspense>` component is used to specify a fallback UI (in this case, `<h2>Loading...</h2>`) while waiting for dynamically imported components to load. It's a key part of lazy loading as it ensures a smooth user experience by displaying a loading indicator until the requested component is ready.
* **Routes:** Inside the `<Routes>` component, various routes are defined, and each route specifies a component to render when the corresponding URL is matched. These components, such as `<Home />`, `<About />`, `<Products />`, and `<Testimonials />`, are dynamically imported using lazy loading. This means that the components will only be loaded from the server when their respective routes are accessed by the user.
```javascript
return (
<div className="App">
{/* <h1>These are the Movies</h1>
<button onClick={getMovies}>Show Movies</button>
<p>{movies.length>0 ? JSON.stringify(movies) : ''}</p> */}
{/* <Suspense fallback={<h2>Loading...</h2>}>
<BrowserRouter>
<Navbar/>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/about' element={<About/>}/>
<Route path='/products' element={<Products/>}/>
<Route path='/testimonials' element={<Testimonials/>}/>
</Routes>
</BrowserRouter>
</Suspense> */}
{/* <CompA/>
<CompA dark />
<CompA yellow/> */}
<Controlled/>
<Uncontrolled/>
</div>
);
```
### Explanation
High Order Components (HOCs) are a design pattern in React that allows you to reuse component logic and share it among multiple components. HOCs are not components themselves but functions that take a component and return a new enhanced component with additional behavior. They are a way to abstract common functionality from components and promote code reusability.
Let's use three components, A, B, and C, each with different features, to illustrate the concept:
1. Component A (3 features)
Component A is a base component with three specific features: FeatureX, FeatureY, and FeatureZ.
Component B (4 features)
2. Component B also requires the same three features as Component A (FeatureX, FeatureY, FeatureZ) and an additional feature, FeatureW.
Component C (5 features)
3. Component C needs the same three features as Components A and B (FeatureX, FeatureY, FeatureZ), plus two extra features, FeatureV and FeatureU.
```javascript
import React from 'react'
const styles = {
dark : {
background : 'black',
color : 'white'
} ,
yellow : {
background : "yellow",
color : 'red'
}
}
function HOC(WrappedComp) {
// args = [dark ,yellow]
return function (args){
let temp ={}
if(args.dark){
temp = {...styles.dark}
}
else if (args.yellow){
temp = {...styles.yellow}
}
let obj = {...args , style : temp}
return <WrappedComp {...obj}/>
}
}
export default HOC
```
This code defines a Higher Order Component (HOC) that takes another component, WrappedComp, as an argument and returns an enhanced version of it with additional styling based on the provided arguments (dark or yellow).
* The styles object contains predefined styles for both 'dark' and 'yellow' modes.
* The HOC function takes `WrappedComp` as an argument and returns a new function.
* Inside this function, it checks whether the args object contains a 'dark' or 'yellow' property.
* Depending on the property found, it applies the corresponding styles to temp.
* The style property is added to the args object with the selected styles.
* Finally, the `WrappedComp` is rendered with the updated args containing the chosen styles.
### Explanation
**Controlled Components:** Controlled components are React components where the value of an input element is controlled by the component's state. In this given below example, the `Controlled` component maintains the value of the input field (text) in its state and updates it through the `onChange` event handler. This ensures that the input's value is always synchronized with the component's state, making it a controlled component. Controlled components are useful for managing and validating user input in React applications.
```javascript
import React , {useState} from 'react'
const Controlled = () => {
const [text , setText] = useState('')
const handleSubmit = (e)=>{
e.preventDefault()
console.log(text)
}
return (
<div>
<form>
<input type='text' value={text} placeholder='controlled' onChange={(e)=>setText(e.target.value)}/>
<button onClick={handleSubmit}>Submit</button>
</form>
</div>
)
}
export default Controlled
```
**Uncontrolled Components:** Uncontrolled components are React components where the value of an input element is not controlled by React state but is directly managed by the DOM. In the given below example, the `Uncontrolled` component uses a `ref` (`inputRef`) to directly access the input field's value from the DOM when needed. Unlike controlled components, React doesn't track or manage the input value's changes in its state. Uncontrolled components are useful in cases where you need to integrate with non-React code or libraries and need direct access to DOM elements.
```javascript
import React , {useRef} from 'react'
const Uncontrolled = () => {
const inputRef = useRef(null)
const handleSubmit = (e)=>{
e.preventDefault()
console.log(inputRef.current.value)
}
return (
<div>
<form>
<input type='text' placeholder='uncontrolled' ref={inputRef}/>
<button onClick={handleSubmit}>Submit</button>
</form>
</div>
)
}
export default Uncontrolled
```

View File

@@ -0,0 +1,732 @@
# Component Architecture
- Component is a section of an architecture.
- When we divide the complete projects into small sections then that architecture is known as component architecture.
- Every section of an architecture is a separate section in itself.
**Example:**
Let us take an example of LinkedIn, in the LinkedIn search box, news, connections, etc. all are different sections, which are built separately but they work together.
## Creating Components
- Everything written in the app.js is rendered on the website.
Here let us take an example of a demo component before creating our component:
```javascript=
function App(){
return {
<div>
<h1> Hello from react</h1>
</div>
};
}
export default App
```
- Components that are created with function are known as functional components.
- Here is a function with the name `App` and this function returns a `div` which contains the `h1` tag.
### Functional Component
You can define a Javascript function and from that function, you can return JSX code and that function will be able to accept props.
**Props** is an object that can have different values for every component.
**JSX:** is a way of writing Javascript and HTML together.
**Creating First Component:**
1. Inside `src`, we will create a folder named `components`.
2. Let us create the first component inside this folder with the name `MyComponent.js`.
3. **Component names always start with a capital letter.**
4. Now open `MyComponent.js`, now we will create a functional component here with the name `MyComponent`.
5. We will create a function with the name `MyComponent`.
```javascript=
function MyComponent(){
}
```
6. This function simply returns a `div` with a `h1`.
```javascript=
function MyComponent(){
return{
<div>
<h1> My first component </h1>
</div>
}
}
```
7. Now we need to export this component using the `export` keyword.
```javascript=
function MyComponent(){
return{
<div>
<h1> My first component </h1>
</div>
}
}
export default MyComponent
```
Here `default` ensures that this component can be used anywhere inside the app.
8. Now we need to import the component inside the main component i.e. `app.js`.
9. We need to write the below line inside `app.js` to import `MyComponent`.
```javascript=
import MyComponent from './components/MyComponent';
function App(){
return {
<div>
<h1> Hello from react</h1>
</div>
};
}
export default App
```
10. Now we will use `MyComponent` inside the `App` component of the `App.js` file.
```javascript=
import MyComponent from './components/MyComponent';
function App(){
return {
<div>
<h1> Hello from react</h1>
<MyComponent/>
</div>
};
}
export default App
```
**Creating Second Component**
1. Create a file name `MyComponent2.js` inside the `components` folder.
2. Open the `MyComponent2.js` file.
3. Now type `rfce` to create a functional component automatically.
4. Here `rfce` stands for react functional component export.
5. After typing `rfce` press enter, and then a functional component is created automatically.
6. And below code will appear automatically.
```javascript=
import React from 'react'
function MyComponent2(){
return{
<div>MyComponent2</div>
}
}
export default MyComponent2
```
7. Now we just have to write our content inside this component.
```javascript=
import React from 'react'
function MyComponent2(){
return{
<div>
<h3>this is my component2</h3>
</div>
}
}
export default MyComponent2
```
8. Now we can import MyComponent2 in `app.js` by writing the below line inside `app.js` to import `MyComponent2`.
```javascript=
import MyComponent2 from './components/MyComponent2';
import MyComponent from './components/MyComponent';
function App(){
return {
<div>
<h1> Hello from react</h1>
<MyComponent/>
</div>
};
}
export default App
```
9. Now we will use `MyComponent2` inside the `App` component of the `App.js` file.
```javascript=
import MyComponent2 from './components/MyComponent2';
import MyComponent from './components/MyComponent';
function App(){
return {
<div>
<h1> Hello from react</h1>
<MyComponent/>
<MyComponent2/>
</div>
};
}
export default Appexport default App
```
10. We can see that we can use multiple components in a single app component.
# JSX
- JSX is not HTML. We can use also javascript inside it.
**Always write javascript code above the return statement.**
Let us take an example of the `MyComponent.js` file.
1. Open `MyComponent.js` file.
2. Declare one javascript variable inside it.
```javascript=
function MyComponent(){
const name = 'Steve'
return{
<div>
<h1> My first component </h1>
</div>
}
}
export default MyComponent
```
3. We can use this javascript variable inside the return statement.
```javascript=
function MyComponent(){
const name = 'Steve'
return{
<div>
<h1> My name is {name} </h1>
</div>
}
}
export default MyComponent
```
4. Now the value of the name variable i.e. `Steve` will be displayed in output.
5. This is only possible due to **JSX**, which allows us to combine JavaScript with HTML, as it is not possible with HTML only.
6. As we defined variable in javascript, we can also define a function, which can be called.\
```javascript=
function MyComponent(){
const name = 'Steve'
const showMessage =()=>{
return 'hello this is a message from showMessage function'
}
return{
<div>
<h1> My name is {name} {showMessage()} </h1>
</div>
}
}
export default MyComponent
```
7. Now the value returned by the function is always displayed in output.
# Component Reusability
- Components can be reused in react.
- And according to our use case we can pass and modify data by something called **props** and **state**.
**Reusing a component:**
We can use a single component multiple times.
```javascript=
import MyComponent2 from './components/MyComponent2';
import MyComponent from './components/MyComponent';
function App(){
return {
<div>
<h1> Hello from react</h1>
<MyComponent/>
<MyComponent/>
<MyComponent/>
<MyComponent/>
<MyComponent2/>
</div>
};
}
export default App
```
As in this, all the components have the same data, we can use the same component again and again but we can also pass different data to component by **props.**
## Props
- Props stands for properties.
- And every component can have different properties.
1. Let us create a new component `UserProfile`, and create a file `UserProfile.js` inside the components folder.
2. Now inside `UserProfile.js` write `rfce` and press enter and then write our content.
```javascript=
import React from 'react'
function UserProfile(){
return{
<div>
<h3>Name: Steve, Age: 25, Occupation: Software Engineer</h3>
</div>
}
}
export default UserProfile
```
3. Now import this component in `app.js` file.
```javascript=
import UserProfile from './components/UserProfile';
function App(){
return {
<div>
<UserProfile/>
</div>
};
}
export default App
```
4. Now we want to reuse the `UserProfile` component.
5. Now we will use props.
6. And we will access props using the key inside it.
```javascript=
import React from 'react'
function UserProfile(props){
return{
<div>
<h3>Name: {props.name}, Age: {props.age}, Occupation: {props.occupation}</h3>
</div>
}
}
export default UserProfile
```
7. And then we will pass different values for calling the component.
```javascript=
import UserProfile from './components/UserProfile';
function App(){
return {
<div>
<UserProfile name='Steve' age={25} occupation='Software Engineer'/>
<UserProfile name='John' age={35} occupation='Civil Engineer'/>
<UserProfile name='Mark' age={40} occupation='Businessmen'/>
</div>
};
}
export default App
```
**Integer value always passed in {}**
Now we can see that we are using the same component three times with different data. So our output will be something like this.
![](https://hackmd.io/_uploads/r1Ppvg-02.png)
# Disadvantages of Props
**Props are immutable**, which means we can not change it at the component level.
Whatever value is coming from the main component, we need to use only that value, we can not change it inside the component.
# State
- There are two types of components in react, one is a class-based component and another is a functional component.
- Functional component is also known as state-less component, but **hooks** are introduced that allow functional components to have access to state and other React features.
# Hooks
Hooks are introduced that allow functional components to have access to state and other React features.
The first hook is `useState`.
# Event handling
We just want to create a button, we want that when that button is clicked then a message is displayed on the console.
1. Let us create a new component named `Event.js`, so create a file named `Event.js` inside the components folder.
2. Inside this component we will create a button.
```javascript=
import React from 'react'
function Event(){
return{
<div>
<h1> Event handling</h1>
<button>click me</button>
</div>
}
}
export default Event
```
2. We need to import this component inside the `App` component.
```javascript=
import Event from './components/Event';
function App(){
return {
<div>
<Event/>
</div>
};
}
export default App
```
3. Now for performing event handling(when the button is clicked then a message is displayed on the console), we will create a function inside the `Event` component.
```javascript=
import React from 'react'
function Event(){
const handleClick=()=>{
console.log('Button was clicked')
}
return{
<div>
<h1> Event handling</h1>
<button>click me</button>
</div>
}
}
export default Event
```
4. Now we need to call this function on the `onClick` event of the button.
```javascript=
import React from 'react'
function Event(){
const handleClick=()=>{
console.log('Button was clicked')
}
return{
<div>
<h1> Event handling</h1>
<button onClick={handleClick}>click me</button>
</div>
}
}
export default Event
```
5. Now when we click on this a message is displayed on the console.
# Hooks
- **Functional component are stateless component**, means it does not has any state,
- **Class Based components are stateful components.**
**Hooks are there to implement all the functionality of the state in the functional component.**
First, we will learn the `useState` hook.
# useState
We will create a simple application, which has two buttons one button will increment the value and another button will decrement the value.
1. We will create a component named `Counter`, so create a file named `Counter.js` inside the components folder.
2. Inside `Counter.js`, write `rfce` and press enter.
```javascript=
import React from 'react'
function Counter(){
return{
<div>
<h1> Counter</h1>
</div>
}
}
export default Counter
```
3. Import the `Counter` component inside the `App` component.
```javascript=
import Counter from './components/Counter';
function App(){
return {
<div>
<Counter/>
</div>
};
}
export default App
```
4. Now we will create two buttons inside the `Counter` component one for increment and another one for decrement, and we will also display the count in the `Counter` component.
```javascript=
import React from 'react'
function Counter(){
return{
<div>
<h2>The count value is</h2>
<button>Increment</button>
<button>Decrement</button>
</div>
}
}
export default Counter
```
5. **useState hook is a function that is inbuilt react, and it will give two values one value is variable and another value is function.**
6. First we need to import `useState`.
```javascript=
import React from 'react'
import {useState} from 'react'
function Counter(){
return{
<div>
<h2>The count value is</h2>
<button>Increment</button>
<button>Decrement</button>
</div>
}
}
export default Counter
```
## Syntax of useState
```javascript=
const[variable, function]=useState()
```
Whatever value is passed inside the useState that will be used for initialization of the variable, like in the below example variable will be initialized with 0.
```javascript=
const[variable, function]=useState(0)
```
7. Inside our functional component, we will use `useState`, and initialize count by 0.
```javascript=
import React from 'react'
import {useState} from 'react'
function Counter(){
const[count, setCount] = useState(0)
return{
<div>
<h2>The count value is </h2>
<button>Increment</button>
<button>Decrement</button>
</div>
}
}
export default Counter
```
8. We will create increment and decrement functions, and call those functions on the `onClick` event of buttons.
```javascript=
import React from 'react'
import {useState} from 'react'
function Counter(){
const[count, setCount] = useState(0)
function increment(){
setCount(count+1)
}
function decrement(){
setCount(count-1)
}
return{
<div>
<h2>The count value is </h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
}
}
export default Counter
```
9. We will display this `count` value inside h2.
```javascript=
import React from 'react'
import {useState} from 'react'
function Counter(){
const[count, setCount] = useState(0)
function increment(){
setCount(count+1)
}
function decrement(){
setCount(count-1)
}
return{
<div>
<h2>The count value is {count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
}
}
export default Counter
```
Now if we see the output our count value is initially displayed as 0, and when we click the increment button, the value will increment, and then the incremented value will be displayed, and when we click the decrement button, then the value will decrement, and then the decremented value will be displayed,
10. We can also set the condition that the count will not be decremented only if its value is 0.
```javascript=
import React from 'react'
import {useState} from 'react'
function Counter(){
const[count, setCount] = useState(0)
function increment(){
setCount(count+1)
}
function decrement(){
if(count===0){
setCount(count)
}
else{
setCount(count-1)
}
}
return{
<div>
<h2>The count value is {count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
}
}
export default Counter
```
# Destructuring
- destructuring values inside the props.
- Props is an object, that has a key-value pair.
- Now we will do destructuring inside the `UserProfile` component.
```javascript=
import React from 'react'
function UserProfile(props){
// destructuring
const {name, age, occupation} = props
return{
<div>
<h3>Name: {props.name}, Age: {props.age}, Occupation: {props.occupation}</h3>
</div>
}
}
export default UserProfile
```
- Now we can access values directly by the variable.
```javascript=
import React from 'react'
function UserProfile(props){
// destructuring
const {name, age, occupation} = props
return{
<div>
<h3>Name: {name}, Age: {age}, Occupation: {occupation}</h3>
</div>
}
}
export default UserProfile
```
- We can also do destructuring in this way.
```javascript=
import React from 'react'
function UserProfile({name, age, occupation}){
return{
<div>
<h3>Name: {name}, Age: {age}, Occupation: {occupation}</h3>
</div>
}
}
export default UserProfile
```
# DOM
- DOM is a tree.
![](https://hackmd.io/_uploads/SJwxcbZA2.png)
- **DOM Manipulations/Operations:** In these elements we can perform some operations, like remove them, add them, insert some data inside them, etc.
- When the DOM manipulation happens, then the whole DOM gets rerendered.
- **Re-Rendering:** When we change anything in a single element, then the whole tree is re-rendered.
- And re-rendering of the whole DOM is not so good, it makes the application slow.
- **To overcome this problem, react introduced virtual DOM.**
# Virtual DOM
Virtual DOM is in memory copy of actual DOM.
- Let us assume we have a real DOM.
![](https://hackmd.io/_uploads/ryDwiWW03.png)
- Now react will create a copy of this DOM.
![](https://hackmd.io/_uploads/H1odobZCh.png)
- Now whenever any change happens in any particular element, then react will first make changes in the virtual DOM, it will not do changes directly in the real DOM.
- Let us assume a `h2` element is changed, then react will do these changes in virtual DOM.
![](https://hackmd.io/_uploads/SJPZ3b-C2.png)
- Now again any other changes happen in any element, let us suppose the `p` element is changed.
- So React will make changes in another virtual DOM.
![](https://hackmd.io/_uploads/Bycr3ZZR3.png)
- **React will keep a record of all the changes and keep creating a virtual DOM at every change.**
- And last virtual DOM has all the changes.
- So according to the final/last virtual DOM, it will re-render the real DOM only once. This process that virtual DOM follows is known as **batch processing.**
And whole process of creating a virtual DOM at every updation, keeping a copy of every virtual DOM and re-rendering the real DOM once is known as **Reconcillation**.
And algorithm used by virtual DOM to compare all the virtual DOMs is known as **diffing algo**

View File

@@ -0,0 +1,783 @@
# Lists in React
Let us suppose we have a list of products,
`productArray = [laptop, headphones, mouse]`
1. Let us create a component named `Products`, so we will create a file named `Products.js` inside the components folder.
2. Now open `Products.js`, then type `rfce` and press enter.
```javascript
import React from 'react'
function Products(){
return{
<div>Products</div>
}
}
export default Products
```
3. Let us assume we have a `products` array, which contains products.
```javascript
import React from 'react'
function Products(){
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
return{
<div>Products</div>
}
}
export default Products
```
4. Now we want to show all the elements of the `products` array in the HTML.
5. The first way is to simply iterate and display every element.
```javascript
import React from 'react'
function Products(){
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
return{
<div>
for(let i = 0;i < products.length; i++){
}
</div>
}
}
export default Products
```
Here syntax is correct, but it will give an error, as JSX does not allow us to write a loop in this way. In place of loops, we can higher-order functions.
6. We will use `map` with an array, by which we get access to all the array elements one by one. We will create a list element of every element one by one.
```javascript
import React from 'react'
function Products(){
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
return{
<div>
{products.map((product) => {
return <li>{product}</li>
})}
</div>
}
}
export default Products
```
7. Now we need to import the `Products` component in `App`.
```javascript=
import Products from './components/Products';
function App(){
return {
<div>
<Products/>
</div>
};
}
export default App
```
When we run this, and we will see the output we will get the correct output, but a warning is also there in the console.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/271/original/upload_9301c0d61c4a7303710b0a2e45d03b3b.png?1695460739)
This warning says that every child in a list should have a unique 'key' prop.
- React says whenever you create a list element, you should provide a unique key to it so that when React updates the DOM, it can uniquely identify what is required to be updated.
The key is a unique ID.
So we will create a key for every list element, and we are creating the key with the product name, as every product has a different name.
```javascript
import React from 'react'
function Products(){
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse']
return{
<div>
{products.map((product) => {
return <li key = {product}>{product}</li>
})}
</div>
}
}
export default Products
```
- But in case, we have two products with the same name, `let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse', 'Laptop']`
```javascript
import React from 'react'
function Products(){
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse', 'Laptop']
return{
<div>
{products.map((product) => {
return <li key = {product}>{product}</li>
})}
</div>
}
}
export default Products
```
- Now the problem will occur, as the two products have the same name, so the key will not remain unique as the same laptop key is assigned to two products.
- Now we will again get a warning `encountered two children with a same key laptop`.
- So we can use index, `map()` will also have access to all the indexes. And every array element has a unique index, starting from 0, 1, 2, 3, .....
- So we can make the key using the index.
```javascript
import React from 'react'
function Products(){
let products = ['Laptop', 'Headphones', 'Keyboard', 'Mouse', 'Laptop']
return{
<div>
{products.map((product, index) => {
return <li key = {index}>{product}</li>
})}
</div>
}
}
export default Products
```
## Using objects
1. We will create an array of objects for products.
```javascript=
import React from 'react'
function Products(){
let productsList = [
{ id: 1, name: "Laptop", price: 35000},
{ id: 2, name: "Headphones", price: 12000},
{ id: 3, name: "Mouse", price: 8000},
{ id: 4, name: "Keyboard", price: 10000},
]
return{
<div>
</div>
}
}
export default Products
```
2. Now we will display this product list in HTML using `map`. Now we are using the `id` of every object of an array for creating the key and then we are displaying the name and price of that particular product.
```javascript=
import React from 'react'
function Products(){
let productsList = [
{ id: 1, name: "Laptop", price: 35000},
{ id: 2, name: "Headphones", price: 12000},
{ id: 3, name: "Mouse", price: 8000},
{ id: 4, name: "Keyboard", price: 10000},
]
return{
<div>
{
productsList.map((product) => {
return <li key = {product.id}>{product.name} : {product.price}</li>
})
}
</div>
}
}
export default Products
```
**Output:**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/050/272/original/upload_6e03052c93e9520de75f963309b5ce9a.png?1695460907)
- We can also remove the return keyword written before `<li>`, but then we need to enclose the list element creation code in `()`, instead of `{}`.
```javascript=
import React from 'react'
function Products(){
let productsList = [
{ id: 1, name: "Laptop", price: 35000},
{ id: 2, name: "Headphones", price: 12000},
{ id: 3, name: "Mouse", price: 8000},
{ id: 4, name: "Keyboard", price: 10000},
]
return{
<div>
{
productsList.map((product) => (
<li key = {product.id}>{product.name} : {product.price}</li>
))
}
</div>
}
}
export default Products
```
# Form Handling in React
- **Form** is a layout which will accept data from the user.
- Form has text boxes, labels, checkboxes, buttons, etc. by which the user can provide some input.
- Then the user will click on submit, then all the data will go to the database, or be sent for some processing.
**In react, we can also work with forms**.
1. Let us create a component named `Form`, so first we will create a file named `Form.js` inside the components folder.
2. Now open `Form.js`, then type `rfce` and press enter.
```javascript
import React from 'react'
function Form(){
return{
<div>Form</div>
}
}
export default Form
```
3. Let us create a form by the `<form>` tag, and inside that form, we will create one label and input field.
```javascript
import React from 'react'
function Form(){
return{
<div>
<h1>This is a form</h1>
<form>
<label>firstName</label>
<input type = "text" value = "firstName" ></input>
</form>
</div>
}
}
export default Form
```
4. Now we need to import the `Form` component in `App`.
```javascript
import Form from './components/Form';
function App(){
return {
<div>
<Form/>
</div>
};
}
export default App
```
- But now if we try to change the data of the input field we are not able to change it, but in HTML if we create an input field we can change the data of it.
- This is because props are immutable. And `value` of the `input` field is behaving like props, and that's why we are not able to update it.
- And if we want to change the value of prop, then we need to use state.
- But in the functional components we don't use state, so in place of it **we will use the useState hook**.
1. Firstly we need to import the `useState` hook.
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
return{
<div>
<h1>This is a form</h1>
<form>
<label>firstName</label>
<input type = "text" value = "firstName" ></input>
</form>
</div>
}
}
export default Form
```
2. Now we will use the `useState()` hook.
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
const [firstName, setFirstName] = useState('')
return{
<div>
<h1>This is a form</h1>
<form>
<label>firstName</label>
<input type = "text" value = {firstName} ></input>
</form>
</div>
}
}
export default Form
```
3. We have initialized `firstName` with an empty string, but for updating the value of `firstName` we will create a function `handleChange`.
Whenever there is a change in the value of the input field this function will be called so we will call this function on the `onChange` event of the input field and this function will receive an event as `e`.
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
const [firstName, setFirstName] = useState('')
let handleChange = (e) => {
}
return{
<div>
<h1>This is a form</h1>
<form>
<label>firstName</label>
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
</form>
</div>
}
}
export default Form
```
4. Inside this function we will call the `setFirstName` with the new typed value so that the `firstName` will be updated to a new typed value.
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
const [firstName, setFirstName] = useState('')
let handleChange = (e) => {
setFirstName(e.target.value)
}
return{
<div>
<h1>This is a form</h1>
<form>
<label>firstName</label>
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
</form>
</div>
}
}
export default Form
```
**Submitting form data**
1. Creating a submit button.
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
const [firstName, setFirstName] = useState('')
let handleChange = (e) => {
setFirstName(e.target.value)
}
return{
<div>
<h1>This is a form</h1>
<form>
<label>firstName</label>
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
<button>Submit Form Button</button>
</form>
</div>
}
}
export default Form
```
2. We will create a function `handleSubmit` and call it on the `onSubmit` event of the form. And `handleSubmit` will also receive the event as a parameter. Inside that function, we will use `e.preventDefault()` to avoid automatic refreshing of the page and we will print `firstName` inside this function.
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
const [firstName, setFirstName] = useState('')
let handleChange = (e) => {
setFirstName(e.target.value)
}
let handleSubmit = (e) => {
e.preventDefault()
console.log(firstName)
}
return{
<div>
<h1>This is a form</h1>
<form onSubmit = {handleSubmit}>
<label>firstName</label>
<input type = "text" onChange = {handleChange} value = {firstName} ></input>
<button>Submit Form Button</button>
</form>
</div>
}
}
export default Form
```
# Form with firstName and lastName
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
let handleFirstNameChange = (e) => {
setFirstName(e.target.value)
}
let handleLastNameChange = (e) => {
setLastName(e.target.value)
}
let handleSubmit = (e) => {
e.preventDefault()
console.log(firstName, lastName)
}
return{
<div>
<h1>This is a form</h1>
<form onSubmit = {handleSubmit}>
<label>firstName</label>
<input type = "text" onChange = {handleFirstNameChange} value = {firstName} ></input>
<label>lastName</label>
<input type = "text" onChange = {handleLastNameChange} value = {lastName} ></input>
<button>Submit Form Button</button>
</form>
</div>
}
}
export default Form
```
We can also print data on the console in object form.
```javascript
import React from 'react'
import {useState} from 'react'
function Form(){
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
let handleFirstNameChange = (e) => {
setFirstName(e.target.value)
}
let handleLastNameChange = (e) => {
setLastName(e.target.value)
}
let handleSubmit = (e) => {
e.preventDefault()
console.log(
{
fName : firstName,
lName : lastName
})
}
return{
<div>
<h1>This is a form</h1>
<form onSubmit = {handleSubmit}>
<label>firstName</label>
<input type = "text" onChange = {handleFirstNameChange} value = {firstName} ></input>
<label>lastName</label>
<input type = "text" onChange = {handleLastNameChange} value = {lastName} ></input>
<button>Submit Form Button</button>
</form>
</div>
}
}
export default Form
```
# useEffect hook
When there are only class-based components in react, then there are life cycle methods.
**Life Cycle Methods of a Component**
1. Component Did Mount
2. Component Did Update
3. Component Did Unmount.
**Component Did Mount:** When react renders the component for the first time.
**Component Did Update:** When you do some updation in the component.
**Component Did Unmount:** Removing component from the life cycle.
All these life cycle methods are used by class-based components.
Functional Components handles all the processes using a single hook i.e. `useEffect` hook.
1. Let us create a component named `Ue1`, so first we will create a file named `Ue1.js` inside the components folder.
2. Now open `Ue1.js`, then type `rfce` and press enter.
```javascript
import React from 'react'
function Ue1(){
return{
<div>Ue1</div>
}
}
export default Ue1
```
3. Now we need to import the `Ue1` component in `App`.
```javascript
import Ue1 from './components/Ue1';
function App(){
return {
<div>
<Ue1/>
</div>
};
}
export default App
```
4. In `Ue1` we will simply create a counter. We will display the count value and create a button for incrementing the count value.
```javascript
import React from 'react'
function Ue1(){
return{
<div>
<h1>This is my Count value :</h1>
<button>increment</button>
</div>
}
}
export default Ue1
```
5. We will use `useState` to update the value of the count on click on the increment button.
```javascript
import React from 'react'
import {useState} from 'react'
function Ue1(){
const[count, setCount] = useState(0)
let incrementCount = () => {
setCount(count + 1)
}
return{
<div>
<h1>This is my Count value : {count}</h1>
<button onClick = {incrementCount}>increment</button>
</div>
}
}
export default Ue1
```
## Syntax of useEffect
```javascript
useEffect(() => {
// operations
})
```
## ComponentMounting and ComponentUpdation
1. Before using `useEffect`, we need to import it. After that, we will `useEffect`.
```javascript
import React from 'react'
import {useState, useEffect} from 'react'
function Ue1(){
const[count, setCount] = useState(0)
let incrementCount = () => {
setCount(count + 1)
}
useEffect(()=>{
console.log('use Effect Runs')
document.title = `Button clicked for ${count} times`
})
console.log('Other code that gets executed')
return{
<div>
<h1>This is my Count value : {count}</h1>
<button onClick = {incrementCount}>increment</button>
</div>
}
}
export default Ue1
```
But when we see the output of it the title is updated after some delay in the updation of the heading.
This is because `useEffect` is by default asynchronous hook, so it will run at the last.
- If we check our console, then first `Other code that gets executed` will be printed then `use Effect Runs` will be printed.
- The `useEffect` code runs at the end, firstly all other code will be executed.
This is because it takes care of two things at a time i.e. `Component Did Mount` and `Component Did Update`.
- When there is any updation in an application, then `useEffect` will take care of it.
## ComponentMounting only
- If we add an empty dependency array in 'useEffect`.
```javascript
import React from 'react'
import {useState, useEffect} from 'react'
function Ue1(){
const[count, setCount] = useState(0)
let incrementCount = () => {
setCount(count + 1)
}
useEffect(() => {
console.log('use Effect Runs')
document.title = `Button clicked for ${count} times`
}, [])
console.log('Other code that gets executed')
return{
<div>
<h1>This is my Count value : {count}</h1>
<button onClick = {incrementCount}>increment</button>
</div>
}
}
export default Ue1
```
Now the title will not be updated at every button click. This is because
**useEffect without dependency array, then it does:**
- Component Did Mount
- Component Did Update
**useEffect with dependency array( empty dependency array), then it do only:**
- Component Did Mount
`useEffect` with dependency array(empty dependency array) runs only once and only component mounting is there.
## ComponentMounting and ComponentUpdation based on events and values
1. Now we will create an input field and we will create `useState` for updating its value.
```javascript
import React from 'react'
import {useState, useEffect} from 'react'
function Ue1(){
const[count, setCount] = useState(0)
const[text, setText] = useState('')
let incrementCount = () => {
setCount(count + 1)
}
useEffect(() => {
console.log('use Effect Runs')
document.title = `Button clicked for ${count} times`
})
console.log('Other code that gets executed')
return{
<div>
<h1>This is my Count value : {count}</h1>
<input type = 'text' value = {text}></input>
<button onClick = {incrementCount}>increment</button>
</div>
}
}
export default Ue1
```
2. We want that whenever we write anything inside this input field then it will be printed in `h2`. We will create `handleChange` to update the value of the text, and this will be called on the `onChange` of the input field.
```javascript
import React from 'react'
import {useState, useEffect} from 'react'
function Ue1(){
const[count, setCount] = useState(0)
const[text, setText] = useState('')
let incrementCount = () => {
setCount(count + 1)
}
let handleChange = (e) => {
setText(e.target.value)
}
useEffect(() => {
console.log('use Effect Runs')
document.title = `Button clicked for ${count} times`
})
console.log('Other code that gets executed')
return{
<div>
<h1>This is my Count value : {count}</h1>
<input onChange = {handleChange} type = 'text' value = {text}></input>
<h2>{text}</h2>
<button onClick = {incrementCount}>increment</button>
</div>
}
}
export default Ue1
```
- Here `useEffect` will run for every updation in the text field and for every button click.
- Now if we want that `useEffect` will only run when we click on the button to increment the count value.
- Then we can pass count in the dependency array, and then the `useEffect` will run only on the updation of the count value.
```javascript
import React from 'react'
import {useState, useEffect} from 'react'
function Ue1(){
const[count, setCount] = useState(0)
const[text, setText] = useState('')
let incrementCount = () => {
setCount(count + 1)
}
let handleChange = (e) => {
setText(e.target.value)
}
useEffect(() => {
console.log('use Effect Runs')
document.title = `Button clicked for ${count} times`
},[count])
console.log('Other code that gets executed')
return{
<div>
<h1>This is my Count value : {count}</h1>
<input onChange = {handleChange} type = 'text' value = {text}></input>
<h2>{text}</h2>
<button onClick = {incrementCount}>increment</button>
</div>
}
}
export default Ue1
```

View File

@@ -0,0 +1,543 @@
# IMDB Clone React Website
## Project Description
We will be creating an IMDB clone, where we will fetch Real-time trending movies data and will show it in grid format, we will be designing the whole application by using Tailwind CSS.
## Features of the project
The following will be the features of our IMDB clone:
- The user will be able to view all the latest & trneding movies (IMDB API)
- User can create his own separate watchlist
- User can filter movies according to genre
- User can sort movies according to ratings
- Pagination will be implemented to move from one page to another to get updated data of other movies
- Search feature will be there for movies.
- We will be deploying this to netlify
## Wireframe
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/481/original/Screenshot_2023-09-20_170326.png?1695209627)
## Implementation
### What is tailwind css?
Tailwind CSS is a highly popular and innovative utility-first CSS framework for web development. Unlike traditional CSS frameworks that provide pre-designed components, Tailwind CSS focuses on providing a comprehensive set of utility classes that make it easier to create custom and responsive designs without the need for writing custom CSS.
**Following are features of Tailwind CSS:**
1. **Utility-First Approach:** Tailwind CSS adopts a utility-first approach, offering a vast collection of small, single-purpose utility classes that can be combined to create complex designs and layouts. This approach promotes code reusability and rapid development.
2. **Customizable and Configurable:** Tailwind is highly customizable through a configuration file, allowing developers to tailor the framework to match their project's specific design requirements. You can customize everything from colors and spacing to typography and breakpoints.
3. **Responsive Design Made Easy:** Creating responsive web designs is simplified with Tailwind CSS. It provides responsive variants of utility classes, enabling developers to adapt the layout and styling of their websites for various screen sizes and devices effortlessly.
4. **Performance Optimization:** Tailwind CSS is designed with performance in mind. It generates optimized CSS files by purging unused classes, resulting in smaller file sizes and faster loading times for web pages.
5. **Active Community and Ecosystem:** Tailwind CSS has a thriving community of developers who contribute to its growth and share resources. Additionally, there are numerous plugins and extensions available that enhance Tailwind's capabilities, making it suitable for a wide range of web development projects.
In this application we will be integrating tailwind to our react app.
- **tailwind.confing.js**
```javascript=
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
```
### Src
- **index.js**:
- In this code snippet, we're diving into the world of web development with React, a popular JavaScript library for building user interfaces.
- We start by importing some essential tools: `React` and `ReactDOM`. These are the building blocks of React applications. Think of them as the magic wand and canvas, respectively, for creating interactive web experiences.
- We also import a CSS file, './index.css'. This file likely contains styles to make our IMDb web page look visually appealing and engaging.
- The star of our show is the `App` component, which represents the heart and soul of our IMDb website. We import it from './App', indicating that this component will be responsible for displaying our movie and TV show information.
- Now, let's set the stage. We create a special variable called `root` using `ReactDOM.createRoot(document.getElementById('root'))`. This variable acts as our spotlight, focusing on a specific area of the webpage where our IMDb content will appear.
- Finally, it's showtime! We call `root.render(<App />)`, which is the grand moment when our `App` component takes center stage and begins rendering. This means it starts building the entire IMDb web page, showing movie posters, cast lists, and everything else our users want to see.
**Code**:
```javascript=
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
```
- **index.css**:
Certainly, here's the provided CSS code snippet with an explanatory introduction:
- **index.css**
- This CSS code snippet is all about styling our IMDb web page. CSS, or Cascading Style Sheets, is used to control the visual appearance of web content.
- The code begins with `@tailwind` directives. These directives are used in conjunction with the Tailwind CSS framework. They help in defining the base styles, components, and utilities for our web page. Tailwind CSS is a utility-first CSS framework that makes it easy to style web applications.
- Following the Tailwind directives, we have styles for the `body` element. These styles define how the main content of our IMDb webpage should look. Key aspects include:
- Setting the margin to 0 to remove any default spacing around the page.
- Specifying a font-family to ensure a consistent and readable text display on various devices and browsers.
- Enabling font-smoothing and grayscale to enhance text readability and visual quality.
- Next, there are styles for the `code` element. This is likely used for displaying code snippets or inline code within our IMDb web page. The specified font-family ensures that code is displayed in a monospace or fixed-width font, making it easy to read and distinguish from regular text.
**Code**:
```css=
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
```
- **App.js**:
Certainly, here's the code explanation for the provided `App.js` code:
- **App.js**
- This JavaScript file is a central part of our IMDb web application. It's where we define how the entire app works and what components it consists of.
- We start by importing several components and libraries that our app depends on:
- `import './App.css';` imports the CSS styles specific to our `App` component, which are likely used to style the layout and appearance of the app.
- `Movies`, `WatchList`, `Navbar`, and `Banner` are components imported from their respective files. These components are used to build various parts of our IMDb app, such as displaying movies, the watchlist, the navigation bar, and a banner.
- `BrowserRouter`, `Route`, and `Routes` are imported from 'react-router-dom'. These are essential for setting up client-side routing, allowing us to navigate between different pages in our app without a full page reload.
- Inside the `App` function, we set up various pieces of functionality for our IMDb app:
- We use `useState` to create two state variables, `watchList` and `pageNo`. These states help manage the user's watchlist and the current page number.
- There are also functions like `handlePrev` and `handleNext` to handle pagination by changing the `pageNo` state when users click on previous or next buttons.
- `handleAddToWatchList` and `handleRemoveFromWatchList` functions allow users to add or remove movies from their watchlist. These functions update the `watchList` state and store the updated watchlist in local storage.
- The `useEffect` hook is used to retrieve the watchlist data from local storage when the component mounts, ensuring that users don't lose their watchlist when they revisit the app.
- The `return` statement defines the structure of our IMDb app using the `BrowserRouter` and `Routes`. Inside the routes, we render various components based on the route path:
- On the root path ('/'), we render the `Banner` and `Movies` components, passing them necessary props like `watchList`, `pageNo`, and functions for adding/removing from the watchlist.
- On the '/watchlist' path, we render the `WatchList` component, providing it with the user's watchlist and functions to remove movies from it.
- The entire `App` function represents the main structure of our IMDb web application, orchestrating the rendering of different components and handling user interactions.
**Code**:
```javascript=
import './App.css';
import Movies from './Components/Movies';
import WatchList from './Components/WatchList';
import Navbar from './Components/Navbar';
import Banner from './Components/Banner';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import {useState,useEffect} from "react"
function App() {
let [watchList,setWatchList] = useState([]);
let [pageNo,setPageNo] = useState(1);
let handlePrev = ()=>{
if(pageNo>1)
setPageNo(pageNo-1)
}
let handleNext = ()=>{
setPageNo(pageNo+1);
}
let handleAddToWatchList = (movieObj)=>{
// console.log("Inside add to watchlist");
// console.log(movieId);
// watchList.push(movieId); // it will not work since the reference is same
let newWatchList = [...watchList,movieObj];
localStorage.setItem("movieApp",JSON.stringify(newWatchList));
setWatchList(newWatchList);
}
let handleRemoveFromWatchList = (movieObj)=>{
let filteredWatchList = watchList.filter((movie)=>{
return movie.id != movieObj.id;
})
localStorage.setItem("movieApp",JSON.stringify(filteredWatchList));
setWatchList(filteredWatchList);
}
useEffect(()=>{
let moviesFromLocalStorage = localStorage.getItem("movieApp");
if(!moviesFromLocalStorage){
return;
}
setWatchList(JSON.parse(moviesFromLocalStorage));
},[])
return (
<BrowserRouter>
<Navbar/>
<Routes>
<Route path='/' element={
<>
<Banner/>
<Movies watchList={watchList}
setWatchList={setWatchList}
handleAddToWatchList={handleAddToWatchList}
handleRemoveFromWatchList={handleRemoveFromWatchList}
pageNo={pageNo}
handleNext={handleNext}
handlePrev={handlePrev}/>
</>
}></Route>
<Route path='/watchlist' element={
<WatchList watchList={watchList}
setWatchList={setWatchList}
handleRemoveFromWatchList={handleRemoveFromWatchList}/>
}
></Route>
</Routes>
</BrowserRouter>
// <>
// <Navbar/>
// <Banner/>
// <Movies/>
// {/* <WatchList/> */}
// </>
);
}
export default App;
```
- **App.css**
- This CSS code snippet is used to style elements within our IMDb web application. It defines various styles to ensure that the app looks visually appealing and functions correctly.
- The `.App` class sets the text alignment to the center. This class is likely associated with the top-level element of our app.
- `.App-logo` is used to style the IMDb logo. It sets the height to 40vmin (a relative unit based on the viewport size) and disables pointer events. This is often used for logos or images.
- The `@media` query checks if the user prefers reduced motion. If not, it applies an animation to `.App-logo` to make it spin infinitely for 20 seconds in a linear fashion. This adds a dynamic touch to the logo when motion is allowed.
- `.App-header` styles the header section of our IMDb app. It sets the background color, minimum height, and configures it to be a flex container with content centered both horizontally and vertically. The font size is adjusted relative to the viewport size, and the text color is set to white.
- `.App-link` styles links in the app with a blue color (#61dafb). This is a common styling for clickable links.
- Finally, `@keyframes App-logo-spin` defines an animation that smoothly rotates an element from 0 degrees to 360 degrees. This is used in conjunction with `.App-logo` to create the spinning effect of the IMDb logo.
**Code**:
```css=
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
```
#### Utility
```javascript=
const genreids = {
28: "Action",
12: "Adventure",
16: "Animation",
35: "Comedy",
80: "Crime",
99: "Documentary",
18: "Drama",
10751: "Family",
14: "Fantasy",
36: "History",
27: "Horror",
10402: "Music",
9648: "Mystery",
10749: "Romance",
878: "Sci-Fi",
10770: "TV",
53: "Thriller",
10752: "War",
37: "Western",
};
export default genreids;
```
#### Components
- **Banner.jsx**:
- This JavaScript file defines the `Banner` component for our IMDb web application. The `Banner` typically appears at the top of the web page and often showcases a prominent image or information related to the content.
- Inside the component function, we use the `useState` and `useEffect` hooks from React to manage component state and perform side effects.
- `useState` initializes a state variable called `detail` with an initial value of `undefined`. This variable will hold data related to the movie we want to display in the banner.
- The `useEffect` hook is used to make an HTTP GET request to the 'https://api.themoviedb.org/3/movie/upcoming' endpoint. This endpoint likely retrieves information about upcoming movies from a movie database. The API key '2816c138913c6ef73d40c883d36fbe56' is included in the request for authentication.
- When the response is received successfully, it extracts data from the first movie in the results array and sets it to the `detail` state variable.
- The `if` statement checks if `detail` is still undefined. If it is, the component returns nothing (undefined). This likely prevents the component from rendering until it has retrieved movie data from the API.
- If `detail` has a value (i.e., movie data has been fetched), the component renders:
- A `div` element with a class that sets the background image of the banner. The `url()` function in the `style` attribute dynamically sets the background image using the `poster_path` property from the `detail` data.
- Inside this `div`, there's another `div` element that contains text. This is likely the movie title displayed on top of the banner image.
- The styling includes making the text white, applying a semi-transparent gray background, and centering the text both horizontally and vertically. The `text-xl` class sets the font size to extra-large.
- The `Banner` component fetches data about an upcoming movie and displays it as a banner on the IMDb webpage, creating an attractive and engaging visual element for users.
**Code**:
```javascript=
import axios from "axios";
import { useEffect, useState } from "react";
function Banner(){
let [detail,setDetail] = useState(undefined)
useEffect(()=>{
axios.get('https://api.themoviedb.org/3/movie/upcoming?api_key=2816c138913c6ef73d40c883d36fbe56')
.then(function(res){
// console.log(res);
// console.log(res.data.results[0].poster_path);
let data = res.data.results[0]
setDetail(data);
})
},[]);
if(detail == undefined){
return
}
return(
<div className="h-[20vh] md:h-[70vh] bg-cover bg-center flex items-end"
style={{backgroundImage:`url(https://image.tmdb.org/t/p/original/${detail.poster_path})`}}>
<div className="text-xl text-white bg-gray-900/60 w-full p-4 text-center ">
{detail.title}
</div>
</div>
)
}
export default Banner;
```
- **MovieCard.jsx**:
- This JavaScript file defines the `MovieCard` component. A `MovieCard` is a visual representation of a movie that users can interact with in our IMDb web application.
- The component function takes several props:
- `movieObj`: An object containing information about the movie, including its `id`.
- `handleAddToWatchList`: A function to add the movie to the user's watchlist.
- `handleRemoveFromWatchList`: A function to remove the movie from the user's watchlist.
- `name`: The name or title of the movie.
- `watchList`: An array representing the user's watchlist.
- `poster_path`: A URL to the movie's poster image.
- Inside the component, there's a function `isContain(movieObj)` that checks if the `movieObj` is already in the user's watchlist. It iterates through the `watchList` array and returns `true` if it finds a movie with the same `id`, indicating that the movie is in the watchlist. Otherwise, it returns `false`.
- The component's `return` statement defines the structure and appearance of a `MovieCard`. It's a `div` element with the following features:
- A fixed height and width (`h-[40vh]` and `w-[200px]`) to maintain a consistent size for movie cards.
- The `bg-center` and `bg-cover` classes ensure that the background image (the movie poster) is centered and covers the entire card.
- The `rounded-xl` class rounds the corners of the card, giving it a pleasant look.
- On hover, the card slightly scales up (`hover:scale-110`) and the cursor changes to a pointer (`hover:cursor-pointer`), providing a visual cue that the card is interactive.
- Inside the card, there's a dynamic icon that changes based on whether the movie is in the user's watchlist or not. If the movie is in the watchlist, it displays a "remove" icon (❌), and clicking it calls the `handleRemoveFromWatchList` function. If the movie is not in the watchlist, it displays an "add" icon (😁), and clicking it calls the `handleAddToWatchList` function.
- Below the icon, there's a text element displaying the movie's name or title. It has a white text color, a semi-transparent gray background, and is centered both horizontally and vertically. The `text-xl` class sets the font size to extra-large.
- This `MovieCard` component is a reusable card for displaying movie information and interacting with the user's watchlist. It provides a visually appealing and user-friendly way to add or remove movies from the watchlist.
**Code**:
```javascript=
export default function MovieCard(props){
let {movieObj,handleAddToWatchList,handleRemoveFromWatchList,name,watchList,poster_path} = props;
function isContain(movieObj){
for(let i=0;i<watchList.length;i++){
if(watchList[i].id == movieObj.id){
return true;
}
}
return false;
}
return(
<div className="h-[40vh] w-[200px] bg-center bg-cover
rounded-xl hover:scale-110 duration-300 hover:cursor-pointer
flex flex-col justify-between items-end overflow-hidden"
style={{
backgroundImage: `url(https://image.tmdb.org/t/p/original/${poster_path})`,
}}>
{isContain(movieObj)?
<div onClick={()=>handleRemoveFromWatchList(movieObj)} className="m-4 bg-gray-900
flex justify-center items-center
h-8 w-8 rounded-lg">
&#10060;
</div>
:<div onClick={()=>handleAddToWatchList(movieObj)}
className="m-4 bg-gray-900
flex justify-center items-center
h-8 w-8 rounded-lg">
&#128525;
</div>
}
<div className="text-xl text-white
bg-gray-900/60 w-full p-2 text-center ">
{name}
</div>
</div>
)
}
```
- **Movies.jsx**
- This JavaScript file defines the `Movies` component, which is responsible for displaying a list of trending movies on our IMDb web application.
- The component function takes several props:
- `watchList` and `setWatchList`: These props are used to manage the user's watchlist.
- `handleAddToWatchList` and `handleRemoveFromWatchList`: Functions for adding and removing movies from the watchlist.
- `pageNo`: The current page number, which is used for pagination.
- `handleNext` and `handlePrev`: Functions for navigating to the next and previous pages of trending movies.
- Inside the component, there's a `useState` hook that initializes a state variable `movies` as an empty array. This variable will store the list of trending movies fetched from an API.
- The `useEffect` hook is used to make an HTTP GET request to the 'https://api.themoviedb.org/3/trending/movie/day' endpoint. The `pageNo` prop is included in the request to retrieve a specific page of trending movies. The API key '2816c138913c6ef73d40c883d36fbe56' is used for authentication.
- When the response is received successfully, it extracts the list of trending movies from `res.data.results` and sets it to the `movies` state variable.
- The component's `return` statement defines the structure of the Movies component:
- A title ("Trending Movies") displayed in a large and bold font at the top.
- A flex container (`flex flex-wrap justify-around gap-8`) that arranges MovieCard components in a responsive grid layout. Each `MovieCard` represents a trending movie.
- Inside the flex container, a `.map()` function is used to iterate through the `movies` array and generate a `MovieCard` component for each movie. The `key` prop is set to the movie's `id` for React's reconciliation.
- The `MovieCard` component is passed various props, including movie information, watchlist data, and functions for adding and removing movies from the watchlist.
- After displaying the list of movies, a `Pagination` component is rendered. This component allows users to navigate between pages of trending movies using the `pageNo`, `handleNext`, and `handlePrev` props.
- The `Movies` component is responsible for rendering trending movies and providing user-friendly interactions for adding/removing movies from the watchlist and pagination.
**Code**:
```javascript=
import MovieCard from "./MovieCard";
import axios from "axios"
import {useEffect, useState} from "react"
import Pagination from "./Pagination";
function Movies(props){
let {watchList,setWatchList,
handleAddToWatchList,handleRemoveFromWatchList,
pageNo,handleNext,handlePrev} = props;
let [movies,setMovies] = useState([]);
useEffect(()=>{
axios.get(`https://api.themoviedb.org/3/trending/movie/day?api_key=2816c138913c6ef73d40c883d36fbe56&page=${pageNo}`)
.then(function(res){
// console.log(res);
// console.log(res.data.results);
setMovies(res.data.results);
})
},[pageNo])
return(
<div className="p-5">
<div className="text-2xl m-5 font-bold text-center">
Trending Movies
</div>
<div className="flex flex-wrap justify-around gap-8">
{movies.map((movieObj)=>{
// console.log(movieObj);
return <MovieCard key={movieObj.id}
movieObj={movieObj}
name={movieObj.title}
poster_path={movieObj.poster_path}
watchList = { watchList}
handleAddToWatchList = {handleAddToWatchList}
handleRemoveFromWatchList = {handleRemoveFromWatchList}/>
})}
</div>
<Pagination pageNo={pageNo}
handleNext={handleNext}
handlePrev={handlePrev}/>
</div>
)
}
export default Movies;
```
- **Navbar.jsx**
- This JavaScript file defines the `Navbar` component, which represents the navigation bar at the top of the IMDb web application. The navigation bar typically contains links to different sections or pages of the website.
- Inside the component function, we use the `Link` component from the 'react-router-dom' library to create navigation links. This allows users to navigate between different sections of the IMDb website without a full page reload, providing a seamless user experience.
- The component renders the following elements:
- An `img` element with a source (`src`) that appears to be a base64-encoded image. This is likely the IMDb logo. The `alt` attribute is empty, indicating that this is a decorative image without alternative text.
- A `Link` component with a `to` prop pointing to the root path ("/") of the application. This link represents the "Movies" section of the website. It has a large font size, a bold font-weight, and a hover effect that changes the cursor to a pointer, making it clear that it's clickable.
- Another `Link` component with a `to` prop pointing to the "/watchlist" path. This link represents the "Watchlist" section of the website. Similar to the "Movies" link, it has a large font size, bold font-weight, and a hover effect.
- The `Navbar` component provides essential navigation options, allowing users to easily switch between the "Movies" and "Watchlist" sections of the IMDb website, enhancing the overall user experience.
```javascript=
import { Link } from "react-router-dom";
function Navbar(){
return(
<div className="flex items-center p-3">
<img className="h-[50px] mx-6" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAflBMVEX///8AAACrq6tTU1MnJyd/f3/X19cEBAT29vaTk5P8/Pz5+fnGxsYJCQkXFxdkZGQsLCyampoyMjLNzc12dna+vr7p6em3t7ddXV1ra2s5OTkjIyPj4+McHBzw8PCioqJERERISEiHh4eUlJRxcXFNTU09PT2mpqaDg4MYGBiakx2lAAAFqklEQVR4nO2c6ZaiMBCFEVpww12x3de25/1fcJBuSVWCkoARzsz9fk2TAXOh6qYqeHQcAAAAAAAAAAAAAAAA+G+Y++uqp1CadfR92TQajc2uU/VUCrN2p4d+I2Va9XwK0PGv56+wIdGrelpGdCejz6WiIWFQ9dx0afVOg3GQqSFhWPUENdjO2ovhEw0JQavqaT5lHh29VY6EuifJOmom1qpLu+oJZ+Bza9VjUfWsGR1/l2GtWtQm259YqxZ1yPY8a9Wj2mzXslaJzaWZ5QKVZbuBtd4JPhbH2dxxzlmDVWT72jWz1phw+Tnq/aZBO/N/vDnbC1hrfz+9+qROd+ngOPWHt2V7MWsNr3LbNKPXWM2X6b/fkO3FrTWI5Gv59Dp93/lM/7Cb7a1eu4y1juTrrWluhfFDGKV/Wcv27ey4+DCc+MpjJtBUrjmmw258ZJL+ZSPbC1hrrOE42zoneki5x909HU5iqZs+7hdnewFrbdyWh21yNnOkQ1e++IIO//bp4hm9LNv963RvaK3BcNHubdMr9Ggmj5U7PKWn3h/XID3ygmwvZq1eu8fn6tPbsJrLn8LCbt9VjpbL9uLW+ilfak7TKnZWiYia3zB9jD1xrKiGctbqyZtqLepIoRLwLOw2Yplslcr2AtYaDNlzW8qf2jnQYVf+RBZ2IX1cw/SwWbbPo2/jqnU8iCs+FuIfSgYM6LCStyzsghkdWjw+6xHdqHn5Y6bhVrVOkrxkztpXNp6bdFjZAWVh17iyMVEM62a7a+atsYbdJE0EFuLhRL72iJ6pTIiH3VHc2cloIOpf3WyPDEQszzufJTMLcbUUZI60VxZCFnY/bpfVH2tm+7ChzVk+l4V4YycPT+jjEs5655ue7HVit3zQH2tlu6+vw5PP5SH+LQ+v6ePaKOmzoycH3hO3POkI0Y+sLzk0eIgrG+dzOrXw+UL4HKXqz+LREwnjTtR9Hhqs1jsoC+GSjHJnvTExKBuUm5BFR73gn0PTvQUC63bU0GDOqpSCHY8OX6XReWRglXsdHY5Db1zcPXxH9zVtS20gx1nVUvCTDh/JQFxMm+1TrDTfiopPXMxo9LBuRw0NFuJqKXikU/l1u1aBQjQYH3VLLXFn+cLLMkBxVr4QKv54pSfHdeStiDPdegyTCkhThUO74z09zLqdo3xSzkI4o3MOp8a9ZVI9KOtnDiLbQ3KUbfs9bzFUd/RLbL+H+/PVL/Z+XaxqItJZKai2GMwglFJwbfoAfukfpm6ZrzuIbE8zgWWA2mJc6McrpeDaoOi5s7k0o9Jf2RDZfg8hlgE5LQYrBdcF3q6tvGOkfEQhRLYvfw7wJlu5UazWG98d23cL7bXMlHqhOGIvLEySgTfZykLIar3N7V761/PeeHkYnEysVQ8x8dusWSmY02KErvl+kWguX46I+ZEjZcDzFsOU8EtuzF6LyPaBVAqqLYZhb59ys1atIrYMItvHvBRUWoyt6Tb8jdhaSy0P+pCdb5dmwIVGgbQhoMfrrFUPke1UR9piFHphLrbi38ggayZJi/F4Q+AxsbXK29jv4pQxnf7k9tLf8DFYtFYtellzMtVg2Vq1aJX7xkjyxrxiCb8UqFfJs/iwy/Ci//3fRf50K2Wv6x3ZXwOpEbpfm83K9loRaC6rraonmotShT+gSA31VpR3dg/w8i9VLRBSN8yFBHWijJCa1Bo/NCEEQuwAIRBiCQiBEEtACIRYAkIgxBIQAiGWgBAIsQSEQIglIARCLAEhEGIJCIEQS0AIhFgCQiDEEhACIZaAEAixBIRAiCUgBEIsASEQYgkIgRBLQAiEWAJCIMQSEAIhloAQCLFEGSEHr0Z8lBBSUyCkbkBI3dAVMs2/VLXorgoGP5ZbCWNNHdLPatUO9QfkHtMu81Nmlhkb6HCc1sytKbWqmgAAAAAAAAAAAAAAAP8SfwGtIGmvToPJOQAAAABJRU5ErkJggg==" alt="" />
<Link to={"/"} className="text-3xl mx-6 font-bold text-sky-600 hover:cursor-pointer">Movies</Link>
<Link to={"/watchlist"} className="mx-6 text-3xl font-bold text-sky-600 hover:cursor-pointer">WatchList</Link>
</div>
)
}
export default Navbar;
```

View File

@@ -0,0 +1,201 @@
# IMDB Clone React Website
## Project Description
We will be creating an IMDB clone, where we will fetch Real-time trending movies data and will show it in grid format, we will be designing the whole application by using Tailwind CSS.
## Features of the project
The following will be the features of our IMDB clone:
- The user will be able to view all the latest & trneding movies (IMDB API)
- User can create his own separate watchlist
- User can filter movies according to genre
- User can sort movies according to ratings
- Pagination will be implemented to move from one page to another to get updated data of other movies
- Search feature will be there for movies.
- We will be deploying this to netlify
## Wireframe
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/481/original/Screenshot_2023-09-20_170326.png?1695209627)
## Implementation
> **Note**: This lecture is in continuation to *'Full Stack LLD & Projects: React-4: IMDB Project- Part 1'*, please refer that lecture before hopping onto this one.
In this lecture we will be implementing the following features:
- Briefing Client side Routing and React Router
- Watchlist Component
- Pagination
### Component
- **WatchList.jsx**
- This JavaScript file defines the `WatchList` component, which represents the user's watchlist section in the IMDb web application. Users can view and manage the list of movies they have added to their watchlist.
- The component function takes several props:
- `watchList`: An array containing the user's watchlist of movies.
- `handleRemoveFromWatchList`: A function to handle the removal of movies from the watchlist.
- `setWatchList`: A function to update the watchlist state.
- The component also uses local state variables to manage filtering and searching of watchlist movies. These state variables include `genreList`, `currGenre`, and `search`.
- Inside the component, there are several functions and effects that manage various aspects of the watchlist:
- `hanldeFilter(genre)`: Updates the current genre filter when a genre is clicked.
- `handleSearch(e)`: Updates the search query when the user types in the search input.
- `sortIncreasing()`: Sorts the watchlist in increasing order of movie ratings.
- `sortDecreasing()`: Sorts the watchlist in decreasing order of movie ratings.
- The `useEffect` hook is used to populate the `genreList` state with unique genres based on the movies in the watchlist. It ensures that the genre filter options are updated whenever the watchlist changes.
- The component's `return` statement defines the structure of the WatchList component:
- Genre filter buttons are displayed at the top. Users can click on these buttons to filter the watchlist by genre.
- A search input field allows users to search for movies in the watchlist based on their titles.
- A table displays the watchlist movies, including columns for movie name, ratings, popularity, genre, and a delete option.
- The table rows are generated based on the current genre filter and search query.
- The ratings column headers allow users to sort the watchlist in ascending or descending order based on movie ratings.
- The delete option in each row allows users to remove a movie from their watchlist.
- The `WatchList` component provides essential functionality for users to manage and view their watchlist, making it a valuable feature of the IMDb web application.
**Code**:
```javascript=
import { useEffect, useState } from "react";
import genreids from "../Utility/genre";
function WatchList(props){
let {watchList,handleRemoveFromWatchList,setWatchList} = props;
let [genreList,setGenreList] = useState(["All Genres"]);
let [currGenre,setCurrGenre] = useState("All Genres");
let [search,setSearch] = useState("");
let hanldeFilter = (genre)=>{
setCurrGenre(genre)
}
let handleSearch = (e)=>{
setSearch(e.target.value);
}
let sortIncreasing = ()=>{
let sorted = watchList.sort((movieA,movieB)=>{
return movieA.vote_average-movieB.vote_average
})
setWatchList([...sorted]);
}
let sortDecreasing = ()=>{
let sorted = watchList.sort((movieA,movieB)=>{
return movieB.vote_average-movieA.vote_average
})
setWatchList([...sorted]);
}
useEffect(()=>{
let temp = watchList.map((movieObj)=>{
return genreids[movieObj.genre_ids[0]];
})
temp = new Set(temp);
setGenreList(["All Genres",...temp]);
},[watchList])
return(
<>
<div className="flex justify-center flex-wrap m-4">
{genreList.map((genre)=>{
return <div key={genre} onClick={()=>hanldeFilter(genre)} className={
currGenre == genre?"hover:cursor-pointer flex justify-center items-center w-[9rem] h-[3rem] rounded-xl bg-blue-400 m-4 text-white font-bold "
:"hover:cursor-pointer flex justify-center items-center w-[9rem] h-[3rem] rounded-xl bg-gray-400/50 m-4 text-white font-bold "}>{genre}</div>
})}
</div>
<div className="flex justify-center my-4">
<input onChange={handleSearch} value={search} className="h-[3rem] w-[18rem]
border-none outline-none bg-gray-200
px-4 text-lg " type="text" placeholder="Search for Movies" />
</div>
<div className="overflow-hidden rounded-lg shadow-md border border-gray-200 m-8">
<table className="w-full text-gray-500 text-center ">
<thead className="bg-gray-50 text-gray-900 border-b-2">
<tr>
<th>Name</th>
<th className="flex">
<div onClick={sortIncreasing} className="p-2"><i className="fa-solid fa-arrow-up"></i></div>
<div className="p-2">Ratings</div>
<div onClick={sortDecreasing} className="p-2"><i className="fa-solid fa-arrow-down"></i></div>
</th>
<th>Popularity</th>
<th>Genre</th>
<th></th>
</tr>
</thead>
<tbody className="text-gray-700">
{watchList.filter((obj)=>{
if(currGenre == "All Genres"){
return true;
}else{
return genreids[obj.genre_ids[0]] == currGenre;
}
})
.filter((movieObj)=>{
return movieObj.title.toLowerCase().includes(search.toLocaleLowerCase());
})
.map((movieObj)=>{
return <tr className="border-b-2">
<td className="flex items-center px-6 py-4">
<img className="h-[6rem] w-[10rem]" src={`https://image.tmdb.org/t/p/original/${movieObj.poster_path}`} alt="" />
<div className="mx-4 ">{movieObj.title}</div>
</td>
<td>{movieObj.vote_average}</td>
<td>{movieObj.popularity}</td>
<td>{genreids[movieObj.genre_ids[0]]}</td>
<td onClick={()=>handleRemoveFromWatchList(movieObj)} className=" text-red-600">Delete</td>
</tr>
})}
</tbody>
</table>
</div>
</>
)
}
export default WatchList;
```
- **Pagination.jsx**:
- This JavaScript file defines the `Pagination` component, which is responsible for rendering pagination controls for navigating between pages of trending movies on the IMDb web application.
- The component function takes three props as arguments:
- `pageNo`: The current page number, indicating the page of trending movies being displayed.
- `handleNext`: A function to handle the next page button click event.
- `handlePrev`: A function to handle the previous page button click event.
- The component renders the following elements:
- A `div` element with a set of CSS classes to style the pagination container. It is flex-based, horizontally centered, and has a background color of gray (#808080). This container holds the pagination controls.
- Inside this container, there are three `div` elements representing the pagination controls:
1. The first `div` contains a left arrow icon, which is likely a visual cue for users to go to the previous page of trending movies. It has padding (`px-8`) for spacing and a hover effect that changes the cursor to a pointer when hovered over. The `onClick` event handler is set to `handlePrev`, indicating that clicking this element will trigger the `handlePrev` function.
2. The second `div` displays the current page number (`pageNo`). It is styled with a bold font weight for emphasis.
3. The third `div` contains a right arrow icon, which serves as a control for users to navigate to the next page of trending movies. Similar to the left arrow, it has padding, a hover effect, and an `onClick` event handler set to `handleNext`.
- The `Pagination` component provides a simple and intuitive way for users to move between pages of trending movies. It enhances the user experience by allowing them to explore a variety of movie options conveniently.
**Code**:
```javascript=
export default function Pagination({pageNo,handleNext,handlePrev}){
return(
<div className="flex justify-center p-4 mt-8 items-center bg-gray-400">
<div onClick={handlePrev} className="px-8 hover:cursor-pointer "><i className="fa-solid fa-arrow-left"></i></div>
<div className="px-8 font-bold hover:cursor-pointer">{pageNo}</div>
<div onClick={handleNext} className="px-8 hover:cursor-pointer"><i className="fa-solid fa-arrow-right"></i></div>
</div>
)
}
```

View File

@@ -0,0 +1,386 @@
Today's agenda will primarily revolve around working on the watchlist component. However, before diving into the watchlist, we'll address a few tasks related to the movie component. In our current project, we've already implemented features like pagination and banners, showcasing various movies. Now, we're at the stage where we want to provide users with the ability to take actions, such as adding a movie to their watchlist.
At this moment, our watchlist is empty, so we'll focus on designing and developing this component.
Let's return to the code. We'd like to include an "Add to Watchlist" button for each movie card, as shown here:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/495/original/Screenshot_2023-09-20_171047.png?1695210312)
> Note to instructor - Help students understand and brainstorm this feature.
Now, let's navigate to the movies section and pick up from where we left off to add the watchlist functionality.
#### Pseudocode
```javascript=
function Movies() {
const [watchList, setWatchList] = useState([]);
}
```
We will start by initializing this as an empty array. One by one, we will add the IDs of all the movies, and then we will retrieve the movies based on their IDs.
**[Ask the leaners]**
Now, how can we implement the process of clicking on a movie to obtain its ID and store it in the watchlist? Do you have any suggestions?
One approach is to use filtering based on the movie's ID. To begin, we will add a button to our movie card. Inside the card div, we will create another div to accommodate this button.
#### Pseudocode
```jsx=
<div className="">
😀
</div>
```
This div is intended for the button. For now, we're just adding an emoji. Let's proceed to style it.
#### Pseudocode
```jsx=
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
😀
</div>
```
If a movie has not been added to the watchlist, you should display an emoji. If it's already on the watchlist, you should display a cross sign. As of now, the watchlist is empty.
The purpose of adding a movie to the watchlist is that when we click on the emoji, a function should be triggered to provide us with the ID of the movie we've clicked on.
#### Pseudocode
```javascript=
// watchList handlers
const addToWatchList = () => {
const newWatchList = [...watchList, id];
setWatchList(newWatchList);
}
```
Let's explore how the spread operator functions.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/497/original/Screenshot_2023-09-20_171534.png?1695210341)
> Note to instructor - Help students understand this.
Now, let's shift our focus back to our div. We are simply going to invoke this.
**[Ask the leaners]**
What should be passed as the argument in this context?
The movie ID should be passed as the argument here.
#### Pseudocode
```jsx=
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
<div onClick={() => addToWatchList(movie.id)}>
😀
</div>
</div>
```
We have wrapped the addToWatchList(movie.id) function call in an arrow function to ensure it's triggered correctly when clicked. So, when you click on this emoji, it will trigger a click event that, in turn, activates the "addToWatchList" function and retrieves the specific ID of the movie.
> Note to instructor - Feel free to test this functionality out.
To demonstrate how the spread operator works, we will create a "test.js" file and write the code accordingly.
#### Pseudocode
```javascript=
let arr = [1, 2, 3, 4, 5];
let updatedArr = [...arr, 6];
console.log(updatedArr);
```
> Note to instructor - Feel free to test this and help students with the concept of spread operator.
Next, we will examine the functionality of a cross button for removing items from the watchlist, involving conditional code.
#### Pseudocode
```jsx=
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
{watchList.includes(movie.id) === false ? (
<div onClick={() => addToWatchList(movie.id)}>
😀
</div>
) : (
<div>
</div>
)}
</div>
```
If the movie ID is not present in my watchlist, then for those movies, display the "Add to Watchlist" button. Otherwise, show the "Remove from Watchlist" button.
At a high level, here are the steps we've taken:
* Added the "Add to Watchlist" button.
* When clicked, the ID of that movie is added to the watchlist array.
* Created the onClick event and the addToWatchList function.
* Now, we also need a feature to remove movies from the watchlist.
* To achieve this, we will create the removeFromWatchList function, which will filter out the specified ID from the watchlist.
Now, we can proceed to create the "Remove from Watchlist" function.
#### Pseudocode
```javascript=
// watchList handlers
const addToWatchList = (id) => {
const newWatchList = [...watchList, id];
setWatchList(newWatchList);
}
const removeFromWatchList = (id) => {
const filteredWatchList = watchList.filter((watchListId) => {
return watchListId !== id;
});
setWatchList(filteredWatchList);
}
```
> Note to instructor - Feel free to test this.
> Note to instructor - Feel free to wait and address any questions that students might have.
Now, let's write the code that allows us to remove a movie from our watchlist when we click on the cross button.
#### Pseudocode
```jsx=
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2">
{watchList.includes(movie.id) === false ? (
<div onClick={() => addToWatchList(movie.id)}>
😀
</div>
) : (
<div onClick={() => removeFromWatchList(movie.id)}>
</div>
)}
</div>
```
> Note to instructor - Feel free to test this.
> Note to instructor - Feel free to wait and address any questions that students might have.
**[Ask the leaners]**
We don't want these buttons to be shown by default and would like them to appear only when we hover over the movie cards. Do you have any suggestions for this?
-> In React, everything is controlled by the state. So, we can create a state to manage the visibility of these buttons.
#### Pseudocode
```javascript=
function Movies() {
const [hovered, setHovered] = useState('');
}
```
Now, we will create two functions:
#### Pseudocode
```javascript=
const showButton = (id) => {
setHovered(id);
}
const hideButton = (id) => {
setHovered('');
}
```
We can create an additional div element for hovering over a movie.
#### Pseudocode
```jsx=
<div
onMouseEnter={() => showButton(movie.id)}
onMouseLeave={() => hideButton()}
>
</div>
```
Now, we'll apply CSS to show this element only when the movie ID matches.
#### Pseudocode
```jsx=
<div className="p-2 bg-gray-900 rounded-xl absolute right-2 top-2" style={{ display: hovered === movie.id ? 'block' : 'none' }}>
{watchList.includes(movie.id) === false ? (
<div onClick={() => addToWatchList(movie.id)}>
😀
</div>
) : (
<div onClick={() => removeFromWatchList(movie.id)}>
</div>
)}
</div>
```
> Note to instructor - Wait for 5-10 minutes for the break
Now, let's proceed to the Watchlist component. Let's start by defining its visual design.
> Note to instructor - Help students understand and brainstorm this.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/498/original/Screenshot_2023-09-20_171609.png?1695210376)
#### Pseudocode
```jsx=
function WatchList() {
let movies = {
// some movie details here
}
return (
<div className="overflow-hidden rounded-lg border border-gray-200 shadow-md m-5">
<table className="w-full border-collapse bg-white text-left text-sm text-gray-500">
<thead>
<tr className="bg-gray-50">
<th className="px-6 py-4 font-medium text-gray-900">Name</th>
<th>
<div className="flex">
<div>Ratings</div>
</div>
</th>
<th>
<div className="flex">
<div>Popularity</div>
</div>
</th>
<th>
<div className="flex">
<div>Genre</div>
</div>
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100 border-t border-gray-100">
<tr className="hover:bg-gray-50">
<td className="flex items-center px-6 py-4 font-normal text-gray-900">
<img className="h-[6rem] w-[10rem] object-fit" src="" alt="" />
<div className="font-medium text-gray-700 text-sm">Star Wars</div>
</td>
<td className="pl-6 py-4">
7.8
</td>
<td className="pl-6 py-4">
7.8
</td>
<td className="pl-2 py-4">
Action
</td>
</tr>
</tbody>
</table>
</div>
);
}
export default WatchList;
```
> Note to instructor - Please feel free to test if everything is functioning correctly up to this point.
Now, we'd like to incorporate this data. Inside the table body, We will utilize the movies array:
#### Pseudocode
```jsx=
function WatchList() {
let movies = [
// Some movie details here
];
return (
<div className="overflow-hidden rounded-lg border border-gray-200 shadow-md m-5">
<table className="w-full border-collapse bg-white text-left text-sm text-gray-500">
<thead>
<tr className="bg-gray-50">
<th className="px-6 py-4 font-medium text-gray-900">Name</th>
<th>
<div className="flex">
<div>Ratings</div>
</div>
</th>
<th>
<div className="flex">
<div>Popularity</div>
</div>
</th>
<th>
<div className="flex">
<div>Genre</div>
</div>
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100 border-t border-gray-100">
{movies.map((movie) => (
<tr className="hover:bg-gray-50" key={movie.id}>
<td className="flex items-center px-6 py-4 font-normal text-gray-900">
<img className="h-[6rem] w-[10rem] object-fit" src="" alt="" />
<div className="font-medium text-gray-700 text-sm">{movie.title}</div>
</td>
<td className="pl-6 py-4">
{movie.vote_average}
</td>
<td className="pl-6 py-4">
{movie.popularity}
</td>
<td className="pl-2 py-4">
Action
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default WatchList;
```
> Note to instructor - Input some data into the movies array and replace all the hardcoded data. Afterward, test this component to ensure it functions correctly.
* Now, the WatchList component displays a list of movies.
* It uses the movie array to populate the movie details.
* Each movie is displayed within a table row (<tr>).
* Movie data such as title, vote average, and popularity are shown in respective columns.
* The code also includes styling classes for proper formatting and spacing.

View File

@@ -0,0 +1,643 @@
# React 8 Prop Drilling and Context API
### Agenda
1. We will understand the problem of prop drilling
2. We will overcome prop drilling by using context API
3. We will discuss useRef hook
4. We will discuss useMemo hook(you should have a basic knowledge of memorisation)
### Prop drilling
When you have a piece of specific information that is required at a specific component but to reach that specific component you will have to go through all the components that are one level up the hierarchy.
To understand this in a better way let's take an example of a family tree where the family wants to pass on information to their granddaughter but they will have to pass the information first to the parents then to the children and in the end, the information will reach the granddaughter as shown in the image.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/503/original/Screenshot_2023-09-20_172236.png?1695210781)
Now if we replace the family with the components the tree will look something like this.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/504/original/Screenshot_2023-09-20_172248.png?1695210795)
In the above image, the prompt: "message:'Hi'" should reach component E but it will have to go through component B and component D as well. Here, one level up is Component D then Component B and then Component A. So, this problem is known as prop drilling.
**Example**
To understand the issue with prop drilling, let's create a React APP with the name Context and then create one folder named "prop_drill" inside the source (src) folder. Inside the folder prop_drill, we will add all the components that are going to be used to replicate the family tree we saw above as an example. Inside the prop_drill folder make five files named 'family.js', 'parents.js', 'children.js', 'grandson.js' and 'granddaughter.js'.
First, create a `family.js` component by importing the react function component export.
```javascript!
import React from 'react';
function Family(){
return{
<div>Family</div>
}
}
export default Family
```
Now, add the family component inside the `app.js` file.
```javascript!
import logo from './logo.svg';
import './App.css';
import Family from './prop_drill/Family';
function App() {
return (
<div className="App">
<Family/>
</div>
);
}
export default App;
```
In the app.css file, simple CSS code is added to make the hierarchical family tree.
```css!
/*app.css file */
.family{
display: flex;
justify-content: left;
margin: 1rem;
}
.parent{
border: 5px solid #000;
padding: 0.5rem;
}
.children{
border: 3px solid #ff5722;
padding: 0.5rem 0 0 1em;
}
.gson{
border: 2px solid #3f51b5;
padding: 0.5rem 0 0 1.5rem;
margin: 0.2rem;
}
.gdaughter{
border: 2px solid #e929ad;
padding: 0.5rem 0 0 1.5rem;
margin: 0.2rem;
}
```
Now, we will create a class of family inside the family.js file and also import the Parent file.
```javascript!
import Parent from './Parent';
const Family = () => {
return (
<div className='family'>
<Parent/>
</div>
);
}
export default Family
```
Add the following program in the Children.js file.
```javascript!
import GrandDaughter from "./GrandDaughter";
import GrandSon from "./GrandSon";
const Children = () => {
return(
<div className="children">
<h2>Children</h2>
<GrandSon/>
<GrandDaughter/>
</div>
);
}
export default Children;
```
After executing the above programs when we run the `npm start` command for our app the result will look like this:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/505/original/Screenshot_2023-09-20_172333.png?1695210819)
Now let's create the remaining two files of grandson and granddaughter as well:
```javascript!
// grandson.js
const GrandSon = () => {
return(
<div className="gson">
GrandSon
</div>
);
}
export default GrandSon;
```
```javascript!
//granddaughter.js
const GrandDaughter = () => {
return(
<div className="gdaughter">
GrandDaughter
</div>
);
}
export default GrandDaughter;
```
As you can see in app.js we have added a parent component and in parent.js we have added a children component. Same way to create heirarchy we have added both grandson and granddaughter in children.js.
The result will look something like this after executing the code:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/506/original/Screenshot_2023-09-20_172355.png?1695210842)
Now, let's pass the information from one component to the granddaughter component. For that, create an object in the app.js file inside the function or outside the function.
```javascript!
const familyInfo = {
familyName: "The Griffins",
onlyForParents : () => {
console.log("Info for Parents");
},
onlyForGrandChildren : () => {
console.log("Info for GrandChildren");
}
}
```
**[Ask the learners]**
How will you pass the familyInfo object in the family component?
-> **By passing prompts** We will pass info prompt inside we will pass familyInfo.
```javascript!
function App() {
return (
<div className="App">
<Family info={familyInfo}/>
</div>
);
}
```
Now, the family component will have access to the familyInfo. For that, we need to destructure the info prompt from app.js.
```javascript!
const Family = ({info}) => {
return (
<div className='family'>
<Parent/>
</div>
);
}
```
Now, let's console log parentInfo.familyName from the parent.js file.
```javascript!
import Children from './Children'
const Parent = ({parentInfo}) => {
console.log('This is from parents:',parentInfo);
return (
<div className="parent">
<h1>{`Parent ${parentInfo.familyName}`}</h1>
<Children/>
</div>
);
}
export default Parent;
```
The result will look something like this:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/507/original/Screenshot_2023-09-20_172422.png?1695210868)
Let's invoke `onlyForParents` in Parent.js.
```javascript!
<p>{parentInfo.onlyForParents()}</p>
```
To show the result on the screen, remove console.log from onlyForParents and also from onlyForGrandChildren then add a return statement.
**Result**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/508/original/Screenshot_2023-09-20_172442.png?1695210891)
The information from parents is now to be passed on to Children. To do that the same steps are followed from family to parents.
```javascript!
const Children = ({childrenInfo}) => {
return(
<div className="children">
<h2>{`Children ${childrenInfo.familyName}`}</h2>
<GrandSon/>
<GrandDaughter/>
</div>
);
}
```
The family name is to be passed on to the grandchildren. It will also follow the same process.
`grandSon.js`
```javascript!
const GrandSon = ({grandSonInfo}) => {
return(
<div className="gson">
<h3>{`GrandSon ${grandSonInfo.familyName}`}</h3>
</div>
);
}
export default GrandSon;
```
`grandDaughter.js`
```javascript!
const GrandDaughter = ({grandDaughterInfo}) => {
return(
<div className="gdaughter">
<h3>{`GrandDaughter ${grandDaughterInfo.familyName}`}</h3>
</div>
);
}
export default GrandDaughter;
```
`children.js`
```javascript!
import GrandDaughter from "./GrandDaughter";
import GrandSon from "./GrandSon";
const Children = ({childrenInfo}) => {
return(
<div className="children">
<h2>{`Children ${childrenInfo.familyName}`}</h2>
<GrandSon grandSonInfo = {childrenInfo}/>
<GrandDaughter grandDaughterInfo = {childrenInfo}/>
</div>
);
}
export default Children;
```
**Result**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/509/original/Screenshot_2023-09-20_172506.png?1695210912)
Passing the information onlyForGrandChildren.
`grandSon.js`
```javascript!
<div className="gson">
<h3>{`GrandSon ${grandSonInfo.familyName}`}</h3>
<p>{grandSonInfo.onlyForGrandChildren()}</p>
</div>
```
`grandDaughter.js`
```javascript!
<div className="gdaughter">
<h3>{`GrandDaughter ${grandDaughterInfo.familyName}`}</h3>
<p>{grandDaughterInfo.onlyForGrandChildren()}</p>
</div>
```
**Result**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/510/original/Screenshot_2023-09-20_172527.png?1695210933)
As we can see the information was passed on from one component to another. Here, the components were very few. If the components are more than 10 it will not be a tedious task and will be more prone to errors.
*To overcome this, the concept of Context API was introduced.*
### Context API
Context is a global state that can have the data the developer wants to pass to all the other components.
Let's understand it with the same example of family.
So, in a family where we were passing the information from component to component, we will create a context in component A i.e., family. The context will contain the message which will be available globally and could be accessed by any component without passing from one component to another.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/511/original/Screenshot_2023-09-20_172547.png?1695210954)
The context works as a provider and all the other components will work as consumers.
**Example**
Let's use the example of family and code using context API. For that, create a new folder inside your source folder with the name ContextComponents (the same way as already done in the above example of prop drilling). Inside this folder create the same files as were already created inside prop_drill.
Also, copy the code for all the js files created which will be rendered.
We will create a context for the family as it is the topmost component after `app.js`. For that, create a new file `FamilyContext.js` where the context will be created. To create a context there is already an inbuilt method named `createContext` in React. The FamilyContext can be imported into your app.js file.
```javascript!
//FamilyContext.js
import React, { createContext } from "react";
//the variable familycontext becomes a context
export const FamilyContext = React.createContext()
```
After importing FamilyContext in your app.js file, we can actually wrap the FamilyC component inside the FamilyContext. And we can pass down the value by just passing the value property inside the FamilyContext tag. Also, remove prompts from files. We have to add a provider with the context as it is a provider and the other components are consumer.
`ParentsC.js`
```javascript!
import ChildrenC from './ChildrenC';
import { FamilyContext } from './FamilyContext';
import { useContext } from 'react';
const ParentsC = () => {
const parentInfo = useContext(FamilyContext);
console.log(parentInfo);
return (
<div className="parent">
<h1>{`Parent ${parentInfo.familyName}`}</h1>
<p></p>
</div>
);
}
export default ParentsC;
```
`app.js`
```javascript!
function App() {
return (
<div className="App">
<FamilyContext.Provider value = {familyInfo}>
<FamilyC/>
</FamilyContext.Provider>
</div>
);
}
```
After the console log the result comes out using the context.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/512/original/Screenshot_2023-09-20_172612.png?1695210981)
In the same way, it can be done for both children and grandChildren.
`GrandSon.js`
```javascript!
import { useContext } from "react";
import { FamilyContext } from "./FamilyContext";
const GrandSonC = () => {
let grandSonInfo = useContext(FamilyContext)
return(
<div className="gson">
<h3>{`GrandSon ${grandSonInfo.familyName}`}</h3>
<p></p>
</div>
);
}
export default GrandSonC;
```
`GrandDaughter.js`
```javascript!
import { useContext } from "react";
import { FamilyContext } from "./FamilyContext";
const GrandDaughterC = () => {
let grandDaughterInfo = useContext(FamilyContext)
return(
<div className="gdaughter">
<h3>{`GrandDaughter ${grandDaughterInfo.familyName}`}</h3>
<p>{grandDaughterInfo.onlyForGrandChildren()}</p>
</div>
);
}
export default GrandDaughterC;
```
`Children.js`
```javascript!
import { FamilyContext } from "./FamilyContext";
import { useContext } from "react";
import GrandSonC from "./GrandSonC";
import GrandDaughterC from "./GrandDaughterC";
const ChildrenC = () => {
const childrenInfo = useContext(FamilyContext)
return(
<div className="children">
<h2>{`Children ${childrenInfo.familyName}`}</h2>
<GrandSonC/>
<GrandDaughterC/>
</div>
);
}
export default ChildrenC;
```
**Result**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/513/original/Screenshot_2023-09-20_172650.png?1695211017)
As we can see, we got the same results using both prop drilling and context API. But, in Context API we didn't use any prompts instead we just passed the context by importing in our components.
> The context is an orchestrator or a centralized place where related elements within a hierarchy can access data from another element within the same hierarchical scope.
You can go through this [article](https://www.smashingmagazine.com/2020/01/introduction-react-context-api/) for more information.
### useRef Hook
Let's understand what is useRef and why is it important using an example.
Create a new component and name it Ref inside your ContextComponents folder.
`Ref.js`
```javascript!
import React, { useState } from "react";
function Ref() {
const [name, setName] = useState("Alex");
function clear(){
setName('')
}
return(
<>
<h1>UseRef</h1>
<input type="text" value={name} onChange = {(e) => setName(e.target.value)}>
</input>
<button onClick={() => clear()}> Clear</button>
</>
);
}
export default Ref;
```
Add this Ref component in the App.js file (comment out the div element).
**Result**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/514/original/Screenshot_2023-09-20_172712.png?1695211038)
In the result, we see that a blue outline is visible at the border of the text box even though there is no code added. But as soon as we click on the clear button, the blue border is also removed, basically all the focus is removed from the text box. Here, we do not want the focus should be moved from the input box or textbox.
UseRef Hook allows you to access DOM properties inside React which returns an object. The object contains a key known as "current" where you can set anything inside it. This key will allow you to manipulate that particular element with respect to the DOM.
So in the above code, we will use the useRef hook. This useRef will allow you to get the input object inside the `current` key. In HTML, we have access to the `ref` attribute which stands for Reference.
```javascript!
function Ref() {
const [name, setName] = useState("Alex");
function clear(){
setName('')
RefElement.current.focus()
}
let RefElement = useRef('')
console.log(RefElement);
return(
<>
<h1>UseRef</h1>
<input ref ={RefElement} type="text" value={name} onChange = {(e) => setName(e.target.value)}>
</input>
<button onClick={() => clear()}> Clear</button>
</>
);
}
```
In the above code, we are setting the key default input as an empty string for the `current` key. The reference is set for the input field. As we don't want to move the focus from the input field we will add the RefElement in the clear() function and use the DOM method focus().
**Result**
![](https://hackmd.io/_uploads/H1f5VJHR3.png)
In the result, we can see even if we click on the clear button the focus from the input field is not moved.
Let's take another example where we want to change the text colour as well.
To do that create another button and name it Change Color. Make a function with the name changeColor() add the RefElement we created and use the DOM properties for changing the color.
```javascript!
function changeColor(){
RefElement.current.style.color = 'Pink'
}
<button onClick={() => changeColor()}> Change Color</button>
```
**Result**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/515/original/Screenshot_2023-09-20_172733.png?1695211059)
You can read the following article on [Medium](https://medium.com/trabe/react-useref-hook-b6c9d39e2022) to understand useRef Hook better.
--
### useMemo
In dynamic programming, there's a step called Memoization. Memoization means to have a temporary memory area where things get stored so that no function calls are made instead we can go there and get the value and use that value. Same way it works for React too.
Let's take a very simple example where we will get the factorial of numbers.
Create a new file named `Factorial.js` inside the ContextComponents folder.
```javascript!
import React, { useState } from 'react'
function Factorial() {
const [number, setNumber] = useState(1)
const [inc, setInc] = useState(0)
const onChange = (event) =>{
setNumber(Number(event.target.value))
}
const onClick = () => {
setInc(i=>i+1)
}
let factorial = factorialOf(number)
return (
<div>
<span>Factorial of - </span>
<input type= 'number' value={number} onChange={onChange}></input>
<span>Factorial Value: {factorial} </span>
<button onClick={onClick}>re-render</button>
</div>
)
}
function factorialOf(n){
console.log('Factorial function called: ')
return n<=0 ?1: n* factorialOf(n-1)
}
export default Factorial
```
The above code is not optimised as there are so many function calls when we try to increase the value. Why? Because with each increasing number, we are calling the function again and again. Also, the number of function calls increases while clicking on re-render as you can see in the image.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/516/original/Screenshot_2023-09-20_172755.png?1695211082)
The re-render button takes the input number and calculates the factorial again because we do not have the value of the factorial already set. That means on clicking the button it calls the function again and again which is not optimised. To overcome this, we will use **useMemo Hook** which is similar to `useEffect` where we do not try to update the state again and again. We update the state only when there's a dependency.
Similarly, there's `useMemo` that is used to memoize the function. It accepts the function that you need to memoize and also comes with the condition of when you want to call the function.
In the above example, we need to memoize the factorial function. To do that use the `useMemo` hook.
```javascript!
const factorial = useMemo(() => factorialOf(number),[number])
```
After running the program, we see that even if we click on the re-render button the function is not called. It is only called when changing the values. The value that was already there is being memoized.
You can refer to this [article](https://dmitripavlutin.com/react-usememo-hook/) to understand useMemo Hook.

View File

@@ -0,0 +1,452 @@
# React Redux and Redux Toolkit
### Redux
Redux is a management tool used to manipulate data within creating a store from where each component can access the properties by dispatching actions to reducers.
**Example**
Let's take an example to understand.
Initialise a react app you can name it `Redux app`.
We will be creating a replica of an E-commerce shopping cart application using Redux.
First, let's create two pages - the home page and the cart page.
You can have two pages using React Router (allows page routing). First, Install React Router then Create a nav bar and add two links: for the home page and cart page.
To install React Router run the command `npm install react-router-dom
The very first step after installing the react router is to **import browser router** then routes and then every specific route.
```javascript!
import { BrowserRouter, Routes, Route } from 'react-router-dom';
```
Step 1: Create a Home page
```javascript!
import React from "react";
const Home = () => {
return (
<div>
<h1>This is Home page.</h1>
</div>
)
}
export default Home
```
Step 2: Create a Cart page
```javascript!
import React from 'react'
const Cart = () => {
return (
<div>
<h1>This is Cart page.</h1>
</div>
)
}
export default Cart
```
In the `app.js` file import Browser router, routes and route and add paths to both the pages.
```javascript!
import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Cart from './pages/Cart';
import Home from './pages/Home';
import NavBar from './components/NavBar';
function App() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path = '/' element = {<Home/>}></Route>
<Route path = '/cart' element = {<Cart/>}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
```
Now, we want to create a nav bar for our app. To do that create a folder named components inside your source folder. Create a file named NavBar.js inside that folder.
After that add the following simple CSS and Links to your pages.
`NavBar.js`
```javascript!
import React from 'react'
import { Link } from 'react-router-dom'
const NavBar = () => {
return (
<div
style = {{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<span className='logo'>Redux Store</span>
<div>
<Link className='navLink' to = '/'>Home</Link>
<Link className='navLink' to='/cart'>Cart</Link>
<span className='cartCount'>Cart items:0</span>
</div>
</div>
)
}
export default NavBar
```
Add your NavBar to your app.js inside the `<BrowserRouter></BrowserRouter>` tag.
Add some CSS inside the `index.css` file to make your page look more optimised.
`index.css`
```css!
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding: 20px;
background: whitesmoke;
}
img {
height: 80px;
}
.card {
background: white;
padding: 20px;
text-align: center;
border-radius: 10px;
}
.productsWrapper{
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 30px;
}
.btn{
border: none;
outline: none;
background: gold;
padding: 5px 10px;
color: black;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease-in-out;
}
.btn:hover{
background: green;
}
.remove-btn{
border: none;
outline: none;
background: gold;
padding: 5px 10px;
color: black;
border-radius: 8px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease-in-out;
}
.remove-btn:hover{
background-color: red;
}
.heading{
padding: 25px 0;
}
.cartCount{
font-weight: bold;
text-transform: uppercase;
margin-left: 40px;
}
.navLink{
text-decoration: none;
color: black;
margin-left: 20px;
}
.cartCard{
display: flex;
align-items: center;
justify-content: space-between;
background: #fff;
margin-bottom: 20px;
padding: 14px;
border-radius: 5px;
}
```
Now, to create our shopping cart application we will be using [fakeStoreAPI](https://fakestoreapi.com) from where we can get some product data.
How to get data from an API?
-> The package that we are going to use is **Axios**. But Fetch can also be used to get data from an API.
Fresh applications do not have access to Axios. Therefore, you have to install it using the command `npm install axios`.
By using axios, you can use Promises or use the fetch API or async await.
We will create a separate component named `Products.js` inside your components folder to get the data pick that component and add it to our home page.
We use two Hooks: **useEffect** to make asynchronous calls to your API and **useState** for maintaining a state.
Function is defined as async and whenever you do something that will take time you use the await.
By using useEffect hook we can call for our API.
This is your API endpoint: `'https://fakestoreapi.com/products'`
`Products.js`
```javascript!
import axios from 'axios'
import React, { useEffect, useState } from 'react'
const Products = () => {
const [products, setProducts] = useState([])
useEffect(() => {
const getProducts = async () => {
const res = await axios.get('https://fakestoreapi.com/products')
console.log(res)
}
getProducts()
},[])
return (
<div></div>
)
}
export default Products
```
We want this data to be added to our Home page. To do that add the Products component inside Home.js.
To display all the products from this API we will use `setProducts(res.data)` and create a new div inside which we will add all the components we want to display like product image, and price and add to the cart button.
`Products.js`
```javascript!
return (
<div>
<div className='productsWrapper'>
{
products.map((product) => {
return <div className='card'>
<img src= {product.image}/>
<h6>{product.title}</h6>
<h5>{product.price}</h5>
<button className='btn'>Add to cart</button>
</div>
})
}
</div>
</div>
)
}
```
Now, what we want is that when we click on the add to cart button the number of cart items should increase. But cart items are added in our navBar component while the add to cart button is added in our Products component.
**[Ask the learners]**
How can I pass data from the product component to the cart component or the navBar component?
-> **Redux** is used to pass the data. Redux is flexible as it can be used with other frameworks as well. We can't use prop drilling or context API because of optimisation issues.
You can install the Redux toolkit using the following command: `npm install @reduxjs/toolkit react-redux`. By installing the redux toolkit two libraries will be installed: **reactjs/toolkit and react-redux**.
You can also visit [Redux](https://redux-toolkit.js.org/tutorials/quick-start) documentation to read more about the Redux toolkit.
Before writing redux code install this [chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd/related) extension.
*When we use redux there are a few things to keep in mind*
* Create a centralised store for all your component features and properties.
* We can create slices (a slice is for a specific component where every function will be written for that specific component) of different components. Whatever component you are trying to integrate your features for, you should create separate slices for them.
In the example of the shopping cart, we will create a cart slice. To create a slice there is a method known as `createSlice` which takes an object inside which we can define everything that we want that particular slice to perform.
`cartSlice.js`
```javascript!
import { createSlice } from "@reduxjs/toolkit";
const cartSlice = createSlice({
name:'cart',
initialStat:[],
reducers : {
add(state, action){
state.push(action.payload)
}
}
```
**cartSlice:** It is created for your cart. Everything that you will do for your cart will be added.
name
**initialState:** The state from where we are going to start there it will be empty.
**reducers:** Inside the reducers key you will put all those functions that you want to be implemented in your cart. For example: we want to add to the cart feature and remove from the cart feature inside our redux application.
**action.payload:** What is the action you are taking with the data that goes inside action.payload.
We are pushing action.payload. InitialState was empty but now as we have taken an action all the data will be pushed to the initialState array. After that, you can count the length of all the objects inside the array and whatever will be the length will be the number added to your cart.
First, we need to export the add reducer that we are destructuring out from the cartSlice.
Second, you have to export the whole cartSlice.
```javascript!
export const {add} = cartSlice.actions //exporting the add reducer
export default cartSlice.reducer //exporting the whole cartslice
```
We will be creating a central store named store.js in our Store folder to store everything related to every component will be stored in it.
Same way as we have the createSlice method by redux, there's another method known as configureStore.
As we know we have an InitialState array and to fetch the data from that particular state for that we have a special kind of hook that is provided from react redux known as **useSelector**. This hook has access to all the global states.
Add useSelector inside the navBar component as shown:
```javascript!
const items = useSelector((state) => state.cart) //use the state of the cart
console.log(items.length)
```
the console log will give output 0 as initially, cart items are 0.
> We have to use Provider from the react-redux library as our app.js will be the initial page that will show all the components. Therefore to wrap all the components we will be using Provider (not from context API) but from react-redux. Inside Provider, we will add a store as we added value inside our context API provider.
**useDispatch** hook is used to use your reducers. We can dispatch actions to our reducers.
Whatever product is getting added is your action payload.
What we want is for the number of cart items should increase as we click on the 'Add to cart' button that exists in the `Products.js` component. To use reducers we will import the useDispatch hook inside our Products.js component.
```javascript!
import { useDispatch } from 'react-redux'
import { add } from '../Store/cartSlice'
const dispatch = useDispatch()
const handleAdd = (product) => {
dispatch(add(product))
}
<button onClick={() => handleAdd(product)} className='btn'>Add to cart</button>
```
After this add `{items.length}` in your Cart Items `<span className='cartCount'>Cart items:{items.length}</span>`
The number of cart items will increase on the `Add to cart` button.
![](https://hackmd.io/_uploads/H1CteEcAn.png)
When you open the Redux dev tool in your browser, go to 'State' where you will see the cart inside 'Tree', and 'Chart'.
Whenever you add an item the data related to that product is added to the cart as you can see in the image.
![](https://hackmd.io/_uploads/H1UC-EqCn.png)
Redux is predictable because you can have control of every step that you are doing. You see the scroll bar in the image shown below, using that scroll bar you can control every step.
![](https://hackmd.io/_uploads/B1GDXN90n.png)
### Cart page
The data will be passed the same way we passed the product length in our cart items.
We want product images, prices, and titles to be displayed on our cart page. And also we will add a remove button to remove any product from our cart.
Add remove reducer inside our cartSlice in your store.
`cartSlice.js`
This reducer will return and filter out from the state that has some products. Wherever the data doesn't match return it and remove the data that matches. Also, export remove reducer the way we did add reducer.
```javascript=
remove(state , action){
return state.filter((item)=> item.id !== action.payload)
}
```
`Cart.js`
```javascript=
import React from 'react'
import {useSelector , useDispatch} from 'react-redux'
import { remove } from '../Store/cartSlice'
const Cart = () => {
const items = useSelector((state)=> state.cart)
const dispatch = useDispatch()
const handleRemove =(itemId)=>{
dispatch(remove(itemId))
}
return (
<div>
<div className='cartWrapper'>
{
items.map((item)=>(
<div className='cartCard'>
<img src={item.image}></img>
<h5>{item.title}</h5>
<h5>Price : ${item.price} </h5>
<button className='remove-btn' onClick={()=> handleRemove(item.id)}>Remove Item</button>
</div>
))
}
</div>
<h1>This is Cart page.</h1>
</div>
)
}
export default Cart
```

View File

@@ -0,0 +1,247 @@
## Agenda
We will try to cover most of these topics in today's sessions and the remaining in the next.
* Build the Home Page
* Map movies to theaters
* Build the shows components
* Build the seating arrangements
* Book tickets
* Make Payments
* Deploy
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## Build the Home Page
Whenwever you login to the home page you must see what all movies are playing and then you want to book that particular movie.
But before moving that as we left a part in shows component , basically the working of the delete button.
**Asking the question:**
Have you attempted the homework of making the delete button?
**No**
Lets quickly write the code for the delete button, lets add the route of the "delete-show".
Basically on hitting the endpoint "/delete-show", will await the shows model and find by id to delete by getting the showID and then respectively shows the sucess message or the respective error.
For this route, will go to the client and make the axios instance for this to delete-movies. So now will be writing the axios instance where we will bw hitting on the route "/api/theatres/delete-show" and sending the id in the payload.
Now will be writing the handledelete method that will be accepting the id and will be calling the deleteshow and id will taken as payload and the `getdata()` gets called or else the error message. and just put this handledelete in the onClick.
Now just demonstarate, by inserting the show name, date, time, movie, ticket_price, and total_seats. Now will be trying the delete button.
Lets start with home page,
### Start by Discussing the Wireframe of The Homepage
* In the home page, will be search part for searching the movies name.
* Then will be adding the posters of the different movies which will be clickable that goes to the some another page.
* On the other page , that will have the details of the movies and below that there will details of the theaters and in the nother space there will be date picker. The available theaters with the selected movie and date/time will be shown with name and time.
* When we will click on the theaters components, that will consist of the seating arrangements and then the users will be able to select the particular seat.
* Then after clicking on the Book Ticket button which will go to the another page that will show the movies details with seat, amount, and the make payment button.
* On clicking the make_payment button that will take us to the stripe portal to make the payments.
### Let's start changing the index.js file as follows:
First of all add the input space for searching the movie name and give classname as search-input.
* Add the height and border-radius to the search-input to give some styling.
* Now as you see in the mongodb in the movies we are having the information of the movies stored.
* To get the details of the movies to add them in the cards that will be done using the get_all_movies route.
* To start building the card, lets divide it int row and col using antd.
* In the row, there will be movies title, seach space and the column properties.
* Now will bw creating the two states one for the movies and other for the searchText using useState.
* Define the method of getdata for the getting all the movies by using loader and dispatch by useDispatch.
* To call the getData() method , use the useEffect and will get all the movies. Also import the HideLoading and ShowLoading from the loaderSlice. Also the message from the antd.
* For the searchText , will be adding the onChange on the event that will use the setSearchText method with the event's target value. And basically the search for the particular text by after converting to the lower case is done with the movies title.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/390/original/upload_b9165fabb38882138bfde830cd6e8612.png?1695983804)
### Mapping of the Theater and Shows
As there are multiple theaters, with multiple shows and multile movies, then how to map the correct movies details, lets discuss this:
For example in the shows at PVR Superplex having the show of the movie Captain America.
Let's suppose they are having shows of the movies at 3 PM and 9PM so are they will be displayed two times.
Do they show they show the PVR Superplex haing two shows one at the 3pm and the other at 9pm. Or do they show like PVR Superplex two times with differently. The first one is the better approach like in the below image.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/391/original/upload_1e89157fcafdf4aea3f4657bb8190f61.png?1695983835)
Like one theater one time, and then we can add on the shows of different time as above.
We have to use the route parameter for the different routes for the different movies by sending the movies id.
Lets import the useNavigate from the react-router-dom, use this in the navigate method where we are changing the movie_id. Also import moment.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/393/original/upload_75f0d47c5e4a2f8b8f02d693f814204a.png?1695983857)
Hence the route changes as we click on the movie as seen in the route we got the movie id along with the date. Same as we observer on the different websites like amazon.
So now we have to show the movies details along with the show details let start,
* We have to take a another component, names as TheatreForMovies.
* In the app.js whenever there will be route of type movie with id, then we will go to this component of theatresForMovies.
* First of all we have to just build the movies_Details and then the date picker.
* Using the movie id we can get all the data using the function `getData()`.
---
title: Map Movies to the theaters
description: Explanation of all the related components and the routes for the mapping page of the movies with theaters.
duration: 2100
card_type: cue_card
---
## Map Movies to the theaters
**Get a movie by ID route**
Lets create the route for this, go to the movie-route.js by just passing the id, so by simply attaching the movie_id with the axiosInstance we can get the id using the route and api call.
Here's a explanation of the code:
1. **Imports**:
- The code imports necessary modules and functions from various libraries such as Ant Design (`antd`), React (`react`), React Router (`react-router-dom`), and others.
2. **React Hooks**:
- The `useParams` and `useNavigate` hooks are used from React Router to access the URL parameters and navigate between routes, respectively.
- The `useState` and `useEffect` hooks are used to manage component state and perform side effects when the component mounts.
3. **Redux Dispatch**:
- The `useDispatch` hook is used to get the Redux dispatch function, which will be used later to dispatch actions.
4. **Data Fetching with getData Function**:
- The `getData` function is defined as an asynchronous function. It is responsible for fetching movie data by its ID from an API using the `GetMovieById` function from the `apicalls/movies` module.
- While fetching data, a loading spinner is displayed using `dispatch(ShowLoading())`.
- After receiving a response, it checks if the response was successful. If successful, it sets the movie data and displays a success message; otherwise, it displays an error message.
- Finally, it hides the loading spinner using `dispatch(HideLoading())`.
5. **Render Movie Information**:
- The movie information is displayed in JSX within a conditional rendering block (`movie && ...`) to ensure that the component renders only when `movie` data is available.
- It displays various details about the movie, such as title, language, duration, release date, and genre. The `moment` library is used to format the release date.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/394/original/upload_5ccf189b12d1fb2422a35e17af842a02.png?1695983908)
**Addition of the date picker using moment:**
* The component will render a section that includes a label "Select Date" and a date picker input field.
* The date picker will have a minimum date restriction set to the current date. Users won't be able to select a date earlier than today's date.
* When a user interacts with the date picker and selects a new date, an onChange event will be triggered.
* Additionally, the code uses the navigate function (likely from React Router) to navigate to a new URL. This new URL includes query parameters with the movie ID (params.id) and the selected date. For example, if the user selects a date of "2023-09-20" for a movie with an ID of "123," the URL might become something like "`/movie/123?date=2023-09-20`."
* The change in the URL, including the selected date as a query parameter, can trigger a different route in your application or update the content displayed on the page.
* This enables your application to react to the selected date and possibly show movie-related information for the chosen date.
**Now start creating threaters and shows running on them:**
Lets start by getting the theater, using the id of the theater for example if the bruce having two 2 theaters with JAWAN movies and the Maek has 1 theater with Captain America then if the user selecting the JAWAN movies how many theater should be given?
**Asking Question** So exactly the 2 theaters should be given which are havin the movie JAWAN.
Lets get all the unique theaters which have the shows of a movie
It's an Express.js route handler that aims to retrieve all unique theaters that have shows of a specific movie on a given date. Here's the explanation:
1. **Route Definition**:
- This code defines an Express.js POST route at the endpoint `/get-all-theatres-by-movie`. It expects the request to be authenticated using the `authMiddleware` middleware.
2. **Request Parsing**:
- In the route handler, it extracts the `movie` and `date` parameters from the request body using destructuring: `const { movie, date } = req.body;`.
3. **Fetching Shows**:
- It then queries the database to find all the shows of a specific movie on the given date using the `Show` model. The `find` method is used with the `movie` and `date` parameters.
- Additionally, it uses `.populate('theatre')` to populate the `theatre` field in the `shows` collection with corresponding theater information.
4. **Finding Unique Theaters**:
- The code initializes an empty array called `uniqueTheatre` to store unique theater data.
5. **Sending Response**:
- The route handler sends a JSON response to the client, indicating that the request was successful (`success: true`).
- The `message` field provides a success message, stating "Unique Data Fetched."
- The `data` field in the response is set to the `uniqueTheatre` array, which contains the unique theater information.
6. **Error Handling**:
- In case an error occurs during the process (e.g., a database query error), the catch block is executed. It sends a JSON response indicating the failure (`success: false`) and includes an error message in the `message` field.
The provided code is a JavaScript function that exports a function named `GetTheatresByMovie`. This function is likely used to make an HTTP POST request to a specific API endpoint in order to get theaters that are showing a particular movie on a given date. Here's an explanation of the code:
1. **export const GetTheatresByMovie = async (payload) => { ... }**:
- This line exports a function called `GetTheatresByMovie` as a named export from the module.
- The function is defined as an asynchronous function, indicating that it can perform asynchronous operations, such as making HTTP requests.
2. **try { ... } catch (err) { ... }:**
- The function is wrapped in a try-catch block, which is used for error handling. It attempts to perform the specified operation within the try block and catches any errors that may occur.
3. **const response = await axiosInstance.post('/api/theatres/get-all-theatres-by-movie', payload);:**
- Inside the try block, the code uses the `axiosInstance` (presumably an Axios instance configured for making HTTP requests) to send an HTTP POST request to the `/api/theatres/get-all-theatres-by-movie` endpoint.
- The `payload` variable is expected to contain the data to be sent as the request body. This likely includes information about the movie and the date.
4. **return response.data;:**
- If the HTTP request is successful (i.e., the server responds with a valid JSON response), the function returns the `data` property of the response. This typically contains the data returned by the server, such as theater information.
5. **return err.response:**
- If an error occurs during the HTTP request (e.g., a network error or a server error), the catch block returns the `response` property of the `err` object.
The provided code appears to be a JavaScript function named `getTheatres`. This function is designed to fetch theater information related to a specific movie and date, similar to the previous code you shared. Here's an explanation of this code:
1. **const getTheatres = async () => { ... }:**
- This function is defined as an asynchronous function and does not take any arguments.
2. **try { ... } catch (err) { ... }:**
- The function is wrapped in a try-catch block for error handling. It attempts to perform the specified operations within the try block and catches any errors that may occur.
3. **dispatch(ShowLoading());:**
- Before making the API request, it dispatches an action (likely a Redux action) to show a loading spinner or loading indicator, indicating that data is being fetched.
4. **const response = await GetTheatresByMovie({ date, movie: params.id });:**
- Inside the try block, it calls the `GetTheatresByMovie` function, passing an object as the payload. This object includes the `date` and `movie` parameters required for the API request. `params.id` is presumably the movie ID.
- The function is awaited, meaning it will wait for the HTTP request to complete and return a response.
5. **console.log(response.data);:**
- It logs the `data` property of the response to the console. This typically contains the data returned by the server, which in this case, should be theater information related to the specified movie and date.
6. **Response Handling:**
- If the response indicates success (`response.success` is `true`), it updates the component state with the theater data using `setTheatres(response.data)` and displays a success message using `message.success(response.message)`.
- If the response indicates failure (`response.success` is `false`), it displays an error message using `message.error(response.message)`.
7. **dispatch(HideLoading());:**
- Finally, it dispatches an action (likely a Redux action) to hide the loading spinner or loading indicator, indicating that the data fetching process has completed.
8. **Error Handling:**
- In case an error occurs during the API request (e.g., network error or server error), the catch block is executed. It hides the loading indicator, and it displays an error message using `message.error(err.message)`.
**Code for the Unique Theaters:**
Route handler for fetching all unique theaters that have shows of a specific movie on a given date. Here's an explanation:
1. **Route Definition**:
- This code defines an Express.js POST route at the endpoint `/get-all-theatres-by-movie`. It expects the request to be authenticated using the `authMiddleware` middleware.
2. **Request Parsing**:
- In the route handler, it extracts the `movie` and `date` parameters from the request body using destructuring: `const { movie, date } = req.body;`.
3. **Fetching Shows**:
- It then queries the database (likely using an ORM like Mongoose) to find all the shows of a specific movie on the given date. The `Show` model is used to query the shows, and the `populate` method is used to retrieve associated theater information for each show: `const shows = await Show.find({ movie, date }).populate('theatre');`.
4. **Finding Unique Theaters**:
- The code initializes an empty array called `uniqueTheatre` to store unique theater data.
- It iterates through each `show` in the `shows` array using a `forEach` loop.
- For each `show`, it checks if there is already a theater with the same `_id` in the `uniqueTheatre` array.
- If no matching theater is found (`!theatre`), it filters all shows with the same theater `_id` and pushes a new object into the `uniqueTheatre` array. This object combines the theater data with an array of shows for that theater.
5. **Response**:
- Finally, the route handler sends a response to the client. If successful, it sends a JSON response with `success`, a success message, and the `uniqueTheatre` array containing theater information and associated shows.
6. **Error Handling**:
- In case of an error during the process (e.g., database query error), it sends a JSON response indicating the failure with an error message.
This route essentially retrieves all theaters that are showing a particular movie on a specific date, eliminating duplicates, and presents the data in a structured format, making it easier for clients to work with unique theater information and associated show details.

View File

@@ -0,0 +1,927 @@
## Agenda
We will try to cover most of these topics in today's sessions and the remaining in the next.
* Build the shows components
* Build the seating arrangements
* Book tickets
* Make Payments
* Deploy
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## Build the Home Page
As till now we have completed the home page, where all the movies and upcoming movies are shown as below:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/446/original/upload_2f04c7d551fcc86af920399ec3c6ad5b.png?1695995692)
As we click on the movies then the related information about the movies theaters and the shows timings should be visible in the below blank region.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/447/original/upload_5e3b842eca1765968a94ee60bebe0355.png?1695995733)
## Shows
Now we will be implementing those related functionalities as follows:
If we get this below data in the theaterforMovie then we can show the related shows and the timings and all.
```javascript
import { message } from "antd";
import { useEffect, useState } from "react";
import {useNavigate, useParams} from "react-router-dom"
import { GetMovieById } from "../apicalls/movies";
import { useDispatch } from "react-redux";
import { HideLoading, ShowLoading } from "../redux/loadersSlice";
import {GetTheatresByMovie} from "../apicalls/theatres"
import moment from "moment";
export default function TheatreForMovie(){
let [movie,setMovie] = useState()
let [theatres,setTheatres] = useState();
let [date,setDate] = useState(moment().format("YYYY-MM-DD"));
const [isHovering, setIsHovering] = useState(false);
const params = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const getData = async() => {
try{
dispatch(ShowLoading());
const response = await GetMovieById(params.id)
if(response.success){
setMovie(response.data);
message.success(response.message)
}else{
message.error(response.message);
}
dispatch(HideLoading());
}catch(err){
dispatch(HideLoading());
message.error(err.message)
}
}
const getTheatres = async () => {
try{
dispatch(ShowLoading());
const response = await GetTheatresByMovie({date,movie:params.id})
console.log(response.data);
if(response.success){
setTheatres(response.data);
message.success(response.message)
}else{
message.error(response.message);
}
dispatch(HideLoading());
}catch(err){
dispatch(HideLoading());
message.error(err.message);
}
}
const handleMouseEnter = (id) => {
setIsHovering(true);
};
const handleMouseLeave = (id) => {
setIsHovering(false);
};
useEffect(() => {
getData();
},[])
useEffect(() => {
getTheatres()
},[date])
return (
movie && (
<div>
{/* movie information */}
<div className = "flex justify-between items-center mb-2">
<div>
<h1 className = "text-2xl uppercase">
{movie.title} ({movie.language})
</h1>
<h1 className = "text-md">Duration : {movie.duration} mins</h1>
<h1 className = "text-md">
Release Date : {moment(movie.releaseDate).format("MMM Do yyyy")}
</h1>
<h1 className = "text-md">Genre : {movie.genre}</h1>
</div>
<div className = "mr-3">
<h1 className = "text-md ">Select Date</h1>
<input
type = "date"
min = {moment().format("YYYY-MM-DD")}
value = {date}
onChange = {(e) => {
setDate(e.target.value);
navigate(`/movie/${params.id}?date=${e.target.value}`);
}}
/>
</div>
</div>
<hr />
{/* movie theatres */}
<div className = "mt - 1">
<h1 className = "text - xl uppercase">Theatres</h1>
</div>
<div className = "mt - 1 flex flex-col gap-1">
{theatres.map((theatre) => (
<div className = "card p-2">
<h1 className = "text-md uppercase">{theatre.name}</h1>
<h1 className = "text-sm">Address : {theatre.address}</h1>
<div className = "divider"></div>
<div className = "flex gap-2">
{theatre.shows
.sort(
(a, b) => moment(a.time, "HH:mm") - moment(b.time, "HH:mm")
)
.map((show) => (
<div key = {show._id} style = {{
backgroundColor: isHovering ? '#DF1827' : 'white',
color: isHovering ? 'white' : '#DF1827',
}}
onMouseEnter = {handleMouseEnter}
onMouseLeave = {handleMouseLeave}
className = "card p-1 cursor-pointer border-primary"
onClick = {() => {
navigate(`/book-show/${show._id}`);
}}
>
<h1 className = "text-sm">
{moment(show.time, "HH:mm").format("hh:mm A")}
</h1>
</div>
))}
</div>
</div>
))}
</div>
</div>
)
);
}
```
The code is a React component named `TheatreForMovie` that is responsible for displaying information about a movie and its associated theaters. It utilizes various React hooks and asynchronous API calls. Here's a breakdown of its functionality:
1. **State Management**:
- The component uses `useState` to manage state variables for `movie`, `theatres`, and `date`.
- It also manages the `isHovering` state to handle mouse hover events.
2. **Initialization**:
- The component extracts route parameters using `useParams()` and obtains a navigation function using `useNavigate()`.
- It also obtains a Redux dispatch function using `useDispatch()`.
3. **Data Retrieval**:
- Two asynchronous functions, `getData()` and `getTheatres()`, are defined to fetch movie details and theater information, respectively.
- These functions use API calls (e.g., `GetMovieById` and `GetTheatresByMovie`) to retrieve data.
- Loading states are managed using Redux actions (`ShowLoading` and `HideLoading`).
4. **Mouse Hover Effects**:
- `handleMouseEnter` and `handleMouseLeave` functions are used to toggle the `isHovering` state when hovering over theater showtimes.
- This is used to change the background and text color of showtime cards.
5. **Effect Hooks**:
- The `useEffect` hook is utilized to fetch movie data (`getData()`) when the component mounts. It runs once because it has an empty dependency array.
- Another `useEffect` hook is used to fetch theater data (`getTheatres()`) whenever the `date` state changes.
6. **Rendering**:
- The component conditionally renders the movie and theater information if the `movie` state exists.
- Movie information, including title, language, duration, release date, and genre, is displayed.
- A date input allows users to select a date, triggering a navigation change.
- Theater information is displayed, including theater name, address, and showtimes.
- Showtimes are sorted by time and displayed in cards that respond to hover events.
7. **Sorting of Theater Shows**:
- Within the rendering section of the component, theater shows are sorted based on their showtimes.
- This sorting is achieved through the `.sort()` method applied to the `theatre.shows` array.
- The sorting logic compares the showtimes, ensuring that shows are displayed in chronological order.
- The `moment` library is used to parse and format showtime values for comparison.
- As a result, theater shows are presented to users in ascending order of showtime, providing a clear and organized view of available showtimes for the selected date.
## Seating Component
Now lets start with the seating component, after selecting the show of the theater as follows.
```javascript
onClick = {() => {
navigate(`/book-show/${show._id}`);
}}
```
When a user clicks an element (like a button), the `onClick` function runs. It uses the `navigate` function to take the user to a new page, typically based on the show's ID.
### Get show by id
```javascript
//get show by id
router.post('/get-show-by-id',authMiddleware,async(req,res) => {
try{
const show = await Show.findById(req.body.showId)
.populate("movie")
.populate("theatre")
res.send({
success:true,
message:"Show fetched",
data:show,
})
}catch(err){
res.send({
success:false,
message:err.message
})
}
})
```
This specific route handles a POST request to retrieve information about a show by its ID. Let's break down what this code does:
1. **Route Definition**:
- This route is defined to listen for POST requests on a specific path, which is "/get-show-by-id".
2. **Middleware**:
- It includes an authentication middleware function called `authMiddleware`. This function is likely responsible for verifying the user's identity and ensuring that only authorized users can access this route.
3. **Request Handling**:
- When a POST request is made to this route, the server executes the code inside the `try` block.
4. **Show Retrieval**:
- Inside the `try` block, the code uses the `Show` model (presumably a MongoDB model) to find a show document in the database based on the provided `showId`. This is done using `Show.findById(req.body.showId)`.
- The `populate` method is used to retrieve additional information related to the show, specifically details about the associated movie and theater. This populates the "movie" and "theatre" fields in the `show` document with their respective data.
5. **Response**:
- If the show is successfully found and populated, a response is sent back to the client with a `success` status set to `true`.
- The response includes a message indicating that the show has been fetched and the `show` data itself.
6. **Error Handling**:
- If any errors occur during the process (e.g., the show with the provided ID doesn't exist or there's an issue with the database), the code inside the `catch` block is executed.
- In case of an error, a response is sent back to the client with a `success` status set to `false`, and an error message is provided.
In summary, this route is designed to handle POST requests to fetch details about a show by its ID. It uses authentication middleware to secure the route, retrieves the show data from a database, populates related movie and theater information, and sends a response with the retrieved data or an error message, depending on the outcome of the database operation.
```javascript
export const GetShowById = async (payload) => {
try{
const response = await axiosInstance.post('/api/theatres/get-show-by-id',payload)
return response.data
}catch(err){
return err.message
}
}
```
The `GetShowById` function serves as a client-side utility for making HTTP POST requests to a server's `/api/theatres/get-show-by-id` endpoint. It is designed to retrieve specific show information based on a provided `payload`, which typically includes data necessary for the request, such as a `showId`.
The function operates asynchronously, utilizing Axios to handle the HTTP request. Within a `try...catch` block, it awaits the completion of the request, ensuring that the program doesn't proceed until a response is received.
On success, the function extracts and returns the response data from the server using `return response.data`. This allows the caller to access and utilize the fetched show data.
However, in the event of an error—whether due to network issues, server problems, or other reasons—the `catch` block captures and handles the error.
### How Many Seats Are There for That Show
Different shows have different number of the shows as below:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/448/original/upload_04c10a7c926af4fbb8d515abf9873734.png?1695995938)
```javascript
import { message } from "antd";
import { useEffect, useState } from "react"
import { useParams, useNavigate } from "react-router-dom";
import { GetShowById } from "../apicalls/theatres";
import {BookShowTickets, MakePayment } from "../apicalls/bookings";
import Button from "../components/Button";
import { HideLoading, ShowLoading } from "../redux/loadersSlice";
import StripeCheckout from "react-stripe-checkout";
import moment from "moment"
import { useDispatch, useSelector } from "react-redux";
export default function BookShow(){
const { user } = useSelector((state) => state.users);
const [selectedSeats, setSelectedSeats] = useState([]);
let [show,setShow] = useState();
const params = useParams();
const dispatch = useDispatch();
const navigate = useNavigate()
const getData = async() => {
try{
const response = await GetShowById({showId:params.id})
if(response.success){
setShow(response.data);
}else{
message.error(response.message)
}
}catch(err){
message.error(err.message)
}
}
const getSeats = () => {
const columns = 12;
const totalSeats = show.totalSeats; // 120
const rows = Math.ceil(totalSeats / columns); // 10
return (
<div>
<p className = "m - 4">Screen This Side</p>
<hr/>
<div className = "flex gap-1 flex-col p-2 card">
<hr/>
{Array.from(Array(rows).keys()).map((seat, index) => {
return (
<div className = "flex gap-1 justify-center">
{/* 0, 1 ,2, ,3, .. 11
0 [ [ 0, 1, 2, 3, 4, 5,6,7.. ,11],
1 [0, 1, 2, 3, .. ,11],
2 .
.
9 [0,1,2 , .... 11]
] */}
{Array.from(Array(columns).keys()).map((column, index) => {
const seatNumber = seat * columns + column + 1;
// 12*1 + 3+ 1 = 16
let seatClass = "seat";
// seat = 0 // coloumns = 12
//0 + 1 + 1 = 2
if (selectedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " selected-seat";
}
if (show.bookedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " booked-seat";
}
return (
seat * columns + column + 1 <= totalSeats && (
<div
className = {seatClass}
onClick = {() => {
if (selectedSeats.includes(seatNumber)) {
setSelectedSeats(
selectedSeats.filter((item) => item !== seatNumber)
);
} else {
setSelectedSeats([...selectedSeats, seatNumber]);
}
}}
>
<h1 className = "text-sm">{seat * columns + column + 1}</h1>
</div>
)
);
})}
</div>
);
})}
</div>
</div>
);
};
const book = async (transactionId) => {
try {
dispatch(ShowLoading());
const response = await BookShowTickets({
show: params.id,
seats: selectedSeats,
transactionId,
user: user._id,
});
if (response.success) {
message.success(response.message);
navigate("/profile");
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
const onToken = async (token) => {
// console.log(token)
try {
dispatch(ShowLoading());
const response = await MakePayment(
token,
selectedSeats.length * show.ticketPrice * 100
);
if (response.success) {
message.success(response.message);
// console.log(response.data);
book(response.data);
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
useEffect(() => {
getData();
},[])
return (
show && (
<div>
{/* show infomation */}
<div className = "flex justify-between card p-2 items-center">
<div>
<h1 className = "text-sm">{show.theatre.name}</h1>
<h1 className = "text-sm">{show.theatre.address}</h1>
</div>
<div>
<h1 className = "text-2xl uppercase">
{show.movie.title} ({show.movie.language})
</h1>
</div>
<div>
<h1 className = "text-sm">
{moment(show.date).format("MMM Do yyyy")} - {" "}
{moment(show.time, "HH:mm").format("hh:mm A")}
</h1>
</div>
</div>
{/* seats */}
<div className = "flex justify-center mt-2">{getSeats()}</div>
{selectedSeats.length > 0 && (
<div className = "mt-2 flex justify-center gap-2 items-center flex-col">
<div className = "flex justify-center">
<div className = "flex uppercase card p-2 gap-3">
<h1 className = "text-sm"><b>Selected Seats</b> : {selectedSeats.join(" , ")}</h1>
<h1 className = "text-sm">
<b>Total Price</b> : {selectedSeats.length * show.ticketPrice}
</h1>
</div>
</div>
<StripeCheckout
token = {onToken}
amount = {selectedSeats.length * show.ticketPrice * 100}
billingAddress
stripeKey = "pk_test_eTH82XLklCU1LJBkr2cSDiGL001Bew71X8"
>
<Button title = "Book Now" />
</StripeCheckout>
</div>
)}
</div>
)
);
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/449/original/upload_8519871882badc6cea20e45f3b757fda.png?1695996092)
Let's dive into the `BookShow` component and focus on the details, particularly the part that handles rendering the seats with the `for` loops:
1. **Component Structure**:<br> The `BookShow` component is a React functional component responsible for booking show tickets. It receives data about a show and allows users to select seats for booking.
2. **Fetching Show Data**:<br> The `getData` function is called when the component mounts (`useEffect`). It fetches details about the show specified by the `params.id` using the `GetShowById` API call and sets the show data in the component's state.
3. **Rendering Seats**:
- The `getSeats` function is responsible for rendering the seats grid. It calculates the number of rows and columns based on the `totalSeats` of the show.
- It returns a JSX structure that represents the seats grid. The grid is divided into rows and columns using nested `map` functions.
```jsx
{Array.from(Array(rows).keys()).map((seat, index) => {
return (
<div className="flex gap-1 justify-center">
{Array.from(Array(columns).keys()).map((column, index) => {
// ... Seat rendering logic
})}
</div>
);
})}
```
- The outer `map` iterates over the rows, and the inner `map` iterates over the columns. For each seat, it calculates a unique `seatNumber` based on the row and column indices.
- The seat's appearance (e.g., styling) depends on its characteristics, such as whether it's selected or booked. This is determined by the `seatClass`.
- Seats that are already booked (`show.bookedSeats`) are given a "booked-seat" class.
- Seats that are currently selected by the user are given a "selected-seat" class.
- Seats that are neither booked nor selected are rendered without any additional class.
4. **Selecting and Deselecting Seats**:
- Seats can be selected or deselected by clicking on them. This interaction is handled by the `onClick` event attached to each seat `<div>`.
- When a seat is clicked, the `setSelectedSeats` function is used to update the `selectedSeats` state based on whether the clicked seat is already in the selected seats array or not.
5. **Displaying Selected Seats and Total Price**:
- Below the seat grid, there's a section that displays the selected seats and the total price. This section is conditionally rendered only when there are selected seats (`selectedSeats.length > 0`).
- It shows the selected seat numbers joined by a comma and calculates the total price based on the number of selected seats and the ticket price for the show.
6. **Booking Seats and Making Payments**:
- The component includes functions (`book` and `onToken`) for booking seats and handling payment. When the user confirms the booking, these functions are called to initiate the booking process and make a payment.
## Seat Selection/Deselection Functionaities
```javascript
if (selectedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " selected-seat";
}
if (show.bookedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " booked-seat";
}
return (
seat * columns + column + 1 <= totalSeats && (
<div
className = {seatClass}
onClick = {() => {
if (selectedSeats.includes(seatNumber)) {
setSelectedSeats(
selectedSeats.filter((item) => item !== seatNumber)
);
} else {
setSelectedSeats([...selectedSeats, seatNumber]);
}
}}
>
<h1 className = "text-sm">{seat * columns + column + 1}</h1>
</div>
)
);
```
- **Class Assignment**:
- The `seatClass` variable is initially set to an empty string for each seat.
- The first `if` statement checks if the current seat number (calculated as `seat * columns + column + 1`) is included in the `selectedSeats` array. If it is, the `"selected-seat"` class is appended to `seatClass`.
- The second `if` statement checks if the seat number is included in the `show.bookedSeats` array. If it is, the `"booked-seat"` class is appended to `seatClass`.
- **Rendering Seats**:
- The code then returns a `div` element representing the seat.
- The condition `seat * columns + column + 1 <= totalSeats` ensures that only valid seats are rendered based on the total number of seats available.
- The `className` attribute is set to `seatClass`, which may include `"selected-seat"` and/or `"booked-seat"` classes based on the conditions.
- An `onClick` event handler is attached to the seat `div`. When a user clicks on a seat, it triggers this function.
- Inside the `onClick` function:
- If the clicked seat number is already in the `selectedSeats` array, it's removed from the array using `selectedSeats.filter()`. This means the user is deselecting the seat.
- If the clicked seat number is not in `selectedSeats`, it's added to the array, indicating the user's selection.
```css!
/* seats */
.seat {
height: 30px;
width: 50px;
display: flex;
justify-content: center;
align-items: center;
border : 1px solid #b1b1b1;
cursor: pointer;
}
.selected-seat {
background-color: #118838;
}
.selected-seat h1 {
color: white !important;
}
.booked-seat {
background-color: #b1b1b1;
cursor: not-allowed;
pointer-events: none;
}
```
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/450/original/upload_c4f6952777f7fe93082df2c33a466bea.png?1695996181)
The provided CSS code defines styles for rendering seats in a grid. Each seat (`.seat`) has a height and width, is centered both horizontally and vertically, and has a border to separate seats. The `cursor` property changes to a pointer to indicate interactivity. Selected seats (`.selected-seat`) have a green background and white text for visibility.
Booked seats (`.booked-seat`) have a gray background and a disabled cursor, preventing further interaction. These styles effectively visualize seat availability and user selections, enhancing the user experience in a seat selection interface.
## Add the Payment Options
"React Stripe Checkout" refers to a React library or component that allows developers to easily integrate Stripe's payment processing capabilities into their React applications. This library streamlines the process of creating a checkout experience, handling payments, and managing user interactions with the Stripe API.
Key features and steps typically involved in using "React Stripe Checkout" include:
1. **Installation**:<br> Developers can install the library in their React project using npm or yarn.
2. **Configuration**:<br> Setting up Stripe credentials and configuring payment options, such as currency and amount.
3. **Integration**:<br> Adding the "React Stripe Checkout" component to the application's payment page or modal.
4. **User Interaction**:<br> Users can enter their payment details, including card information.
5. **Tokenization**:<br> The library securely tokenizes the payment information, ensuring sensitive data is not exposed to the application server.
6. **Payment Processing**:<br> Stripe processes the payment using the token and completes the transaction.
```javascript
<StripeCheckout
token = {onToken}
amount = {selectedSeats.length * show.ticketPrice * 100}
billingAddress
stripeKey = "pk_test_eTH82XLklCU1LJBkr2cSDiGL001Bew71X8"
>
<Button title = "Book Now" />
</StripeCheckout>
```
The code snippet provided is an example of how you might use the "react-stripe-checkout" library to integrate Stripe's payment processing into a React application. Here's an explanation of the code:
1. **\<StripeCheckout> Component:** This is the main component from the "react-stripe-checkout" library, and it acts as a wrapper around your payment button. It allows you to configure and handle the payment process.
2. **token={onToken}:** This prop specifies the callback function (`onToken`) that will be called when the payment is successful. The `token` contains the payment information, which you can then use to process the payment on the server.
3. **amount={selectedSeats.length * show.ticketPrice * 100}:** This prop specifies the total amount to charge in the smallest currency unit (cents in this case). It calculates the total based on the number of selected seats and their individual ticket prices.
4. **billingAddress:** This prop indicates that you want to collect the customer's billing address during the payment process.
5. **stripeKey:** This prop is where you provide your Stripe API key (in test mode, as indicated by the "pk_test_" prefix). It connects your React app to your Stripe account.
6. **<Button title="Book Now" />:** This is the button or UI element that triggers the payment process when clicked. It's wrapped inside the `<StripeCheckout>` component, making it the "Pay Now" or "Book Now" button.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/451/original/upload_82e3bafc31613a0c60453969ca8cb490.png?1695996351)
When a user clicks the "Book Now" button, the `<StripeCheckout>` component will handle the payment process. If the payment is successful, it will call the `onToken` callback
function, allowing you to perform further actions, such as storing the payment details on your server or updating the UI to reflect the successful payment.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/452/original/upload_ae57e6a2ed7a0a47022fb038eb3b6a53.png?1695996513)
Make sure to replace the `stripeKey` value with your actual Stripe API key, and ensure that your Stripe account is set up correctly to handle payments in test mode.
In bookShow.js file make the below changes as follows:
```javascript
const onToken = async (token) => {
// console.log(token)
try {
dispatch(ShowLoading());
const response = await MakePayment(
token,
selectedSeats.length * show.ticketPrice * 100
);
if (response.success) {
message.success(response.message);
// console.log(response.data);
book(response.data);
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
```
* The onToken function is defined, which handles payment processing using Stripe.
* It dispatches actions for showing and hiding a loading indicator.
* It makes a payment request to the server using the MakePayment function and the payment token.
* Depending on the response, it displays success or error messages and potentially triggers further actions like booking.
In bookRoute.js file make the below changes as follows:
```javascript
const authMiddleware = require("../middlewares/authMiddleware");
const router = require("express").Router();
const stripe = require("stripe")(process.env.stripe_key);
const Booking = require('../models/bookingModel')
const Show = require('../models/showModel')
router.post('/make-payment',authMiddleware,async(req,res) => {
try{
const {token,amount} = req.body;
console.log(token);
const customer = await stripe.customers.create({
email:token.email,
source:token.id
})
const charge = await stripe.charges.create({
amount:amount,
currency:"usd",
customer:customer.id,
receipt_email:token.email,
description:"Ticket has been booked for a movie"
})
const transactionId = charge.id;
res.send({
success:true,
message:"Payment done, Ticket booked",
data:transactionId
})
}catch(err){
res.send({
success:false,
message:err.message
})
}
})
```
* The Express route "/make-payment" is defined, which expects a POST request with payment-related data, including the token and amount.
* It uses the Stripe API to create a customer and charge their card.
* If the payment is successful, it returns a success message along with a transaction ID.
* If there's an error, it returns an error message.
In booking.js file make the below changes as follows:
```javascript
import { axiosInstance } from "."
export const MakePayment = async(token,amount) => {
try{
const response = await axiosInstance.post('/api/bookings/make-payment',{token,amount});
return response.data
}catch(err){
return err.response
}
}
```
The MakePayment function is defined, which makes an API call to your server's "/make-payment" endpoint with the payment token and amount.
It returns the response data if successful or the error response if there's an issue.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/475/original/upload_9ed9f36f03a3bea064f0f0844874a731.png?1696008818)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/476/original/upload_a136b39f32498d0585a1b504ab4c675b.png?1696008840)
## Bookings in the Profile
```javascript
const mongoose = require("mongoose");
const bookingSchema = new mongoose.Schema(
{
show: {
type: mongoose.Schema.Types.ObjectId,
ref: "shows",
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "users",
},
seats: {
type: Array,
required: true,
},
transactionId: {
type: String,
required: true,
},
},
{ timestamps: true }
);
module.exports = mongoose.model("bookings", bookingSchema);
```
* A Mongoose schema called bookingSchema is defined, representing the structure of booking documents.
* It includes fields such as show (a reference to the show), user (a reference to the user making the booking), seats (an array of selected seats), and transactionId (a unique identifier for the transaction).
* The schema is exported as a Mongoose model named "bookings."
In bookingRoute.js file make the below changes as follows:
```javascript
//book shows
router.post("/book-show", authMiddleware, async (req, res) => {
try {
// save booking
const newBooking = new Booking(req.body);
await newBooking.save();
const show = await Show.findById(req.body.show);
// update seats
await Show.findByIdAndUpdate(req.body.show, {
bookedSeats: [...show.bookedSeats, ...req.body.seats],
});
res.send({
success: true,
message: "Show booked successfully",
data: newBooking,
});
} catch (error) {
res.send({
success: false,
message: error.message,
});
}
});
router.get("/get-bookings", authMiddleware, async (req, res) => {
try {
const bookings = await Booking.find({ user: req.body.userId })
.populate("user")
.populate("show")
.populate({
path: "show",
populate: {
path: "movie",
model: "movies",
},
})
.populate({
path: "show",
populate: {
path: "theatre",
model: "theatres",
},
});
res.send({
success: true,
message: "Bookings fetched successfully",
data: bookings,
});
} catch (error) {
res.send({
success: false,
message: error.message,
});
}
});
```
* A new route handler "`/book-show`" is defined, which expects a POST request to book a show.
* It creates a new booking record using the provided data and saves it to the database.
* It also fetches the existing show data to update the list of booked seats.
* The response includes a success message and the newly created booking data or an error message in case of failure.
* Another route handler "`/get-bookings`" is defined, which expects a GET request to fetch all bookings of a user.
* It retrieves user-specific booking records, populates relevant data (user, show, movie, and theatre), and sends them in the response.
* The response includes a success message and the booking data or an error message in case of failure.
In bookings.js file make the below changes as follows:
```javascript
// book shows
export const BookShowTickets = async (payload) => {
try {
const response = await axiosInstance.post(
"/api/bookings/book-show",
payload
);
return response.data;
} catch (error) {
return error.response.data;
}
};
export const GetBookingsOfUser = async () => {
try {
const response = await axiosInstance.get("/api/bookings/get-bookings");
return response.data;
} catch (error) {
return error.response.data;
}
};
```
* Two functions are defined to make API calls related to bookings.
* BookShowTickets sends a POST request to the "`/book-show`" endpoint to book show tickets, including the show ID, selected seats, transaction ID, and user ID.
* `GetBookingsOfUser` sends a GET request to the "`/get-bookings`" endpoint to fetch the bookings of the currently authenticated user.
In bookingShow.js file make the below changes as follows:
```javascript
const book = async (transactionId) => {
try {
dispatch(ShowLoading());
const response = await BookShowTickets({
show: params.id,
seats: selectedSeats,
transactionId,
user: user._id,
});
if (response.success) {
message.success(response.message);
navigate("/profile");
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
```
* The book function is defined, which is responsible for handling the booking of show tickets.
* It makes an API call using BookShowTickets, passing relevant data such as the **show ID, selected seats, transaction ID, and user ID**.
* Depending on the response, it displays success or error messages and potentially navigates to the user's profile page.
* Loading indicators are shown while the booking process is in progress.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/477/original/upload_56c81be1c50ede02d12ccb1deb18708e.png?1696009093)

View File

@@ -0,0 +1,639 @@
## Agenda
**Topics to cover in Node:**
* Exploring different node inbuilt modules
* Understanding server side development with node (http)
* Understanding http module with req and res
* Serving static html sites & json files via the server
We will try to cover most of these topics in today's sessions and the remaining in the next.
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## Nodejs
Node.js is an open-source, server-side JavaScript runtime environment that allows you to run JavaScript code on the server. It was created by Ryan Dahl and first released in 2009. Node.js is built on the V8 JavaScript engine, which is also used in Google Chrome, and it provides an event-driven, non-blocking I/O model that makes it well-suited for building scalable and high-performance network applications.
## Why use nodejs for web server
Node.js is a popular choice for web servers, especially in scenarios involving heavy I/O operations and small server requirements. Here's why Node.js is a suitable option for such use cases:
1. **Non-Blocking I/O Model:** Node.js is designed around a non-blocking, event-driven architecture. This means it can efficiently handle multiple I/O operations concurrently without blocking the execution of other tasks. When performing heavy I/O operations, such as reading and writing files, making network requests, or interacting with databases, Node.js can initiate these operations and continue executing other code while waiting for the I/O operations to complete. This asynchronous approach is highly advantageous for scenarios with many concurrent I/O tasks.
2. **Scalability:** In situations involving heavy I/O, it's common for multiple clients to make simultaneous requests to the server. Node.js's non-blocking model allows it to handle a large number of concurrent connections efficiently, making it a suitable choice for scalable applications. It can process incoming requests as soon as they arrive, rather than waiting for each request to complete before moving on to the next one.
3. **Low Overhead:** Node.js has a relatively small memory footprint compared to some other web server technologies. This makes it well-suited for small server applications where resource utilization needs to be efficient. You can run multiple Node.js instances on a single server without consuming excessive system resources.
5. **Rich Ecosystem:** Node.js has a vast ecosystem of libraries and modules available through npm, which can simplify the development of web servers for various purposes. Developers can find packages to handle specific I/O tasks, such as file uploads, database connections, and HTTP requests, making it easier to build web servers tailored to their needs.
## How are we going to learn nodejs development
Learning Node.js development and building a full-stack MERN (MongoDB, Express.js, React, Node.js) application involves multiple steps and concepts. Here's a roadmap to guide you through the process:
1. **Basic JavaScript Knowledge**:
Before diving into Node.js, ensure you have a strong foundation in JavaScript, as Node.js is JavaScript on the server-side.
2. **Node.js Fundamentals**:
Start by learning the basics of Node.js, including installation, core modules, and working with the Node.js runtime. You can find introductory tutorials and courses online.
3. **Module and Package Management (npm)**:
Understand how to use npm (Node Package Manager) to manage dependencies and create projects. Learn how to initialize a project, install packages, and create a package.json file.
4. **Server Concepts with Node.js**:
Explore the core concepts of building a server with Node.js. This includes creating an HTTP server, handling requests, and sending responses.
5. **Web Server with Express.js**:
Dive into Express.js, a popular Node.js framework for building web applications and APIs. Learn how to set up routes, handle HTTP requests, and use middleware for tasks like authentication and error handling.
6. **RESTful API with Express.js**:
Extend your Express.js knowledge to create RESTful APIs. Understand HTTP methods (GET, POST, PUT, DELETE), request and response handling, and best practices for building APIs.
7. **MVC Architecture**:
Explore the Model-View-Controller (MVC) architectural pattern and how it applies to building web applications with Node.js and Express.js. Organize your code into models, views, and controllers for better maintainability.
8. **MongoDB**:
Learn about MongoDB, a NoSQL database commonly used with Node.js. Understand how to install MongoDB, perform CRUD operations, and work with collections and documents.
9. **Mongoose**:
Integrate Mongoose, an ODM (Object-Document Mapping) library, with your Node.js and Express.js application to simplify database operations and schema management.
10. **JWT Authentication**:
Implement JSON Web Token (JWT) authentication to secure your APIs. Learn how to generate tokens, validate them, and protect routes using JWT.
11. **React Integration**:
If you want to build a MERN application, learn React for front-end development. Understand React components, state management, and how to create a user interface.
12. **Integration of MERN App**:
Combine your knowledge of Node.js, Express.js, MongoDB, and React to build a full-stack MERN application. Create RESTful APIs on the server and connect them to your React front-end.
13. **Miscellaneous**:
Explore other concepts like error handling, testing (using tools like Jest), deployment (using platforms like Heroku or AWS), and performance optimization.
14. **Practice and Build Projects**:
The best way to solidify your skills is by working on projects. Start with simple projects and gradually move on to more complex ones to gain practical experience.
## Fs module in depth
The `fs` module in Node.js stands for "File System," and it provides a way to work with the file system on your computer or server. It allows you to read from and write to files, manipulate directories, perform file operations, and more. Let's explore some of the key functionalities of the `fs` module in-depth:
**1. Reading Files:**
The `fs` module provides methods for reading the contents of files. The most commonly used method for this purpose is `fs.readFile()`:
```javascript
const fs = require('fs');
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
console.log(data);
});
```
In this example, `readFile()` reads the content of 'example.txt' and then calls the provided callback function with any errors encountered and the file's contents.
**2. Writing Files:**
You can also use the `fs` module to write data to files using methods like `fs.writeFile()`:
```javascript
const fs = require('fs');
const content = 'Hello, world!';
fs.writeFile('example.txt', content, 'utf8', (err) => {
if (err) {
console.error(err);
return;
}
console.log('File has been written.');
});
```
Here, `writeFile()` creates or overwrites 'example.txt' with the provided content.
**3. Synchronous vs. Asynchronous Operations:**
Most `fs` module functions come in both synchronous and asynchronous versions. The asynchronous versions (e.g., `fs.readFile()`) allow non-blocking file operations, while synchronous versions (e.g., `fs.readFileSync()`) block the Node.js event loop until the operation is complete.
Asynchronous methods are typically preferred in Node.js to maintain the application's responsiveness.
**4. Working with Directories:**
You can perform operations on directories using methods like `fs.mkdir()`, `fs.rmdir()`, `fs.readdir()`, and `fs.stat()`. These methods allow you to create, remove, list, and get information about directories, respectively.
**5. Renaming and Deleting Files:**
`fs.rename()` can be used to rename files, and `fs.unlink()` to delete them:
```javascript
fs.rename('old-file.txt', 'new-file.txt', (err) => {
if (err) {
console.error(err);
return;
}
console.log('File has been renamed.');
});
fs.unlink('file-to-delete.txt', (err) => {
if (err) {
console.error(err);
return;
}
console.log('File has been deleted.');
});
```
**6. File Statistics:**
The `fs.stat()` method provides information about a file's status, including its size, permissions, and modification timestamp.
```javascript
fs.stat('example.txt', (err, stats) => {
if (err) {
console.error(err);
return;
}
console.log('File size: ' + stats.size);
console.log('Is directory? ' + stats.isDirectory());
});
```
**7. Watching for Changes:**
Node.js also allows you to watch for changes to files and directories using `fs.watch()` and `fs.watchFile()` methods, which can be useful for real-time monitoring of file system changes.
In Node.js, you can use the `fs` module to create and delete directories. Here's how you can do it:
**Creating a Directory:**
To create a directory, you can use the `fs.mkdir()` method. Here's an example:
```javascript
const fs = require('fs');
const directoryName = 'my-directory';
fs.mkdir(directoryName, (err) => {
if (err) {
console.error(`Error creating directory: ${err}`);
} else {
console.log(`Directory "${directoryName}" created successfully.`);
}
});
```
In this code, `fs.mkdir()` is used to create a directory named "my-directory." The callback function is called when the directory creation is complete. If an error occurs, it will be logged.
**Deleting a Directory:**
To delete a directory, you can use the `fs.rmdir()` method. Here's an example:
```javascript
const fs = require('fs');
const directoryName = 'my-directory';
fs.rmdir(directoryName, { recursive: true }, (err) => {
if (err) {
console.error(`Error deleting directory: ${err}`);
} else {
console.log(`Directory "${directoryName}" deleted successfully.`);
}
});
```
In this code, `fs.rmdir()` is used to delete the "my-directory" directory. The `{ recursive: true }` option is provided to ensure that the directory and its contents are deleted recursively. The callback function is called when the deletion is complete, and any errors are logged.
Make sure to handle errors appropriately when creating or deleting directories in your Node.js applications to ensure that your code is robust and reliable.
You can check whether a directory or file exists in Node.js using the `fs` module. Here's how you can do it:
**Checking if a Directory Exists:**
To check if a directory exists, you can use the `fs.existsSync()` method. Here's an example:
```javascript
const fs = require('fs');
const directoryPath = '/path/to/your/directory';
if (fs.existsSync(directoryPath)) {
console.log(`The directory "${directoryPath}" exists.`);
} else {
console.log(`The directory "${directoryPath}" does not exist.`);
}
```
Replace `/path/to/your/directory` with the actual path to the directory you want to check. `fs.existsSync()` returns `true` if the directory exists and `false` if it doesn't.
**Checking if a File Exists:**
To check if a file exists, you can use the `fs.existsSync()` method as well. Here's an example:
```javascript
const fs = require('fs');
const filePath = '/path/to/your/file.txt';
if (fs.existsSync(filePath)) {
console.log(`The file "${filePath}" exists.`);
} else {
console.log(`The file "${filePath}" does not exist.`);
}
```
Replace `/path/to/your/file.txt` with the actual path to the file you want to check. `fs.existsSync()` returns `true` if the file exists and `false` if it doesn't.
It's worth noting that `fs.existsSync()` is a synchronous method, so it can block the Node.js event loop. If you prefer an asynchronous approach, you can use the `fs.access()` method. Here's an example:
```javascript
const fs = require('fs');
const pathToCheck = '/path/to/your/directory-or-file';
fs.access(pathToCheck, fs.constants.F_OK, (err) => {
if (err) {
console.log(`The path "${pathToCheck}" does not exist.`);
} else {
console.log(`The path "${pathToCheck}" exists.`);
}
});
```
Replace `/path/to/your/directory-or-file` with the actual path you want to check. The `fs.access()` method asynchronously checks if the path exists and whether it's accessible (in this case, using the `fs.constants.F_OK` flag to check for existence).
Both approaches can be used to determine whether a directory or file exists in your Node.js application, depending on your preference for synchronous or asynchronous code.
## Path Module
The `path` module in Node.js provides utilities for working with file and directory paths. It's an essential module when dealing with file system operations and path manipulation in your Node.js applications. Here are some important functions and concepts from the `path` module:
1. **`path.join([...paths])`**: This method joins multiple path segments into a single path string, taking care of platform-specific path separators (e.g., backslashes on Windows and forward slashes on Unix-like systems).
```javascript
const path = require('path');
const fullPath = path.join('folder', 'subfolder', 'file.txt');
```
2. **`path.resolve([...paths])`**: Resolves an absolute path from multiple path segments, starting from the root directory. It can be helpful for creating absolute paths based on relative ones.
```javascript
const path = require('path');
const absolutePath = path.resolve('folder', 'subfolder', 'file.txt');
```
3. **`path.basename(path[, ext])`**: Returns the base filename of a path, optionally removing a file extension if provided.
```javascript
const path = require('path');
const fileName = path.basename('/path/to/file.txt');
```
4. **`path.dirname(path)`**: Returns the directory name of a path.
```javascript
const path = require('path');
const dirName = path.dirname('/path/to/file.txt');
```
5. **`path.extname(path)`**: Returns the file extension of a path, including the dot.
```javascript
const path = require('path');
const extension = path.extname('/path/to/file.txt');
```
6. **`path.parse(pathString)`**: Parses a path string into an object with properties like `root`, `dir`, `base`, `name`, and `ext`.
```javascript
const path = require('path');
const pathInfo = path.parse('/path/to/file.txt');
```
7. **`path.normalize(path)`**: Normalizes a path by resolving '..' and '.' segments and converting slashes to the appropriate platform format.
```javascript
const path = require('path');
const normalizedPath = path.normalize('/path/to/../file.txt');
```
8. **`path.isAbsolute(path)`**: Checks if a path is an absolute path.
```javascript
const path = require('path');
const isAbsolute = path.isAbsolute('/path/to/file.txt');
```
9. **`path.relative(from, to)`**: Returns the relative path from one path to another.
```javascript
const path = require('path');
const relativePath = path.relative('/path/from', '/path/to');
```
The `path` module is particularly useful when working on cross-platform applications or when dealing with file and directory paths dynamically in your Node.js code. It ensures that your path manipulation is consistent and compatible with various operating systems.
### Copy a file from one folder to another in Node.js
To copy a file from one folder to another in Node.js, you can use the `fs` module. Here's how you can do it:
```javascript
const fs = require('fs');
const path = require('path');
// Define the source and destination file paths
const sourceFilePath = '/path/to/source-folder/source-file.txt';
const destinationFilePath = '/path/to/destination-folder/destination-file.txt';
// Create a readable stream from the source file
const readStream = fs.createReadStream(sourceFilePath);
// Create a writable stream to the destination file
const writeStream = fs.createWriteStream(destinationFilePath);
// Pipe the data from the source file to the destination file
readStream.pipe(writeStream);
// Handle any errors that may occur during the copy process
readStream.on('error', (err) => {
console.error(`Error reading the source file: ${err}`);
});
writeStream.on('error', (err) => {
console.error(`Error writing to the destination file: ${err}`);
});
// When the copy is complete, log a success message
writeStream.on('finish', () => {
console.log('File copied successfully.');
});
```
In this code:
1. Replace `/path/to/source-folder/source-file.txt` with the actual path to the source file you want to copy.
2. Replace `/path/to/destination-folder/destination-file.txt` with the desired path and name for the destination file.
Here's an explanation of what the code does:
- It uses the `fs.createReadStream()` method to create a readable stream from the source file.
- It uses the `fs.createWriteStream()` method to create a writable stream to the destination file.
- It uses the `.pipe()` method to pipe the data from the source stream to the destination stream, effectively copying the file.
- It sets up error event listeners on both the source and destination streams to handle any errors that may occur during the copy process.
- It sets up a finish event listener on the destination stream to log a success message when the copy is complete.
This code will copy the contents of the source file to the destination file. If the destination file already exists, it will be overwritten. Make sure to handle errors and adjust file paths as needed for your specific use case.
---
title: Server side development
description: Exploring Server side Development with http module
duration: 2100
card_type: cue_card
---
## Server side development
Server-side development, client-side development, and working with database clients are essential components of modern web application development. Let's explore these concepts:
**1. Server-Side Development:**
Server-side development refers to the part of web application development that occurs on the server, typically using server-side technologies and programming languages. Here are key aspects:
- **Server:** A server is a computer or software application that responds to client requests over a network. In web development, a server typically hosts the backend of a web application.
- **Server-Side Technologies:** Common server-side technologies include Node.js, Python (with frameworks like Django or Flask), Ruby (with Ruby on Rails), Java (with Spring or Java EE), PHP, and more. These technologies enable you to create the server logic, handle requests from clients, interact with databases, and generate dynamic content.
- **Server Logic:** Server-side code manages user authentication, business logic, data processing, and database interactions. It often generates HTML, JSON, or other data to send back to the client.
- **Security:** Security measures like input validation, authentication, authorization, and protecting against common vulnerabilities (e.g., SQL injection, XSS) are typically implemented on the server side.
**2. Client-Side Development:**
Client-side development focuses on the part of web application development that occurs in the user's web browser. Here are key aspects:
- **Client:** The client is the user's device (e.g., web browser) that sends requests to a server to access web content or services.
- **Client-Side Technologies:** Common client-side technologies include HTML, CSS, and JavaScript. HTML is used for structuring web content, CSS for styling, and JavaScript for adding interactivity and functionality to web pages.
- **Front-End Frameworks:** Developers often use front-end frameworks and libraries like React, Angular, or Vue.js to build complex and responsive user interfaces.
- **User Experience:** Client-side development is responsible for creating an engaging and user-friendly experience. This includes handling user interactions, form validations, and rendering dynamic content without requiring full page reloads.
- **Performance:** Optimizing client-side performance is crucial, as the client device has limited resources. Techniques like lazy loading, minification, and caching are employed to enhance the user experience.
**3. Database Client:**
A database client is a software component or library that allows your server-side code to communicate with a database management system (DBMS). Here are key aspects:
- **Database Management System (DBMS):** A DBMS is software that manages databases, including storing, retrieving, updating, and organizing data. Examples of DBMSs include MySQL, PostgreSQL, MongoDB, and SQLite.
- **Database Client Libraries:** To interact with a DBMS, developers use specific client libraries or drivers for their chosen programming language. These libraries provide functions and methods to connect to the database, execute queries, and retrieve results.
- **ORM (Object-Relational Mapping):** Some server-side frameworks and languages offer ORMs (e.g., Sequelize for Node.js, Hibernate for Java) that provide a higher-level abstraction for working with databases. ORMs map database tables to objects in code, simplifying database interactions.
- **Data Access:** Server-side code uses database clients to perform CRUD operations (Create, Read, Update, Delete) on data stored in databases. This includes querying data, inserting new records, updating existing records, and deleting records.
## Server-side development using the `http` module
Server-side development using the `http` module in Node.js allows you to create a basic HTTP server to handle incoming requests and send responses. Here's a step-by-step guide to building a simple HTTP server using the `http` module:
1. **Import the `http` Module:**
Start by requiring the `http` module in your Node.js script:
```javascript
const http = require('http');
```
2. **Create the HTTP Server:**
You can create an HTTP server using the `http.createServer()` method. This method takes a callback function that will be invoked for each incoming HTTP request.
```javascript
const server = http.createServer((req, res) => {
// Handle incoming requests here
});
```
3. **Handle Incoming Requests:**
Inside the callback function, you can handle incoming HTTP requests. The `req` object represents the request, and the `res` object is used to send the response back to the client.
```javascript
const server = http.createServer((req, res) => {
// Set response header
res.setHeader('Content-Type', 'text/plain');
// Write response content
res.write('Hello, World!');
// End the response
res.end();
});
```
In this example, we set the `Content-Type` header to `'text/plain'`, write "Hello, World!" as the response content, and then end the response.
4. **Specify the Listening Port and Host:**
You need to specify the port and host (usually `'localhost'` for development) on which your server will listen for incoming requests:
```javascript
const port = 3000;
const host = 'localhost';
server.listen(port, host, () => {
console.log(`Server is listening on http://${host}:${port}`);
});
```
5. **Start the Server:**
Finally, you can start the server by calling the `server.listen()` method. This will start listening for incoming HTTP requests on the specified port and host.
6. **Test Your Server:**
Run your Node.js script, and your server will be accessible at the specified URL (e.g., http://localhost:3000). You can use a web browser or tools like cURL or Postman to send HTTP requests to your server.
Here's the complete code for a basic HTTP server:
```javascript
const http = require('http');
const server = http.createServer((req, res) => {
// Set response header
res.setHeader('Content-Type', 'text/plain');
// Write response content
res.write('Hello, World!');
// End the response
res.end();
});
const port = 3000;
const host = 'localhost';
server.listen(port, host, () => {
console.log(`Server is listening on http://${host}:${port}`);
});
```
This simple HTTP server will respond with "Hello, World!" to any incoming request. You can expand upon this foundation to handle different types of requests and serve dynamic content based on the requested URL or route.
You can modify the code to send an HTML response containing an `<h1>` tag. Here's an example of a Node.js HTTP server that responds with an HTML `<h1>` tag:
```javascript
const http = require('http');
const server = http.createServer((req, res) => {
// Set response header with Content-Type as text/html
res.setHeader('Content-Type', 'text/html');
// Write HTML response
res.write('<html><head><title>Node.js HTTP Server</title></head><body>');
res.write('<h1>Hello, World!</h1>');
res.write('</body></html>');
// End the response
res.end();
});
const port = 3000;
const host = 'localhost';
server.listen(port, host, () => {
console.log(`Server is listening on http://${host}:${port}`);
});
```
In this code:
- We set the `Content-Type` header to `'text/html'` to indicate that the response will contain HTML content.
- We use `res.write()` to send the HTML content, which includes an `<h1>` tag with the text "Hello, World!".
- The response is ended with `res.end()`.
Now, when you access the server in your web browser, you will receive an HTML response with the specified `<h1>` tag. This demonstrates how you can send HTML content as a response using the Node.js `http` module.
### Nodemon
Nodemon is a tool that helps you develop Node.js applications by automatically restarting the Node.js process whenever changes are detected in your project's files. It's particularly useful during development because it eliminates the need to manually stop and restart your Node.js application every time you make code changes.
Here's how to use Nodemon:
1. **Installation**:
You can install Nodemon globally using npm (Node Package Manager) with the following command:
```bash
npm install -g nodemon
```
Alternatively, you can install it as a development dependency in your project by running the following command inside your project directory:
```bash
npm install --save-dev nodemon
```
2. **Basic Usage**:
After installing Nodemon, you can use it to run your Node.js application instead of the standard `node` command. For example:
```bash
nodemon your-app.js
```
Replace `your-app.js` with the entry point file of your Node.js application.
3. **Automatic Restart**:
Nodemon will watch for changes in the project directory and its subdirectories. Whenever you save changes to your code, Nodemon will automatically restart your Node.js application with the updated code. This eliminates the need to manually stop and restart the application.
4. **Nodemon Configuration**:
Nodemon allows you to customize its behavior by using a `nodemon.json` configuration file or specifying configuration options in your project's `package.json` file. You can configure things like which files to watch, ignore specific files or directories, and more.
Example `nodemon.json`:
```json
{
"watch": ["src"],
"ignore": ["node_modules"]
}
```
Example `package.json` (under the `"nodemon"` key):
```json
"nodemon": {
"watch": ["src"],
"ignore": ["node_modules"]
}
```
5. **Additional Features**:
Nodemon offers additional features such as running scripts, passing environment variables, and more. You can explore these options in the Nodemon documentation and tailor them to your specific needs.
Nodemon is a valuable tool for improving the development workflow in Node.js projects, as it simplifies the process of testing and iterating on your code. It's commonly used in combination with development frameworks like Express.js to streamline web application development.
### Json in the response and setting the header
You can modify the code to send a JSON response from your Node.js HTTP server. Here's an example of a Node.js HTTP server that responds with JSON data:
```javascript
const http = require('http');
const server = http.createServer((req, res) => {
// Set response header with Content-Type as application/json
res.setHeader('Content-Type', 'application/json');
// Define JSON data
const jsonData = {
message: 'Hello, World!',
date: new Date(),
};
// Convert JSON object to a JSON string
const jsonResponse = JSON.stringify(jsonData);
// Write JSON response
res.write(jsonResponse);
// End the response
res.end();
});
const port = 3000;
const host = 'localhost';
server.listen(port, host, () => {
console.log(`Server is listening on http://${host}:${port}`);
});
```
In this code:
- We set the `Content-Type` header to `'application/json'` to indicate that the response will contain JSON data.
- We define a JSON object called `jsonData`, which contains a message and the current date.
- We use `JSON.stringify()` to convert the JSON object into a JSON string.
- The response is ended with `res.end()`.
Now, when you access the server in your web browser or send an HTTP request to it, you will receive a JSON response containing the specified data. This demonstrates how you can send JSON data as a response using the Node.js `http` module.

View File

@@ -0,0 +1,467 @@
## Agenda
**Topics to cover in Express:**
We will try to cover most of these topics in today's sessions and the remaining in the next.
* What is express
* How to use express with node
* Http methods-get,post,put,delete, patch
* Postman-to test your api endpoints
* Middlewares
It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
## Express Module
In Node.js, the Express module is a popular framework for building web applications and APIs. It simplifies the process of handling HTTP requests and responses and provides a structured way to create routes and middleware. Let's explore the Express module with some examples.
**Example 1: Setting Up an Express Application**
First, you need to install Express in your project:
```bash
npm install express --save
```
Now, let's create a simple Express application:
```javascript
// Import the Express module
const express = require('express');
// Create an Express application
const app = express();
// Define a route
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
// Start the server
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
In this example:
- We import the `express` module and create an Express application instance.
- We define a route for the root URL ("/") using `app.get()`, which responds with "Hello, Express!" when accessed via a GET request.
- We start the server on port 3000.
**Example 2: Express Routes**
Express allows you to define multiple routes for different URL paths and HTTP methods. Here's an example:
```javascript
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
app.get('/about', (req, res) => {
res.send('This is the about page.');
});
app.post('/data', (req, res) => {
res.send('Received a POST request.');
});
```
In this example:
- We define a route for the root URL ("/") and an "/about" page that responds to GET requests.
- We also define a route for "/data" that responds to POST requests.
---
### Postman
Postman is a popular and powerful tool used by developers and testers to simplify the process of testing APIs (Application Programming Interfaces). It provides an intuitive graphical user interface (GUI) that allows you to send HTTP requests to your API endpoints, inspect responses, and automate API testing. Here are some key features and uses of Postman:
1. **Creating a Request**:
- Open Postman and click the "New" button to create a new request.
- Choose the HTTP method (e.g., GET, POST) for your request.
2. **Request URL**:
- Enter the URL of the API endpoint you want to test in the request URL field.
3. **Headers**:
- You can set request headers by clicking on the "Headers" tab.
- Headers are used to pass additional information to the server, such as authentication tokens or content type.
4. **Request Body**:
- If your request requires a request body (e.g., for POST or PUT requests), you can define it in the "Body" tab.
- You can send data in various formats like JSON, form-data, x-www-form-urlencoded, etc.
5. **Sending a Request**:
- Click the "Send" button to send the request to the specified endpoint.
- Postman will display the response in the lower part of the window.
6. **Response**:
- You can view the response status code, headers, and body in the response section.
- You can also format and highlight the response body using the options provided.
7. **Collections**:
- Organize your requests into collections for better management.
- Create a new collection by clicking the "New" button under "Collections."
8. **Variables**:
- Use environment and global variables to store values that can be reused across requests.
- Variables are helpful for managing different environments (e.g., development, production) or dynamic data.
9. **Tests and Assertions**:
- Write test scripts to validate the response data.
- You can use JavaScript to write custom tests.
- Use the "Tests" tab in the request to add test scripts.
These are some of the basic concepts and features of Postman. It's a versatile tool with many capabilities that can greatly simplify the process of working with APIs, testing, and collaborating with your team. As you become more familiar with Postman, you can explore its advanced features and customization options to suit your specific needs.
Here are simple examples of how to implement POST and DELETE requests in an Express.js application.
**POST Request Example**:
In this example, we'll create a basic Express application that handles a POST request to add a new user to a list of users.
```javascript
const express = require('express');
const app = express();
const port = 3000;
// Middleware to parse JSON request bodies
app.use(express.json());
// Sample user data (in-memory storage)
let users = [
{ id: 1, name: 'User 1' },
{ id: 2, name: 'User 2' }
];
// POST endpoint to add a new user
app.post('/users', (req, res) => {
const newUser = req.body;
// Assign a unique ID to the new user (in a real app, you'd typically use a database)
const userId = users.length + 1;
newUser.id = userId;
// Add the new user to the list
users.push(newUser);
res.status(201).json({ message: 'User created', user: newUser });
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
To test this, send a POST request to `http://localhost:3000/users` with a JSON body, for example:
```json
{
"name": "New User"
}
```
The server will respond with a JSON message confirming that the user has been created.
**DELETE Request Example**:
In this example, we'll create an Express application that handles a DELETE request to remove a user from the list.
```javascript
const express = require('express');
const app = express();
const port = 3000;
// Sample user data (in-memory storage)
let users = [
{ id: 1, name: 'User 1' },
{ id: 2, name: 'User 2' }
];
// DELETE endpoint to delete a user by ID
app.delete('/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
// Find the user index by ID
const userIndex = users.findIndex(user => user.id === userId);
if (userIndex === -1) {
return res.status(404).json({ message: 'User not found' });
}
// Remove the user from the array
users.splice(userIndex, 1);
res.json({ message: 'User deleted' });
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
To test this, send a DELETE request to `http://localhost:3000/users/1` to delete the user with ID 1.
These examples demonstrate how to implement POST and DELETE requests in an Express.js application. They focus on the core functionality without extensive error handling or database interactions. In practice, you would typically include more robust validation and potentially use a database for data storage.
## Middleware
Middleware functions in Express.js are functions that have access to the request (`req`) and response (`res`) objects and can perform actions or transformations on them. They are used to handle tasks like parsing request data, authentication, logging, error handling, and more. Middleware functions can be added to your Express application using `app.use()` or applied to specific routes using `app.use()` or `app.METHOD()` (e.g., `app.get()`, `app.post()`).
Here's a simple example of how to create and use middleware in an Express.js application:
```javascript
const express = require('express');
const app = express();
const port = 3000;
// Custom middleware function
const loggerMiddleware = (req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next(); // Call the next middleware in the chain
};
// Register the middleware globally for all routes
app.use(loggerMiddleware);
// Route handler
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
In this example:
1. We define a custom middleware function called `loggerMiddleware`. It logs the request method and URL along with a timestamp.
2. We use `app.use()` to register the `loggerMiddleware`, which means it will be executed for all incoming requests.
3. We have a simple route handler for the root path ("/") that sends a "Hello, Express!" response.
When you run this Express application, every incoming request will trigger the `loggerMiddleware` to log information about the request. This is just one example of middleware; you can create and use middleware functions for various purposes, including authentication, request validation, error handling, and more.
Middleware functions are executed in the order they are registered with `app.use()`, so the order of middleware registration matters. You can also apply middleware to specific routes by using `app.use()` or `app.METHOD()` for those routes.
For example, if you wanted to apply `loggerMiddleware` only to a specific route, you could do the following:
```javascript
app.get('/special', loggerMiddleware, (req, res) => {
res.send('This route is special!');
});
```
In this case, the `loggerMiddleware` will only run for requests to the "/special" route.
Remember that middleware functions can perform various tasks, and you can create custom middleware to suit your application's needs.
### Authentication middleware
Authentication middleware in an Express.js application is used to check if a user is authenticated before allowing access to certain routes or resources. Below is a simple example of how to create an authentication middleware to protect routes and ensure users are authenticated before accessing them. We'll use a basic username and password authentication mechanism for demonstration purposes.
Here's an example where we use `app.use` to protect all routes with the authentication middleware:
```javascript
const express = require('express');
const app = express();
const port = 3000;
// Sample user data (in-memory storage)
const users = [
{ id: 1, username: 'user1', password: 'password1' },
{ id: 2, username: 'user2', password: 'password2' },
];
// Middleware for basic authentication
const authenticate = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ message: 'Unauthorized' });
}
const [authType, authCredentials] = authHeader.split(' ');
if (authType !== 'Basic') {
return res.status(401).json({ message: 'Unauthorized' });
}
const credentials = Buffer.from(authCredentials, 'base64').toString('utf-8');
const [username, password] = credentials.split(':');
const user = users.find((u) => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: 'Unauthorized' });
}
req.user = user; // Attach the user object to the request for later use
next();
};
// Apply the authentication middleware globally for all routes
app.use(authenticate);
// Protected route
app.get('/protected', (req, res) => {
res.json({ message: 'You have access to the protected resource!', user: req.user });
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
In this updated example:
- We use `app.use(authenticate)` to apply the `authenticate` middleware globally for all routes. This means that every route defined after `app.use(authenticate)` will require authentication.
- The "/protected" route does not have the `authenticate` middleware applied directly to it. Instead, it inherits the authentication requirement from the global middleware.
Now, all routes are protected by the authentication middleware, and you don't need to manually apply the middleware to each individual route.
### Route-level middleware
Route-level middleware in Express.js allows you to apply middleware functions to specific routes rather than applying them globally to all routes. This gives you more fine-grained control over which routes have specific middleware functions applied to them. Here's an example of how to use route-level middleware:
```javascript
const express = require('express');
const app = express();
const port = 3000;
// Sample user data (in-memory storage)
const users = [
{ id: 1, username: 'user1', password: 'password1' },
{ id: 2, username: 'user2', password: 'password2' },
];
// Middleware for basic authentication
const authenticate = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ message: 'Unauthorized' });
}
const [authType, authCredentials] = authHeader.split(' ');
if (authType !== 'Basic') {
return res.status(401).json({ message: 'Unauthorized' });
}
const credentials = Buffer.from(authCredentials, 'base64').toString('utf-8');
const [username, password] = credentials.split(':');
const user = users.find((u) => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: 'Unauthorized' });
}
req.user = user; // Attach the user object to the request for later use
next();
};
// Apply the authentication middleware to a specific route
app.get('/protected', authenticate, (req, res) => {
res.json({ message: 'You have access to the protected resource!', user: req.user });
});
// Another route without authentication middleware
app.get('/public', (req, res) => {
res.json({ message: 'This is a public resource.' });
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
In this example:
- We define the `authenticate` middleware as before.
- We apply the `authenticate` middleware to the "/protected" route by including it as a second argument after the route path when defining the route.
- The "/public" route does not have the `authenticate` middleware applied, so it does not require authentication.
With this setup, the "/protected" route requires authentication because the `authenticate` middleware is applied to it. The "/public" route, on the other hand, does not require authentication and is accessible without any additional middleware. Route-level middleware allows you to customize the middleware applied to each route in your Express.js application.
### Built-in Middleware
Sure, let me explain these three commonly used built-in middleware functions in Express:
1. **express.static(root, [options])**:
- `express.static` is a middleware function that serves static files such as HTML, CSS, JavaScript, images, and more.
- It is often used to serve client-side assets, making it easy to host static files like HTML, CSS, and JavaScript for your web application.
- The `root` parameter specifies the root directory from which to serve static files.
- The optional `options` object allows you to configure various settings, such as caching and file handling.
Example:
```javascript
const express = require('express');
const app = express();
// Serve static files from the 'public' directory
app.use(express.static('public'));
```
In this example, if you have an "index.html" file in the "public" directory, you can access it in your browser by navigating to `http://localhost:3000/index.html`.
#### Static Hosting
Static hosting, also known as static web hosting or static file hosting, refers to the process of serving static files, such as HTML, CSS, JavaScript, images, and other assets, over the internet using a web server. Static files are files that do not change dynamically based on user interactions or database queries.
Static hosting is commonly used for websites, single-page applications (SPAs), documentation sites, and other web-based content that does not require server-side processing.
2. **express.json([options])**:
- `express.json` is a middleware function that parses incoming JSON requests and makes the parsed data available in the `req.body` property.
3. **express.urlencoded([options])**:
- `express.urlencoded` is a middleware function that parses incoming URL-encoded data from forms and makes it available in the `req.body` property.
These built-in middlewares in Express simplify common tasks like serving static files and parsing request data, making it easier to handle different types of requests in your web application or API.
### Route Parameters and Query Parameters
In web development, route parameters and query parameters are mechanisms for passing data to a web server or an application, typically through a URL. They are commonly used to customize and control the behavior of a web application by providing information about the requested resource or specifying additional options.
**Route Parameters**:
Route parameters are part of the URL's path and are used to define variable parts of a route. They are typically denoted by placeholders in the route pattern, surrounded by colons (`:`). When a client makes a request with a URL that matches the route pattern, the values specified in the URL are extracted and made available to the server or application.
For example, in a RESTful API, you might have a route for retrieving a specific user's profile:
```
GET /users/:userId
```
In this URL, `:userId` is a route parameter. When a request is made to `/users/123`, the server can extract the value `123` from the URL and use it to retrieve the user with that ID.
**Query Parameters**:
Query parameters are part of the URL's query string and are used to provide additional information or data to the server. They are typically specified after the `?` character in the URL and are in the form of key-value pairs.
For example, in a search feature, you might have a URL that includes query parameters to filter results:
```
GET /search?q=keyword&page=2&sort=desc
```
In this URL, `q`, `page`, and `sort` are query parameters. They allow the server to understand the search query, the desired page, and the sorting order.
In summary, route parameters and query parameters are essential for building dynamic web applications and APIs. They allow you to customize the behavior of your routes and pass data between clients and servers effectively. Route parameters are part of the URL's path and are extracted using placeholders in the route pattern, while query parameters are part of the URL's query string and are provided as key-value pairs after the `?` character. Express.js simplifies the handling of both types of parameters in your server-side code.

View File

@@ -0,0 +1,284 @@
### Introduction
Representational State Transfer (REST) constitutes an architectural paradigm that prescribes a set of guidelines for the construction of web services. A RESTful API provides a straightforward and adaptable means to interact with web services, devoid of any intricate processing requirements.
### Working
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/541/original/Screenshot_2023-09-20_184131.png?1695215654)
A client communicates with a server by issuing an HTTP request using a web URL, which can take the form of HTTP GET, POST, PUT, or DELETE requests. In return, the server provides a response in the form of a resource, which can take various formats such as HTML, XML, images, or JSON. Currently, JSON is the prevalent and widely used format in the realm of Web Services.
#### Code
```javascript!
const express = require('express')
const fs = require('fs')
const data = fs.readFileSync('data.json')
console.log('This is JSON data -> ' + data)
const app = express()
app.listen(8080, ()=>{
console.log('server started')
})
```
### Introduction
In HTTP there are five methods that are commonly used in a REST-based Architecture:
* GET
* POST
* PUT
* PATCH
* DELETE
We'll revisit these later in this script.
**Code:**
```javascript!
const express = require('express')
const app = express()
const fs = require('fs')
const data = JSON.parse(fs.readFileSync('data.json', "utf-8"))
const products = data.products
app.use(express.json())
console.log('This is JSON data -> ' + data)
//HTTP methods
app.get('/products', (req, res)=>{
res.send(products)
})
//we can pass something in id, find that id in json and find it's properties
//get
app.listen(8080, ()=>{
console.log('server started')
})
```
**Explanation:**
### GET() method
`GET()` is used to request data from a specified resource, such as a web page, an API endpoint, or any other resource accessible via a URL. It is primarily used for read-only operations, where we want to retrieve information without modifying the resource.
When a `GET()` request is made, it fetches data from the server without causing any changes to the resource. The data is then returned in the response, and the resource then remains unchanged on the server.
**Code:**
```javascript!
const express = require('express')
const app = express()
const fs = require('fs')
const data = JSON.parse(fs.readFileSync('data.json', "utf-8"))
const products = data.products
app.use(express.json())
console.log('This is JSON data -> ' + data)
//HTTP methods
app.get('/products', (req, res)=>{
res.send(products)
})
app.get('/products/:id', (req, res) =>{
const id = req.params.id
console.log(id)
const product = products.find(p=>p.id === id)
console.log(product)
})
//we can pass something in id, find that id in json and find it's properties
//get
app.listen(8080, ()=>{
console.log('server started')
})
```
**Output**
```
undefined
```
**Explanation:**
**Now why did we get output as undefined?**
Whenever you pass some thing to your id it comes in string format as opposed to when to pass it as `console.log(2)` it goes as arugment in number format
Let's rectify this and see how it works
**Rectified Code:**
```javascript!
const express = require('express')
const app = express()
const fs = require('fs')
const data = JSON.parse(fs.readFileSync('data.json', "utf-8"))
const products = data.products
app.use(express.json())
console.log('This is JSON data -> ' + data)
//HTTP methods
app.get('/products', (req, res)=>{
res.send(products)
})
app.get('/products/:id', (req, res) =>{
const id = Number(req.params.id)
console.log(id)
const product = products.find(p=>p.id === id)
console.log(product)
})
//we can pass something in id, find that id in json and find it's properties
//get
app.listen(8080, ()=>{
console.log('server started')
})
```
**Output**
```
Server Started
2
{
id: 2,
title: 'iPhoneX',
Description: 'Sim Free Model A1981'
price: 899,
discount Percentage: 17.94
rating: 4.44,
stocks: 34,
brand: 'Apple',
category: 'smartphones',
...
}
```
**Note:**
If you want to structure the output in JSON format then instead of `console.log(product)` you can do `res.json(product)`
### POST() method
`POST()` method is used to submit data to a specified resource, often to create a new resource on the server or to trigger a specific action that involves data submission.
When a `POST()` request is made, the data us sent to the server, and the server processes this data according to the resource's endpoint or the API's defined behavior. A new resource is created, if it already exists it updates it or triggers some other action.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/543/original/Screenshot_2023-09-20_184458.png?1695215705)
**Code:**
```javascript!
..same code
//post method
app.post('/products', (req, res)=>{
console.log(req.body)
products.push(req.body)
res.status(201).json(req.body)
})
```
### PUT() method
The `PUT()` method is an HTTP request method used to update a resource or create a new resource at a specific URI (Uniform Resource Identifier)
**Code:**
```javascript!
app.put('/products/:id', (req, res)=>{
const id = Number(req.params.id)
const productIndex = products.findIndex(p=>p.id===id)
products.splice(productIndex, 1, {...req.body, id:id})
res.status(201).json()
})
//talk about splice in terms of array while explaining
```
### PATCH() method
`PATCH()` is used to request partial modifications or updates to an existing resource.
When a `PATCH()` request is made, it typically sends a patch document or set of instructions to the server, indicating how the resource should be modified. The server processes these instructions and applies the specified changes to the resource
**Code:**
```javascript!
app.patch('/products/:id', (req, res)=>{
const id = Number(req.params.id)
const productIndex = products.findIndex(p=>p.id===id)
const product = products(productIndex)
products.splice(productIndex, 1, {...product, ...req.body})
res.status(201).json()
})
```
### DELETE() Method
`DELETE()` is used to request the removal or deletion of a resource located at a specific URI.
When a DELETE() request is made, it instructs the server to delete the resource identified by the URI. After a successful DELETE() operation, the resource no longer exists at that URI.
**Code:**
```javascript!
app.delete('/products/:id', (req, res)=>{
const id = Number(req.params.id)
const productIndex = products.findIndex(p=>p.id===id)
const product = products(productIndex)
products.splice(productIndex, 1)
res.status(201).json(product)
})
```
### Introduction to MongoDB
MongoDB, an open-source document-oriented database, is purpose-built for efficiently handling extensive datasets. It falls within the NoSQL (Not only SQL) database category due to its departure from the conventional table-based storage and retrieval of data.
### MongoDB Architecture
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/544/original/Screenshot_2023-09-20_184157.png?1695215721)
Now we know that MongoDB serves as a database server where data is stored. In other words, MongoDB provides an environment where you can initiate a server and subsequently create multiple databases within it.
Data in MongoDB is organized into collections and documents. This establishes a hierarchical relationship between databases, collections, and documents, which can be explained as follows:
**NOTE:** MongoDB's server supports the concurrent operation of multiple databases.
#### Analogy
Let's understand the MongoDB architecture with the help of an analogy
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/545/original/Screenshot_2023-09-20_184204.png?1695215735)
Let's consider a Mongoose application connected to a database called "Scaler." Within this database, there are three collection (courses, instructors, and students), and each collection corresponds to a multiple documents each representing data stored in diverse formats.
#### Install MongoDB on your system
You can install MongoDB on your system by typing the following command in your command terminal
```
npm install mongodb
```
#### Adaptability of MongoDB
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/049/546/original/Screenshot_2023-09-20_184211.png?1695215762)
Mongoose is specifically designed for Node.js, and so it's a natural choice for developers working with JavaScript on the server-side. This compatibility ensures seamless integration with Node.js applications.
#### Connect to MongoDB
**Code:**
```javascript!
const mongoose = require('mongoose')
const DB = "API"
mongoose.connect(DB, {
useNewUrlParser : true,
useUnifiedTopology : true,
}).then(()=>{
console.log('connection sucessful')
}).catch((err)=>{
console.log(error)
})
```
Run your file with:
```
npx nodemon test.js
```
**Explanation:**
Once Mongoose library is imported, a constant variable `DB` is defined with the value of API which represents the name or URL of the MongoDB database we want to connect to
A connection is then initiated with the MongoDB database using the `mongoose.connect` method and options object is passed with the `useNewUrlParser` (enables the use of the new URL parser for MongoDB) and `useUnifiedTopology` (enables the use of the new Server Discovery and Monitoring engine in MongoDB) options set to `true`. These options configures the connection.
Once the connection is successful, a message is then logged on the console. If however an error is encountered during the connection to database process the error message is logged onto the console
In this way this code sets up a connection to a MongoDB database named "API" using Mongoose

View File

@@ -0,0 +1,255 @@
## Today's Content
- MVC Architecture
- CRUD operation using MongoDB and Mongoose
# MVC Architecture Overview
Imagine we're using the following analogy to explain the MVC architecture:
"A customer (view) is ordering a pizza, so he makes a request to the waiter (controller). The waiter takes the request and goes to the chef (model) in the kitchen and fetches the items from the kitchen (database) to make the pizza. Once it's ready, the chef serves the pizza back to the waiter, who then serves it to the customer."
Now, let's break down the MVC architecture within this analogy.
**Model:**
- Represents the data and logic of the application.
- In the pizza example, the chef in the kitchen is the model.
- Manages and fetches data (ingredients) and performs operations (cooking) on it.
- The model is unaware of the user interface.
**View:**
- Represents the user interface or what the user interacts with.
- In the pizza example, the customer is the view.
- Displays information (menu options) to the user and captures user input (order).
- Passes user input to the controller.
**Controller:**
- Acts as an intermediary between the model and the view.
- In the pizza example, the waiter is the controller.
- Receives and processes user requests (orders) from the view.
- Interacts with the model (chef) to fetch data (ingredients) and perform actions (cooking).
- Sends updates back to the view to display the result (serving the pizza).
**The following image gives an idea about MVC architecture**:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/052/060/original/upload_24f17def1d1ac7e73c3a74069cf7b9f8.png?1696316119)
In essence, MVC separates the application into three distinct components, making it easier to manage and maintain. The view handles the presentation and user interaction, the model manages the data and logic, and the controller orchestrates the communication between the view and the model. This separation of concerns enhances code organization and promotes scalability and maintainability in software development.
# Question
What is the primary responsibility of the Model component in the MVC architecture?
# Choices
- [ ] Handles user interface interactions.
- [ ] Manages routing and URL handling.
- [x] Represents the data and logic of the application.
- [ ] Interacts with the user and captures input.
# Benifits of MVC architecture
- **Separation of Concerns:**
- Divides the application into Model, View, and Controller for clear separation of responsibilities.
- **Modular Development:**
- Supports development and maintenance of separate, reusable modules for each component.
- **Improved Code Reusability:**
- Allows reuse of Models, Views, and Controllers in different parts of the application or other projects.
- **Enhanced Maintainability:**
- Changes in one component have minimal impact on the others, simplifying maintenance and debugging.
- **Scalability:**
- Facilitates parallel development and the addition of new features without major rework.
- **User Interface Flexibility:**
- Adapts to various user interfaces while keeping the core logic intact.
- **Efficient Testing and Debugging:**
- Enables isolated unit testing for each component, easing issue identification and resolution.
- **Parallel Development:**
- Supports multiple developers or teams working on different components simultaneously.
- **Support for Multiple Views:**
- Utilizes the same Model and Controller with multiple Views for diverse user interfaces.
- **Long-Term Maintainability:**
- Promotes organized and understandable code, reducing technical debt over time.
# Question
Which of the following is NOT a benefit of using the MVC architecture in software development?
# Choices
- [ ] Enhanced Maintainability
- [ ] Efficient Testing and Debugging
- [ ] Improved Code Reusability
- [x] Tight Coupling between Components
# Mvc Implementation in Project
### index.js Usage
- **Responsibility**: The `index.js` file serves as the entry point of your application, handling server setup and routing initialization.
- **Server Setup**: It initializes an Express server using the `express` library.
- **Routing Configuration**: The `productRouter` is imported from `productRoutes.js` and configured to handle incoming requests.
- **Middleware**: Express middleware, such as `express.json()`, is applied to handle JSON request bodies.
- **Server Start**: The server is started on port 8080, listening for incoming requests.
```javascript
// index.js
const express = require('express')
const app = express()
// Import the productRouter for handling routes
const productRouter = require('./routes/productsRoutes')
// Use the productRouter middleware to handle routes
app.use('/', productRouter.router)
// Apply middleware to handle JSON request bodies
app.use(express.json())
// Start the server, listening on port 8080
app.listen(8080, () => {
console.log('Server Started')
})
```
**In this section of code**:
- `express` is used to create and configure the server.
- The `productRouter` is used to define and manage the routes, connecting them to the Controller component.
- Express middleware is applied, such as `express.json()`, which parses incoming JSON request bodies.
- The server is started and listens on port 8080, making your application accessible to clients.
This file essentially serves as the glue that ties together the MVC components, initializing the server, configuring routes, and ensuring proper request handling and response generation.
### Model Implementation
- **Responsibility**:<br> Manages data, performs database interactions, and defines data structure.
- **Data Definition**:<br> The Model defines the structure of core entities (courses) in your application.
- **Database Interaction**:<br> It handles database connections and operations using Mongoose.
- **Entity Schema**:<br> The model includes a schema definition for the 'Course' entity, specifying fields like name, creator, published date, isPublished, and rating.
- **Entity Model**:<br> The 'Course' model is created using Mongoose to represent the data structure.
**Code**:
```javascript
// myDb.js
// ... (previous code)
// Schema definition for the 'Course' model
const courseSchema = new mongoose.Schema({
name: String,
creator: String,
publishedDate: { type: Date, default: Date.now },
isPublished: Boolean,
rating: Number
})
// Model creation for the 'Course' entity
const Course = mongoose.model('Course', courseSchema)
// Functions for creating and interacting with 'Course' documents
// (createCourse, getCourse, updateCourse, deleteCourse)
```
#### Code Explanation:
- `mongoose.connect(DB, { useNewUrlParser: true, useUnifiedTopology: true })`: Establishes a connection to the database using the provided MongoDB connection string.
- `courseSchema`: Defines the structure of the 'Course' entity with its fields and data types.
- `Course = mongoose.model('Course', courseSchema)`: Creates a model named 'Course' based on the defined schema, allowing you to interact with the database using this model.
- Functions like `createCourse`, `getCourse`, `updateCourse`, and `deleteCourse` are used for various operations on 'Course' documents in the database.
### Controller Implementation
- **Responsibility**:<br> Acts as an intermediary between the Model and the View (routes), handling incoming requests and preparing responses.
- **Data Processing**:<br> Controller functions process various actions such as retrieving data, creating, updating, and deleting data.
- **Data Interaction**:<br> These functions interact with the Model to perform data operations.
- **Business Logic**:<br> The Controller contains business logic for processing and validating data.
- **Response Preparation**:<br> It prepares and sends responses back to the View (routes) based on the request handling.
**Code**:
```javascript
// productControllers.js
const fs = require('fs')
// Data retrieval from JSON file
const data = JSON.parse(fs.readFileSync('data.json', "utf-8"))
const products = data.products
// Controller functions for handling different actions
// (getAllProducts, getProduct, createProduct, replaceProduct, updateProduct, deleteProduct)
```
#### Code Explanation:
- The Controller functions, such as `getAllProducts`, `getProduct`, `createProduct`, etc., are responsible for handling specific HTTP requests related to products.
- These functions perform data processing, including reading from a JSON file and manipulating product data.
- Business logic, such as finding products by ID or updating product information, is contained within these functions.
- Responses are prepared and sent back to the View (routes) with appropriate HTTP status codes and data.
# Question
In a typical MVC implementation, what is the role of the Controller component?
# Choices
- [ ] Manages the data and logic of the application.
- [ ] Represents the user interface and displays information.
- [ ] Handles routing and URL configuration.
- [x] Acts as an intermediary between the Model and the View, processing requests and preparing responses.
### View (Routes) Implementation
- **Responsibility**:<br> In this project, the View component is represented by routes, which define endpoints for user interactions.
- **Request Handling**:<br> Routes receive incoming HTTP requests from users.
- **Controller Interaction**:<br> They invoke Controller functions to process requests and provide data.
- **Response Generation**:<br> Based on the Controller's response, routes generate and send appropriate HTTP responses.
- **User Interaction**:<br> Routes facilitate user interactions with the application through defined endpoints.
**Code**:
```javascript
// productRoutes.js
const express = require('express')
const productController = require('../controllers/productControllers')
const router = express.Router()
// Routes for different user interactions
router.get('/products', productController.getAllProducts)
router.get('/products/:id', productController.getProduct)
router.post('/products', productController.createProduct)
router.put('/products/:id', productController.replaceProduct)
router.patch('/products/:id', productController.updateProduct)
router.delete('/products/:id', productController.deleteProduct)
```
#### Code Explanation:
- Express routes, defined in `productRoutes.js`, specify various endpoints for user interactions related to products.
- These routes handle HTTP requests, such as GET, POST, PUT, PATCH, and DELETE, for product-related actions.
- The routes interact with the Controller component by invoking the corresponding Controller functions, passing request data as needed.
- Responses generated by the Controller are returned to the client through these routes.
In summary, this project demonstrates the separation of concerns through the Model-View-Controller pattern, with distinct roles for the Model (data management), Controller (business logic), and View (routes for user interaction). The bullet-pointed code explanations provide a detailed breakdown of each component's responsibilities and how they interact within your application.

View File

@@ -0,0 +1,638 @@
## Agenda
The agenda for today's session includes implementing authentication and authorization, exploring protected routes for both users and administrators, discussing BookMyShow integration, covering payment processing with Stripe, and generating tickets. Toward the end, we will focus on deployment. Our initial focus for today will be on the authentication and authorization aspects.
---
title: Project Setup and Adding Frontend Code for Registration
description: In this segment, we'll set up React, Express, Node.js, and a MongoDB database, and we'll also write frontend code for user registration.
duration: 2700
card_type: cue_card
---
## Project Setup and Adding Frontend Code for Registration
Before we move into authentication and authorization, it's essential to set up the project infrastructure. We'll start by configuring React, Express, Node.js, and MongoDB. These components must be installed and configured correctly to proceed.
To begin, let's create a folder named "bookMyShow-project." Inside this folder, create another one called "client," where we'll develop the frontend of our application. To kickstart the React app, use the following command:
```cpp
npm create-react-app
```
Additionally, create a folder named "server" in the main project directory. The "client" folder will house all the frontend code, while the "server" folder will be dedicated to setting up our Express backend. With the React app initialized, let's navigate to the "App.js" file to continue.
#### Pseudocode
```cpp
function App() {
return (
<div>
<h1>
Hello
</h1>
</div>
)
}
export default App;
```
Now, let's initiate our client application by running the command npm start.
> Note to instructor - Take a moment to show the local host and see how our app looks.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/324/original/upload_c3cacc7db150560f524aa121bf3cb867.png?1695968640)
With our React app successfully set up, it's time to create two fundamental pages: the login and registration pages. These pages are essential components of any application.
Our initial set of pages includes:
1. Home Page
2. Register Page
3. Login Page
To expedite the development process, we will leverage Ant Design, a library that provides pre-built components, eliminating the need for extensive custom CSS. Within the component section, you'll find various components, each with an example code.
Since our focus is on building the login and registration pages, we'll primarily work with forms. So, let's navigate to the component section, search for "form," and explore multiple form examples. You can choose a form that suits your needs and copy the code from there.
In our client directory, let's begin by installing the required dependencies:
```javascript
npm install react-router-dom antd axios
```
Now, let's structure our project by creating a "pages" folder. Inside this folder, create three subfolders: "login," "register," and "Home." Within each of these subfolders, create an "index.js" file.
Here's the structure for "index.js" within the "Home" folder:
#### Pseudocode
```javascript
const Home = () => {
return (
<div>
This is my home page
</div>
)
}
export default Home;
```
Similarly, for the "index.js" file in the "login" folder:
#### Pseudocode
```javascript
const Login = () => {
return (
<div>
This is my Login page
</div>
)
}
export default Login;
```
And for the "index.js" file in the "register" folder:
#### Pseudocode
```javascript
const Register = () => {
return (
<div>
This is my Register page
</div>
)
}
export default Register;
```
We will navigate to the 'App.js' file import all of these components set up React Router DOM and create our first route
#### Pseudocode
```javascript
import Home from "./pages/Home";
import Login from "./pages/Login";
import Register from "./pages/Register";
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<div>
<BrowserRouter>
<Routes>
<Route path = '/' element = {<Home />} />
<Route path = '/login' element = {<Login />} />
<Route path = '/register' element = {<Register />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
```
> Note to instructor - Test the functionality of our home page
Within our 'client' folder, we'll create a stylesheet that we'll use in our components. We already have five CSS files prepared:
* '`alignment.css`' contains alignment-related code for flex, margin, justify, padding, and more, so we won't need to worry about these aspects.
* `'custom.css'` is available for customization if you want to tailor the appearance of your application.
* `'form-elements.css'` provides styles for form elements.
* `'sizes.css'` is for adjusting various sizes.
* `'theme.js'` contains definitions for themes, text styles, fonts, and colors that we'll be using.
Now, let's turn our attention to the registration page. Here's what we need for this page:
* **Three fields:** name, email, and password.
* A 'Register' button that users can click to complete the registration process.
We'll use the form components provided by Ant Design to facilitate this."
#### Pseudocode
```javascript
import React from 'react';
import { Form } from 'antd';
const Register = () => {
return (
<div>
<Form>
<Form.Item label = 'Name' name = 'name' rules = {[{ required: true, message: "Please enter your name" }]}>
<input type = 'text' />
</Form.Item>
</Form>
</div>
);
}
export default Register;
```
Now, let's proceed to include the remaining fields.
#### Pseudocode
```htmlembedded
import React from 'react';
import { Form } from 'antd';
const Register = () => {
return (
<div>
<Form>
<Form.Item label = 'Name' name='name' rules = {[{ required: true, message: "Please enter your name" }]}>
<input type = 'text' />
</Form.Item>
<Form.Item label = 'Email' name = 'Email' rules = {[{ required: true, message: "Please enter your email" }]}>
<input type = 'text' />
</Form.Item>
<Form.Item label = 'Password' name = 'Password' rules = {[{ required: true, message: "Please enter your password" }]}>
<input type = 'text' />
</Form.Item>
</Form>
</div>
);
}
export default Register;
```
Now, let's make sure to include all our stylesheets in 'App.js' for consistency
#### Pseudocode
```javascript
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import Login from './pages/Login';
import Register from './pages/Register';
import './stylesheets/alignment.css';
import './stylesheets/sizes.css';
import './stylesheets/form-elements.css';
import './stylesheets/theme.css';
import './stylesheets/Custom.css';
function App() {
return (
<div>
<BrowserRouter>
<Routes>
<Route path = "/" element = {<Home />} />
<Route path = "/login" element = {<Login />} />
<Route path = "/register" element = {<Register />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
```
We've already added the necessary styling, and to save time, we'll directly integrate our form code into the 'index.js' file of the 'Register' component. We're removing the previously written code and replacing it with Ant Design components to help you understand how to use them effectively.
But there's one element missing - a button. To address this, let's create a 'components' folder. Inside this folder, we'll create a 'buttons' subfolder where we'll add the button code. Then, we'll import the button component into our 'Register' page."
#### Pseudocode
```javascript
import React, { useEffect } from 'react';
import Button from '../../components/Button';
import { Link, useNavigate } from 'react-router-dom';
import { Form, message } from 'antd';
import { RegisterUser } from '../../apicalls/users';
const Register = () => {
const navigate = useNavigate();
const onFinish = async (values) => {
try {
const response = await RegisterUser(values);
if (response.success) {
message.success(response.message);
console.log(response.message);
} else {
message.error(response.message);
console.error(response.message);
}
} catch (error) {
message.error(error);
}
};
useEffect(() => {
if (localStorage.getItem('token')) {
navigate('/');
}
}, []);
return (
<div className = "flex justify-center h-screen items-center bg-primary">
<div className = "card p - 3 w - 400">
<h1 className = "text - xl mb - 1">Welcome to Scaler Shows! Please Register</h1>
<hr />
<Form layout = "vertical" className = "mt - 1" onFinish = {onFinish}>
<Form.Item
label = "Name"
name = "name"
rules = {[{ required: true, message: 'Please input your name!' }]}
>
<input type = "text" />
</Form.Item>
<Form.Item
label = "Email"
name = "email"
rules = {[{ required: true, message: 'Please input your email!' }]}
>
<input type = "email" />
</Form.Item>
<Form.Item
label = "Password"
name = "password"
rules = {[{ required: true, message: 'Please input your password!' }]}
>
<input type = "password" />
</Form.Item>
<div className = "flex flex - col mt - 2 gap - 1">
<Button fullWidth title = "REGISTER" type = "submit" />
<Link to = "/login" className = "text-primary">
{' '}
Already have an account? Login
</Link>
</div>
</Form>
</div>
</div>
);
};
export default Register;
```
## Implementation of the Login Page
Next, we will craft the login page, following a similar approach. We'll modify the button text to 'Login' and eliminate the 'name' field from the login page's form.
#### Pseudocode
```javascript
import React , {useEffect} from 'react'
import {Form, message} from "antd";
import Button from "../../components/Button";
import { Link , useNavigate } from "react-router-dom";
import { LoginUser } from '../../apicalls/users';
const Login = () => {
const navigate = useNavigate()
const onFinish = async(values) => {
try {
const response = await LoginUser(values)
if(response.success){
message.success(response.message)
localStorage.setItem('token' , response.data)
window.location.href = "/";
}
else{
message.error(response.message)
}
} catch (error) {
message.error(error.message)
}
}
useEffect(() => {
if (localStorage.getItem("token")) {
navigate("/");
}
}, []);
return (
<div className = "flex justify-center h-screen items-center bg-primary">
<div className = "card p - 3 w - 400">
<h1 className = "text-xl mb-1">Welcome Again! Please Login</h1>
<hr />
<Form layout = "vertical" className = "mt - 1" onFinish = {onFinish}>
<Form.Item
label = "Email"
name = "email"
rules = {[{ required: true, message: "Please input your email!" }]}
>
<input type = "email" />
</Form.Item>
<Form.Item
label = "Password"
name = "password"
rules = {[{ required: true, message: "Please input your password!" }]}
>
<input type = "password" />
</Form.Item>
<div className = "flex flex - col mt - 2 gap - 1">
<Button fullWidth title = "LOGIN" type = "submit" />
<Link to = "/register" className = "text-primary">
{" "}
Don't have an account? Register
</Link>
</div>
</Form>
</div>
</div>
)
}
export default Login
```
## Establish Server-Side Architecture
Next, we'll move into the server-side operations. Within our server directory, we'll create a file named 'server.js' and proceed to install the Express framework.
To enhance security, we'll make use of 'bcryptjs', a package specialized in password hashing. The necessary packages can be installed with the following command:
```javascript
npm install express mongoose bcryptjs jsonwebtoken
```
While the client side encompasses all frontend code, the server side will house the backend logic. In 'server.js', we'll initiate the Express server. This step forms the foundation for our backend operations.
#### Pseudocode
```javascript
const express = require('express');
const app = express();
app.listen(8082, () => {
console.log('Server is Running');
});
```
In the code snippet provided, we have the 'server.js' file for our Express application. To start the server, you can use 'npx nodemon server.js.' This command ensures that your server automatically restarts upon code changes, streamlining the development process.
As part of our server setup, we'll create an environment configuration ('`.env`') file where you can store essential information such as the MongoDB URL and `JSON Web Token (JWT)` secrets. This allows us to keep sensitive data secure.
Furthermore, we'll establish a 'dbconfig.js' file in the 'config' folder. This file will contain the configuration settings for connecting to our MongoDB database.
#### Pseudocode
```javascript
const mongoose = require('mongoose')
mongoose.connect(process.env.mongo_url)
const connection = mongoose.connection
connection.on('connected' , () => {
console.log('Connection Succesful')
})
```
To bring everything together, the revised 'server.js' code snippet includes the necessary 'require' statements, environment variable loading, and the inclusion of the 'dbConfig' module. This ensures that our server is properly configured and ready to run on port 8082."
#### Pseudocode
```javascript
const express = require('express');
const app = express();
require('dotenv').config(); // Load environment variables
const dbConfig = require('./config/dbConfig'); // Import database configuration
app.listen(8082, () => {
console.log('Server is Running');
});
```
## Exploring Authentication
Authentication is a fundamental aspect of our application. We'll define the schema and create a model based on that schema. For user registration, our schema will encompass essential fields like name, password, and email.
To structure our project effectively, we'll establish a 'model' folder. Our initial model, 'userModel.js,' will commence with defining a schema. With this schema in place, we'll proceed to create a model, enabling the creation of various user documents. Mongoose will play a pivotal role in defining our schema.
#### Pseudocode
```javascript
// userModel.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
isAdmin: {
type: Boolean,
required: true,
default: false
}
});
module.exports = mongoose.model('users', userSchema);
```
Additionally, we'll implement email validation and introduce an 'isAdmin' property to manage admin-specific permissions for select users.
Routing is another crucial aspect. We'll create user routes within our 'routes' folder.
#### Pseudocode
```javascript
// userRoutes.js
const router = require('express').Router();
const User = require('../models/userModel');
// Register a user
router.post('/register', async (req, res) => {
try {
const userExists = await User.findOne({ email: req.body.email });
if (userExists) {
return res.send({
success: false,
message: 'User Already Exists'
});
}
const newUser = new User(req.body);
await newUser.save();
res.send({ success: true, message: 'Registration Successful, Please login' });
} catch (error) {
console.log(error);
}
});
module.exports = router;
```
In the 'server.js' file, we will establish a route for user-related operations:
#### Pseudocode
```javascript
const express = require('express');
const app = express();
require('dotenv').config();
const dbConfig = require('./config/dbConfig');
const userRoute = require('./routes/userRoute');
app.use(express.json());
app.use('/api/users', userRoute);
app.listen(8082, () => {
console.log('Server is Running');
});
```
> Note to instructor - Test the functionality till this point
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/325/original/upload_0a1c4c3b0afa456fb137116cf0e88e23.png?1695969450)
## Investigating Hashing and Encryption
Hashing and encryption are both methods used to protect data, but they operate differently, and their purposes vary.
Encryption is a two-way process that involves converting data into a coded format (cipher) that can be easily reversed back to its original form (plaintext) using a decryption key. In encryption, there's always a corresponding decryption process, allowing data to be securely stored and transmitted while maintaining its reversibility.
Hashing, on the other hand, is a one-way process. It involves converting data, such as a password, into a fixed-length string of characters, often a hash value or digest. The key distinction is that there's no straightforward way to reverse this process and obtain the original data. If, for instance, a password like "abc" is hashed, it becomes something like "%B#82^&," and this transformation is irreversible.
One common library used for hashing in JavaScript is bcrypt. Now, let's implement bcrypt in the 'userRoute' code:
#### Pseudocode
```javascript
const router = require('express').Router();
const bcrypt = require('bcryptjs');
const User = require('../models/userModel');
// Register a user
router.post('/register', async (req, res) => {
try {
const userExists = await User.findOne({ email: req.body.email });
if (userExists) {
return res.send({
success: false,
message: 'User Already Exists'
});
}
// Hash the password
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(req.body.password, salt);
req.body.password = hashedPassword;
const newUser = new User(req.body);
await newUser.save();
res.send({ success: true, message: "Registration Successful, Please login" });
} catch (error) {
console.log(error);
}
});
module.exports = router;
```
In this code, we've integrated bcrypt to securely hash the user's password before storing it in the database. This enhances password security ensures that plaintext passwords are not stored.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/326/original/upload_ea5e3fdb5425c91bf4e666263f4ffa03.png?1695969502)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/327/original/upload_e13d594015ef8e048d6515afeecbd113.png?1695969539)

View File

@@ -0,0 +1,407 @@
# Full Stack LLD & Projects: FullStack-5: Project Part 2- (Creating User's & Admins Route)
## Agenda
* Revise the concepts taught in the previous class.
* Registration route creation
* Login route creation for the user
* JWT and Protected Route
## Login and Registration Route Creation
>Before proceeding, show a glimpse of the project done till now.
>Ask the students if they have any doubt uptil now.
Now that we have written the code for registration, we will be going ahead with the login process.
* Registration is a one-time process.
* Login- After entering the username and password, we only need to check for the credentials if the user exists in the database or not
>Ask the students which method should be used to create login route. Like for registration, we used the POST method, similarly, what should be used here?
The answer is, we will be using the POST method for login route creation. It is so, because we are sending the data to the backend and then matching it.
### Steps
**Step 1**- Create a post request with name `/login` and it will have a async callback.
**Step 2**- We will have the first check for email whether it is present in the database or not.
```javascript
const user = await User.findOne({email : req.body.email})
```
The above line is used to find the user email in the particular list of emails.
**Step 3**- If the user is not present, we will not allow the user to login and give an error message. We will set `success` as `false` and message as 'user does not exist'.
```javascript
if(!user){
return res.send({
success : false,
message : 'User does not exist'
})
}
```
**Step 4**- Now we will be checking for password. We will check if the entered password matches the password in the database. In Bcrypt, there are two methods to compare- `compare` and `compareSync`. As we are using `await`, we will be using `compare`. It will compare the entered password into hash form with the password in the database.
```javascript
const validPassword = await bcrypt.compare(req.body.password , user.password)
```
Here, the first parameter is the password we have entered and the second parameter is the password corresponding to the username.
**Step 5**- If the passwords do not match, we will display an error message.
```javascript
if(!validPassword){
return res.send({
success : false,
message : 'Invalid Password'
})
}
```
**Step 6**- If everything is successful, we will send a successful message that the user has been logged in successfully.
```javascript
res.send({
success : true,
message : 'User Logged in',
})
```
Now, let us test our code. The route in postman will be changed to `login`. We also do not require the `name` key in our data so we erase it and only keep the `email` and `password`. We send the data and we received 'success' message.
>Also show the message after entering an incorrect email and password.
Now, the data needs to be shown on the frontend. We need to link the client and the server. We will be splitting the terminal- zsh and open client and server in split view.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/343/original/upload_f3674562643f00dcbd6f06ce1e68219a.png?1695972822)
On the left is Client and on the right is Server. To run the server we will use the following commands one by one-
```javascript
cd bookmyshow-project
cd server
server.js
```
Now, we have to link our server and client. In our `client` folder, we will go under `package.json` and add the following code-
```javascript
"proxy": "http://localhost:8082"
```
The address is the server address which can be found from postman. Our client will now use the above address as proxy.
Frontend should know what data should be there, what data is coming, what data to validate. We will be creating a folder `apicalls` under `client` folder. We will be using **axios** to make API calls.
We will create a file called `index.js` under `apicalls` where we will set axios.
We will import axios and create axios instance.
```javascript
import axios from 'axios'
export const axiosInstance = axios.create({
headers : {
credentials : 'include',
method : 'post',
'Content-Type' : 'application/json'
}
})
```
The `create` method of axios is used to create an instance. We will add the above headers.
Now we will set up the route for frontend with file name `user.js`.
**Step 1**- We will require axios instance. As it is in the same folder, we will just use a `.`(dot)
**Step 2**- Register a new user. We will create a function`RegisterUser` which will take in the payload. it is an async function. Inside it we will use a try-catch function. A variable `response` is created and we will be using `axiosInstance` and the method is `post`. The route which we will hit on is `/register` right now. Then the response data will be returned.
```javascript
export const RegisterUser = async ()=>{
try {
const response = await axiosInstance.post('/register' , payload)
return response.data
} catch (error) {
return error
}
}
```
>Open `pages` folder and then `Register` folder and within it open `index.js`.
**Payload**
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/344/original/upload_9441d841911b694a4ceef8a4b3bc465f.png?1695972854)
Now in the above code, we can see that there are various fields and we have to enter some data in it. There is a button of `type="submit"` which sends the data which is entered by the user. That data is known as the **payload**.
In our `registerUser` function, our route is `/register`, and the method is `axios.post`. The proxy that we have set- `"proxy": "http://localhost:8082"` is the server's proxy. The axios instance that we have created will look at the server/ proxy that we have made and inside our proxy we have `register` which will set the data. Basically that user will be registered at the backend.
**Step 3**-
* Now, we need to get our payload out and send it to the `register` function. In our `index.js` file opened before, we will be making changes to it.
* We will be using the `onFinish` function by AntD. It is an AntD version of `onSubmit`.
* We will implement a `onFinish()` function. Whatever data we get, we will call it as `values`.
* We will also import the `registerUser` function.
```javascript
import { RegisterUser } from '../../apicalls/users'
```
* We will have a try-catch block. So, inside the try block, we will try to send values to the `registerUser` function as payload values.
* If it has been successfully submitted, there won't be any error and success message will be displayed. If it has an error, an error will be displayed.
```javascript
const Register = () => {
const onFinish= async (values)=>{
try {
const response = await RegisterUser(values)
if(response.success){
message.success(response.message)
console.log(response.message)
}
else{
message.error(response.message)
console.log(response.message)
}
} catch (error) {
message.error(error)
}
}
```
We will also add payload as a parameter to
```javascript
export const RegisterUser = async (payload) => {
try {
const response = await axiosInstance.post('/register' , payload)
return response.data
} catch (error) {
return error
}
}
```
We will also add the `onFinish` prop to the Form Component.
```javascript
<Form layout = "vertical" className = "mt - 1" onFinish = {onFinish}>
```
Now let us try to register our user on the webpage.
If the application runs successfully, you will see a success message. If there is any error, like CORS error, we need to fix it. Let's know more about it-
CORS error stands for Cross Origin Resource Sharing. These issues arise due to cross resource sharing. To prevent this, we have to install CORS first and add it in our index.js by using the following lines-
```javascript
var cors = require('cors')
app.use(cors())
```
### Login Route Creation
For login route, inside our users.js file, we will create a `LoginUser` function. You can change the `/login` route to `api/users/login` and similarly for registration and in our `server.js` where we had declared the routes.
```javascript
export const LoginUser = async (payload) => {
try {
const response = await axiosInstance.post('api/users/login' , payload)
return response.data
} catch (error) {
return error
}
}
```
In our `index.js` file of the Login folder, we will write our code. for login data submission to the backend.
**Login**
* If the user exists, then login successfully.
* As soon as we login, we will send the user to the home page. And we need the user to navigate from one page to another. For that we will first import hook `useNavigate` from React DOM. Modify the line to
```javascript
import { Link , useNavigate } from "react-router-dom";
```
We will add the location to the navigate function to where we want the current page.
```javascript
const Login = () => {
const navigate = useNavigate()
const onFinish = async(values) =>{
try {
const response = await LoginUser(values)
if(response.success){
message.success(response.message)
localStorage.setItem('token' , response.data)
window.location.href = "/";
}
else{
message.error(response.message)
}
} catch (error) {
message.error(error.message)
}
}
}
```
## JWT
### Introduction
* JWT stands for **JSON Web Token**.
* Tell the students that in most of the websites, we do not need to login again and again. In sites like FB, Instagram, when we login before, it does not ask us to login again whenever we open the app. It is possible because of **JWT**.
* JWT is important to get logged in for a particular session.
* Send [JWT](https://jwt.io/) token link to the students.
* JWT tokens are unique for each session.
### Generate Tokens
Let us try to generate a unique token for in our `userRoute.js` file for login.
We will first require the jwt package.
```javascript
const jwt = require('jsonwebtoken')
```
We will define a `token` variable and use the `sign` method that creates a token. We have to pass it an ID for it to generate a token.
In our `.env` file, we will define `jwt-secret` is a text that you create by yourself. It should not be exposed which might cause harm to the system.
In our case, we will assign it-
```javascript
jwt_secret = scaler-movies
```
The `expiresIn` function is used to mention for how much time we want the token to remain alive.
```javascript!
const token = jwt.sign({userId : user._id} , process.env.jwt_secret , {expiresIn :"1d"})
console.log(token)
````
Using the above token, a session will be available to us. A session is a time period. In the above example, we have used `1d` which means 1 day. The token is alive for 1 day and will expire after it.
We will also send data as token using the following-
```javascript
res.send({
success : true,
message : 'User Logged in',
data : token
})
```
In the Postman app, let us try to login and check whether we receive tokens or not.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/345/original/upload_737138f77bbf2405b8dd1a6bccbd797e.png?1695973009)
Now, paste the data part as selected above and let us go to the JWT website and paste it.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/346/original/upload_ccaf8a491276cc6f0702d41fa1e16f30.png?1695973028)
In the payload part, you can see the `userId` starts with `64` and ends with `56`. Now, when we check in database in postman, we can see the a user with same starting and ending id.
If you want to check for which particular user a token was generated, we can go to JWT website, paste the token and get the ID of that user.
In our frontend, we will be going to our `index.js` file and setting the token in our local storage.
```javascript
if(response.success){
message.success(response.message)
localStorage.setItem('token' , response.data)
window.location.href = "/";
}
```
The token saved as `response.data` will be saved as token in the local storage. That token will be saved for the time period set by us earlier.
Many times we see the message **session expired** that means the token saved in the local storage has gone.
we login again now. After logging in we can see under the Applications tab and under local storage the token being stored.
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/347/original/upload_86e590e42dbba2888f15677de6f5857f.png?1695973053)
>Ask the students if they have any idea about Protected Routes.
### Introduction
* Protected Route is a route that is not accessible publicly.
* Login and register page are public routes. But, if you want to see the home page, you will have to pass through the login and registration.
* Some of the resources like Paid courses are coded in such a way that they are under Protected Routes.
Open `userRoute.js` and create a protected route.
### Create a Protected Route
* We will be creating a route. Here we want to get the details so we will be using the `get` method.
* We do not need the password, so we will be using `-name of the property` hyphen along with the name of the property which we do not want.
* For the code to work, we will have to create a middleware. Inside our `server` folder, we will create a middlware. File called `authMiddleware.s` will be created.
```javascript
router.get('/get-current-user',authMiddleware, async (req , res) => {
try {
const user = await User.findById(req.body.userId).select('-password')
res.send({
success : true,
message : 'User details fetched Successfully',
data : user
})
} catch (error) {
res.send({
success: false,
message: error.message,
});
}
})
```
For the middleware, we will write the following code-
Inside the try block, we will use the authorization token. It will contain all the details how you are authorized.
Authorization token consists of two parts-
* Bearer
* JSON web token
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/051/348/original/upload_0a53611cf032de2e3578251e93142fac.png?1695973084)
* We want the JSON web token, so we will be using the 1st index.
* We will be using the `decoded` variable to check whether the `jwt_secret` matches the token generated. If it is generated by the secret, then that user has to be allowed.
```javascript
const jwt = require('jsonwebtoken')
module.exports = function(req , res , next){
try {
const token = req.headers.authorization.split(' ')[1]
const decoded = jwt.verify(token , process.env.jwt_secret)
req.body.userId = decoded.userId
next()
} catch (error) {
res.status(401).send({ success: false, message: "Invalid token" });
}
}
```
If the user is not verified, an error message is generated.
In our `index.js` file of `apicalls`, we will add the authorization by including-
```javascript
authorization : `Bearer ${localStorage.getItem('token')} `
```
So, when we hit the `/get-current-user` route, it will pass through the middleware `authMiddleware` and when the middleware lets it pass, we will get the user.

View File

@@ -0,0 +1,339 @@
## Protected Route
A protected route is a route that is not available publicly
**So how to access it?**
It can only be accessed after passing ones credentials only after which can one see the actual route.
## Token
**JWT (JSON Web):** Whenever a person logs in, a JWT token is generated which is totally unique and this token is then saved to a local storage of browser user is using.
### How does it work?
If the token is already present in the browser local storage system knows that user is logged in.
```javascript
const token = jwt.sign({userID: user_id}, process.env.jwt_secret, {expires})
```
### Properties Used:
Our objective is to retrieve the userID of the currently signed-in user. Following are the steps:
* We obtain the userID from the database where user information is stored.
* For this specific user, a JSON web token is generated.
* To achieve this, we utilize a **jwt_secret**, which is essentially a unique value responsible for generating these web tokens.
* Additionally, we employ a property that defines the token's lifespan, ensuring that after a user logs in, they will have access for only one day.
* Subsequently, once the token expires, the user will need to log in again.
```javascript
console.log(token)
res.send({
sucess: true,
message: "User logged in",
data: token
})
```
## Implementation of a protected route?
Once the token is generated and sent to the database which will then be used to implement a protected route only after which will a user be able to access the content of a particular route
```javascript
router.get('/get-current-user', authMiddleware, async (req, res) => {
try{
const user = await User.findById(req.body.userId).select('')
res.send({
sucess: true,
message: "User details fetched successfully"
data: user
})
}
catch(error){
res.send({
success: false,
message: error.message,
});
}
})
```
Here `/get-current-user` is a protected route,
`authMiddleware` checks if the user is authorized to acess this route by checking if the user is authenticated or not.
Only if the token is present in the database (generated prior by jwt_secret) only then we will allow the user to access the contents.
```javascript
module.exports = function(req, res, next){
try{
const token = req.headers.authorization.split('')[1]
const decoded = jwt.verify(token, process.env.jwt_secret)
req.body.userId = decoded.userId
next()
}
catch(error){
res.status(401).send({success: false, message: "Invalid"})
}
}
```
If a token is available, we first decode the user ID and then proceed to the next step.
Returning to our earlier code, we retrieve the userID and omit the password as it is unnecessary. Once we locate the user, we display a message indicating that they may proceed.
## Front-end
Now that the backend part is done we need to move onto the front end part of this
Let's see checks needed for login function
```javascript
import React from 'react'
const ProtectedRoute = ({childern}) => {
return {
<div></div>
}
}
export default ProtectedRoute
```
Here,
**childern (home page):** a component that I want to make it protected
### Redux
As we move further manging a state is diffcult which is why we'll be using redux
```javascript
npm install @reduxjs/toolkit
npm install react-redux
```
We're using redux here to basically use a loader whenever a user logs in
Three files can be created as following
```javascript
/redux
- loaderSlice.js
- store.js
- userSlice.js
```
**userSlice.js**
```javascript!
import {createSlice} from '@reduxjs/toolkit';
const loadersSlice = createSlice({
name: "loaders",
initialState: {
loading: false,
},
reducers: {
ShowLoading : (state) => {
state.loading = true;
},
HideLoading : (state) => {
state.loading = false;
}
}
});
export const {ShowLoading, HideLoading} = loaderSlice.actions;
export default loadersSlice.reducer;
```
**Store.js**
```javascript
import { configureStore } from "@reduxjs/toolkit";
import loadersReducer from "./loadersSlice";
import usersReducer from "./usersSlice";
const store = configureStore({
reducer: {
loaders: loadersReducer,
users: usersReducer,
},
});
export default store;
```
**loaderSlice.js**
```javascript
import {createSlice} from '@reduxjs/toolkit';
const loadersSlice = createSlice({
name: 'loaders',
initialState: {
loading : false,
},
reducers: {
ShowLoading : (state) => {
state.loading = true;
},
HideLoading : (state) => {
state.loading = false;
}
}
});
export const {ShowLoading, HideLoading} = loadersSlice.actions;
export default loadersSlice.reducer;
```
Our second step would be to show details if the existing user tries to login again, so the question here is how to get the details of a particular user
We can get user data from the following
```javascript
const { user } = useSelector{(state) => state.users}
```
Thus our final `protectedroute.js` is:
```javascript
import { message } from "antd";
import React, { useEffect} from "react";
import { GetCurrentUser } from "../apicalls/users";
import { useNavigate } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { SetUser } from "../redux/usersSlice";
import { HideLoading, ShowLoading } from "../redux/loadersSlice";
function ProtectedRoute({ children }) {
const { user } = useSelector((state) => state.users);
const navigate = useNavigate();
const dispatch = useDispatch();
const getpresentUser = async () => {
try {
dispatch(ShowLoading());
const response = await GetCurrentUser();
dispatch(HideLoading());
if (response.success) {
dispatch(SetUser(response.data));
} else {
dispatch(SetUser(null));
message.error(response.message);
localStorage.removeItem("token");
navigate("/login");
}
} catch (error) {
dispatch(HideLoading());
dispatch(SetUser(null));
message.error(error.message);
}
};
useEffect(() => {
if (localStorage.getItem("token")) {
getpresentUser();
} else {
navigate("/login");
}
}, []);
return (
user &&
(
<div className = "layout p-1">
<div className = "header bg-primary flex justify-between p-2">
<div>
<h1 className = "text-2xl text-white cursor-pointer"
// onClick={() => navigate("/")}
>Book My Show</h1>
</div>
<div className = "bg-white p-1 flex gap-1">
<i className = "ri-shield-user-line text-primary mt-1"></i>
<h1
className = "text-sm underline"
onClick = {() => {
if (user.isAdmin) {
navigate("/admin");
} else {
navigate("/profile");
}
}}
>
{user.name}
</h1>
<i
className = "ri-logout-box-r-line mt-1"
onClick = {() => {
localStorage.removeItem("token");
navigate("/login");
}}
></i>
</div>
</div>
<div className = "content mt-1 p-1">{children}</div>
</div>
)
);
}
export default ProtectedRoute;
```
## Summary so far
To sum up we have the following:
* `userRoute.js` file
* Get current user route
* Middleware
* **API calls**: In order to get the current user
* **Redux**: contains slices and store.js files
* `Protected route.js` file
## Login
In order to prevent the user from going back to login page after logging in we can do the following
```javascript
useEffect{() => {
if(localStorage.getItem("token")){
navigate("/");
}
}, []};
```
You can use CDN remix icon link in your html file to add stylish icons
## Users
We'll have two types of user
### Admin
1. **Add the movies:** By editing updates
2. **Add the theatres**
`isAdmin: true`
### User
1. login/logout
2. see the shows
3. book the tickets
4. generate ticket
5. make payment
`isAdmin: false`
`/admin` If this is present in the url of the app then check for certain permissions by checking if `isAdmin` property is *true*, if yes give the rights and if not then revert to
`/profile`: normal user rights.
## UI - Display
The finished UI should have the following:
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/687/original/upload_9010f428767216bb0f328d971b568460.png?1697915994)
![](https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/054/688/original/upload_a50a1c5e7351b37a4c5c3b8c469e34b5.png?1697916285)
### Display: Form elements
1. **Add button**: It places the text at respective places/variables
2. **Movie Route**: Adds movies using Instance.AddMovies
3. **Table** at UI