# CS230 preparation
**1. CSS Selectors and Specificity**
- CSS Selector Priority: Hierarchy - Inline styles > IDs > Classes, attributes, and pseudo-classes > Elements and pseudo-elements.
**CSS selector priority is the machanism that CSS use to determine which element will be selected.**
- Specificity Calculation: Count 1 for each element and pseudo-element, 10 for each class, attribute, and pseudo-class, 100 for each ID, and 1000 for inline styles.
- CSS Pseudo Classes: Used to define a special state of an element (e.g., `a:hover` for hovering state).
- CSS Pseudo Elements: Used to style a specific part of an element (e.g., `p::first-letter` styles the first letter).
- Inappropriateness of CSS Generated Content: Not accessible by screen readers, not selectable or copyable, doesn't change with user language settings.
**2. AJAX and JavaScript**
- AJAX Understanding: AJAX (Asynchronous JavaScript And XML) updates parts of web content without reloading the entire page. It sends and receives HTTP requests/responses in the background.
- Pure JavaScript AJAX: A GET request example `var xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.send();` A POST request example `var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify(data));`
- AJAX POST with jQuery: `$.ajax({ type: "POST", url: url, data: data, success: success, dataType: 'json', contentType : "application/x-www-form-urlencoded" });`
**3. AJAX for Dynamic Web Page Generation**
- Advantages of AJAX:
- the page doesn't have to be reloaded so the speed will be faster
- the browser can start rendering even before full data arrive from server
- saving bandwidth. Because data is fetched from the same page
- Only necessary data is transferred
- good user experience, more interactive
- Disadvantages of AJAX:
- Ajax won't work if user disable javascript. Because AJAX depends on Javascript
- Ajax content can be a problem for Search engines.
- Ajax code can be read by human easily
- AJAX code is difficult to debug
- AJAX without jQuery:
using fetch api from javascript. this is a modern replacement for XMLHttpRequest that can call an AJAX request.
```javascript=
const ajax = {}
ajax.get = async (url) =>{
try{
const res = await fetch(url)
const data = await res.json()
return data
} catch(error){
console.log(error)
}
}
ajax.post = async(url, data)=>{
try{
const res = await fetch(url,
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
)
const data = await res.json()
return data
} catch(error){
console.log(error)
}
}
```
- AJAX with jQuery:
the AJAX function here "$.get" is used to send a GET/POST request with the given `url`. The callback funciton parameters will be data - the content that our page request and status - the status of the request.
In the POST request, there is an added parameter right after url, is the object data that send along with request to backend as a body
```javascript
$.get(
"http://localhost:5000/api/blogs",
function(data, status) {
//do something with return data
}
);
$.post(
"http://localhost:5000/api/blogs",
{
title: "cs230 test",
date:"5/17/2023"
},
(data, status)=>{
//do something here
}
);
```
**Q1d**
- HTTP GET vs POST:
- GET retrieves data from server, doesn't intend to modify server data.
- POST submits data to be processed in the server, intend to modify server data
- HTTP headers example:
- GET Header:
- GET /api/blogs HTTP/1.1
- Host: localhost:5000
- Accept-Language: en-US,en;q=0.9
- Accept-Encoding: gzip, deflate
- Accept: text/html,application/xhtml+xml,application/xml;
- POST Header:
- POST /api/blogs HTTP/1.1
- Host: localhost:5000
- Accept-Language: en-US,en;q=0.9
- Accept-Encoding: gzip, deflate
- Accept: text/html,application/xhtml+xml,application/xml;
- text=hello&check_box=true&radio=option1
- Javascript Form Processor:
```javascript
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.post('/api/blogs', async (req, res) => {
//extract data
const { text, checkbox_box, radio } = req.body;
// Validate data
if (typeof text !== 'string' || ![true, false].includes(checkbox_box) || !['value1', 'value2'].includes(radio)) {
return res.status(400).send('Invalid data');
}
// Process data
const feedback = await Feedback.create({text, checkbox_box, radio})
res.send({feedback, message:"New feedback created"});
});
```
**Q1e NO**
- MySQLi vs PDO: MySQLi only works with MySQL databases, procedural and OOP interfaces, support for prepared statements. PDO works with 12 different databases, only OOP interface, support for prepared statements, named parameters.
- Server-Side Prepared Statements in PHP:
```php
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
foreach ($users as $user) {
$stmt->execute([$user['name'], $user['email']]);
}
```
- Extract from a relational database and save to MongoDB:
```php
$records = $pdo->query('SELECT * FROM users')->fetchAll();
$collection = (new MongoDB\Client)->test->users;
foreach ($records as $record) {
$collection->insertOne($record);
}
```
**Q1f**
**Prepared Statements**
- A prepared statement is a feature used in database management systems, allowing SQL statements to be prepared and parsed once, but executed multiple times with different parameters.
- Example:
```php
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();
```
**Bound Parameters**
- Bound parameters are variables that are attached to a prepared statement. They take the place of literal values in the SQL statement.
- Example: In the above example, `$email` is a bound parameter.
**Advantages over Executed Statements**
- *Security*: protect against SQL injection attacks, as user input isn't directly put into the SQL string.
- *Performance*: preparing the statement once and executing it multiple times with different parameters can be more efficient.
- *Convenience*: Binding parameters can make code easier to read and maintain, as you do not have to manually quote and escape parameters.
**Q1g**
- Server-Side Prepared Statements in Node.js:
```javascript=
const { Client } = require('pg')
//config data for connecting database
const config = {
host: "webcourse.cs.nuim.ie",
user: "p230049",
password: "abc",
database: "cs130",
port: 5432,
};
const client = new Client(config)
//connect database using pg library
client.connect(function (err) {
if (err) throw err;
console.log("Connected!");
});
const users = [
{email:"tan.vo@gmail.com", name:"Vo"},
{email:"tan@gmail.com", name:"Tan"},
]
Promise.all(users.map( async(user) => {
await client.query('INSERT INTO p230049.cs230_users (email, name) VALUES ($1, $2) RETURNING *'
,[user.email, user.name]);
});
```
- Extract from a relational database and save to MongoDB in Node.js:
```javascript
const users = await client.query('SELECT * FROM users');
const { MongoClient } = require("mongodb");
const uri = "mongodb+srv://abc:xyz@cluster0.abczyx.mongodb.net"
const mongoClient = new MongoClient(uri);
const run = async ()=>{
try{
const usersCollection = mongoClient.db("cs230").collection("users");
usersCollection.insertMany(users)
} catch(error){
console.log(error)
}
}
run().catch(console.dir);
```
**Q1h**
- Sanitizing user data for MongoDB: NodeJS has various libraries for sanitizing input data like "validator" or "express-validator". For MongoDB, ensure to use the official MongoDB driver's API correctly. The driver's API will automatically sanitize input.
```javascript
const validator = require('validator');
let userData = user.name;
userData = validator.escape(userData);
```
- Using Server-Side Prepared Statements in Node.js:
```javascript
const sql = 'INSERT INTO users (name, email) VALUES ?';
const values = users.map(user => [user.name, user.email]);
connection.query(sql, [values], (error, results, fields) => {
if (error) throw error;
console.log('User records inserted: ', results);
});
```
In this example, we're preparing an SQL statement to insert user data into the database. The `?` placeholder is used to avoid SQL injection, and we're mapping user data to an array of values.
- Extract from MongoDB and save to a relational database in Node.js:
```javascript
const MongoClient = require('mongodb').MongoClient;
MongoClient.connect(uri, async function(err, client) {
const collection = client.db("test").collection("users");
const users = await collection.find().toArray();
client.close();
const sql = 'INSERT INTO users (name, email) VALUES ?';
const values = users.map(user => [user.name, user.email]);
connection.query(sql, [values], (error, results, fields) => {
if (error) throw error;
console.log('User records inserted: ', results);
});
});
```
The structure of MongoDB collection "users" and relational database table "users" both have "name" and "email" fields.
**Q1i**
MEAN Architecture and REST API Implementation
- MEAN stands for MongoDB, Express.js, AngularJS, and Node.js. It's a full-stack JavaScript solution.
- REST APIs can be built backend with Express.js and Node.js; MongoDB is used for the database, and AngularJS for the front-end.
- Differences from LAMP: MEAN is entirely JavaScript-based, while LAMP uses Linux, Apache, MySQL, and PHP.
- Differences from JAMstack: JAMstack focuses on frontend with prebuilt markup and APIs, while MEAN is full-stack.
Single-Page Applications (SPAs) and MVC Design Pattern
- SPAs load a single HTML page and dynamically update it as users interact.
- MVC separates an application into Model (data), View (UI), and Controller (business logic).
- AngularJS in MEAN is well-suited for SPA development and uses MVC.
Best Platform for SPAs: LAMP, JAMstack, or MEAN
- LAMP: Not ideal for SPAs due to lack of frontend framework.
- JAMstack: Suitable for SPAs that rely on static content and APIs.
- MEAN: Ideal for SPAs due to the inclusion of AngularJS and JavaScript full-stack solution.
**Q1j**
- Modeling approaches:
- Relational databases: Normalization is used to eliminate data redundancy and improve data integrity. This involves dividing large tables into smaller ones and defining relationships between them.
- MongoDB: Data is typically denormalized, with related data stored in single structures or 'documents'. This improves read performance.
- In MongoDB, a normalized approach may be preferred when: dealing with large documents, when data changes frequently, or when there's a need to represent more complex
**Q2a**
- MVVM (Model-View-ViewModel) is a design pattern that helps in separating the business logic from the UI. It consists of:
- Model: Represents data and business logic.
- View: UI representation of the data.
- ViewModel: A connector between the model and the view, exposing streams of data relevant to the view.
- MVVM differs from MVC in how they handle user input and separate concerns. In MVC, the controller handles user input and updates the model, whereas in MVVM, it's the ViewModel's responsibility.
- MVC in AngularJS: **remove**
```javascript
var app = angular.module('myApp', []);
app.controller('MyController', function($scope) {
$scope.model = {
name: "John Doe"
};
});
```
In this example, AngularJS Controller 'MyController' is managing the model data for the view.
- One-way binding means that changes in the model automatically update the view but changes in the view do not affect the model. Two-way binding means that changes in the model update the view and vice versa.
- One-way data binding implies that the flow of data is unidirectional.
- Example: In React, one-way data binding can be illustrated as follows:
```javascript=
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { message: 'Hello, world!' };
}
render() {
return <h1>{this.state.message}</h1>;
}
}
```
Here, the view (the h1 element) is updated when the model (this.state.message) changes, but not vice versa.
Two-Way Data Binding:
Two-way data binding, on the other hand, means that UI fields are bound to model data dynamically such that when a UI field changes, the model data changes with it and vice-versa.
```javascript=
<input [(ngModel)]="name">
<p>Hello, {{name}}!</p>
```
Here, any change to the name property will update the input field and any change to the input field will update the name property.
**Q2b**
- The Model-View-Presenter (MVP) is a design pattern in web development that separates an application into three components. The Model represents the data and business logic, the View displays the data, and the Presenter acts as an intermediary, handling communication between the Model and the View. The Presenter manipulates the Model and updates the View accordingly. The View and Presenter are decoupled for easier unit testing. MVP has two variants: Passive View where the View doesn't access the Model, and Supervising Controller where it does. MVP enhances automated testing and improves separation of concerns in presentation logic.
- MVP vs MVC (paraphrase it later)
- MVP: View is more loosely coupled to the model. The presenter is responsible for binding the model to the view.
- Easier to unit test because interaction with the view is through an interface
- Usually view to presenter map one to one. Complex views may have multi presenters.
- MVC Pattern: Controllers are based on behaviours and can be shared across views
- Can be responsible for determining which view to display.
- chatgpt:The Model-View-Presenter (MVP) and Model-View-Controller (MVC) patterns are both design patterns used in web development, but they differ in several ways.
- In the MVP pattern, the View is loosely coupled with the Model, and it's the Presenter's responsibility to bind them together. This setup makes unit testing easier as the interaction with the View is done through an interface. Generally, there is a one-to-one mapping between the View and the Presenter, though more complex Views may be linked with multiple Presenters.
- On the other hand, in the MVC pattern, Controllers, which are based on behaviors, can be shared among multiple Views. Furthermore, Controllers can determine which View to display, allowing more flexibility in managing different views of the same data.
- MVC in pure JavaScript:
```javascript=
class TitleModel {
constructor() {
this.title = "";
}
}
class TitleController {
constructor(model) {
this.model = model;
}
handleClick() {
this.model.title = "New Title";
}
}
class TitleView {
constructor(cont) {
this.controller = cont;
this.titleEl = document.getElementById("title");
this.titleEl.innerText = cont.model.title;
this.titleEl.addEventListener("click", cont.handleClick.bind(cont));
}
}
function main() {
var model = new TitleModel();
var controller = new TitleController(model);
var view = new TitleView(controller);
}
main();
```
Here, the Controller in MVC pattern is set up in pure JavaScript. It communicates between the Model and the View.
- One-way binding means that changes in the model automatically update the view, but changes in the view do not affect the model. Two-way binding means that changes in the model update the view and vice versa.
**Q2c**
- Differences:
- In MVP, the Presenter holds a direct reference to the View, while in MVVM, the ViewModel does not.
- The View directly binds to properties on the ViewModel in MVVM, requiring binding technology or boilerplate code for efficient operation.
- Similarities:
- Both patterns aim to separate concerns, improving maintainability and testability.
- Both act as intermediaries between the View and the Model, managing data flow and application logic.
- MVVM in pure JavaScript:
```javascript
class TitleModel {
constructor() {
this.title = "Default";
}
}
class TitleViewModel {
constructor(model) {
this.model = model;
}
get title() {
return this.model.title;
}
set title(value) {
this.model.title = value;
}
}
class TitleView {
constructor(viewModel) {
this.viewModel = viewModel;
this.titleElement = document.getElementById("title");
this.titleElement.innerText = this.viewModel.title;
this.titleElement.addEventListener("click", this.handleClick.bind(this));
}
handleClick() {
this.viewModel.title = "New Title";
this.updateView();
}
updateView() {
this.titleElement.innerText = this.viewModel.title;
}
}
// Main function
function main() {
var model = new TitleModel();
var viewModel = new TitleViewModel(model);
var view = new TitleView(viewModel);
}
main();
```
Here, the ViewModel communicates with the Model and exposes an API for the View to interact with.
- One-way binding is when changes in the model state reflect on the view automatically, but changes in the view do not affect the model state. Two-way binding is when both the model state changes reflect on the view and vice versa.
**Q2d**
- Cross-Origin Resource Sharing (CORS) allows web applications to request resources from a another domain than the original one:
- CORS allows a web page to fetch API to another domain, which is by default disallowed because of security restrictions.
- For example, if a JavaScript app at `http://localhost:3000` send a request to `http://localhost:5000/api/user.json`. If there is no CORS, the browser would block this request. However, i`localhost:5000` config appropriate CORS, this request can be allowed.
- We can config by setting HTTP headers (`Access-Control-Allow-Origin`) on the server to tell which origins are permitted to access its resources.
- CORS is a compromise between security and flexibility. It allows more flexibility in accessing resources, but it could occur potential security issues.
- CORS in NodeJS and jQuery:
On server, we can use cors library to enable CORS. Using `app.use(cors())` will allows the server to response to CORS request from any other origin.
Server-side (NodeJS):
```javascript
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/data', function (req, res) {
res.json({ message: 'Hello World' });
});
app.listen(5000);
```
On Client, we can use jQuery $.get to send a GET request to the server. The request must successfully done even it was called from different origin.
Client-side (jQuery):
```javascript
$.get('http://localhost:5000/data',function(data) {
console.log(data);
}
);
```
client logs the data to the console after getting it from localhost:5000/data. The data should be a JSON object {message:"Hello World"}
A simple GET request is a straightforward request made from the client to the server, typically used to fetch data. It does not trigger a preflight CORS request.
```javascript
async function fetchData() {
try {
const res = await fetch('http://example.com/data');
const data = await res.json();
return data
} catch (error) {
console.error( error);
}
}
fetchData();
```
Preflighted CORS POST Request
A preflighted CORS POST request is sent by the client to the server when making a POST request that doesn't meet the criteria of a simple request. It includes an additional preflight request to determine if the server allows the actual POST request.
```javascript
async function postPreflightedData() {
try {
const res = await fetch('http://locahost:5000/blogs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Custom-Header': 'custom-value'
},
body: JSON.stringify({ message: 'Hello World' })
});
const data = await res.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
postPreflightedData();
```
The POST request triggers a preflight OPTIONS request to find if the server allows the POST request with custom headers. The server would respond with CORS headers that show if the request is accepted.
**Q2e**
- CORS (Cross-Origin Resource Sharing) is a standard that allows or restricts cross-origin HTTP requests initiated from scripts running in the browser. It mitigates risks associated with the Same-Origin policy.
- CORS in a PHP application:
we can use PHP headers to set the CORS headers.
```php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST");
$data = ["message" => "Hello"];
echo json_encode($data);
```
Client-side (Pure JavaScript):
```javascript
async function fetchData() {
try {
const response = await fetch('http://locahost:5000/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
fetchData();
```
The Servers use the header() function to set the CORS.
- Access-Control-Allow-Origin header allows requests from all origins (*)
- Access-Control-Allow-Methods header allows GET and POST methods
- Access-Control-Allow-Headers header allows the Content-Type header.
The Client uses the fetch to send a GET request. The server responds with a JSON-encoded response.
Server-side (PHP):
- Simple CORS POST requests are made directly without any preflight request. Preflight CORS POST requests, on the other hand, involve a preliminary "OPTIONS" request sent by the browser to the server to determine whether the server is prepared to handle the actual request.
**Q2f**
**Client-side HTTP Headers:**
- Request:
- Method: OPTIONS
- URL: `http://localhost:5000/api/blogs`
- Headers:
- Origin: `http://localhost:5000`
- Access-Control-Request-Method: POST
- Access-Control-Request-Headers: Content-Type
- Response:
- Status: 200 OK
- Headers:
- Access-Control-Allow-Origin: `http://localhost:5000`
- Access-Control-Allow-Methods: POST
- Access-Control-Allow-Headers: Content-Type
**Server-side HTTP Headers:**
- Request:
- Method: POST
- URL: `http://localhost:5000/blogs`
- Headers:
- Origin: `http://localhost:5000`
- Content-Type: application/json
- Response:
- Status: 200 OK
- Headers:
- Access-Control-Allow-Origin: `http://localhost:5000`
- Content-Type: application/json
The client sends a preflight OPTIONS request to the server. This is to check whether the server allows the POST request or not.
In response, the server tells the client that the POST request is allowed request from all origin and POST methods.
The client sends the POST request with the JSON content after receiving the response from preflight request.
The server responds and gives the client permit to access the response data.