Categories
React Native

React Native — Images, Views, and Text

React Native is a mobile development that’s based on React that we can use to do mobile development.

In this article, we’ll look at how to use it to create an app with Reac.

Uri Data Images

We can add images for a base64 URI.

For example, we can write:

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Image
        style={{
          width: 51,
          height: 51,
          resizeMode: 'contain'
        }}
        source={{
          uri:
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=='
        }}
      />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

We just set the uri property to set the image URI.

We should only use this for small images.

Background Image

For example, we can write:

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { ImageBackground, StyleSheet, View, Text } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <ImageBackground source={require('./assets/fork.jpg')} style={{ width: '100%', height: '100%' }}>
        <Text>Inside</Text>
      </ImageBackground>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

We used the ImageBackground component to add the background image.

Then we can have things like text inside it.

View

A View is a container that supports layouts with flexbox, styles, touch handling, and accessibility controls.

For example, we can write:

import React from 'react';
import { View, Text } from 'react-native';

export default function App() {
  return (
    <View
      style={{
        flexDirection: "row",
        height: 100,
        padding: 20
      }}
    >
      <View style={{ backgroundColor: "green", flex: 0.3 }} />
      <View style={{ backgroundColor: "red", flex: 0.5 }} />
      <Text>Hello World!</Text>
    </View>
  );
}

The outer View component is a flexbox container for the inner View components.

We can set the backgroundColor for views.

And we can add the Text component to add the text.

Text

The Text component is a component that’s used for displaying text.

For example, we can write:

import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';

const onPressTitle = () => {
  console.log("title pressed");
};

export default function App() {
  const titleText = useState("Bird's Nest");
  const bodyText = useState("This is not really a bird nest.");

  return (
    <View
      style={{
        flexDirection: "row",
        height: 100,
        padding: 20
      }}
    >
      <Text style={styles.baseText}>
        <Text style={styles.titleText} onPress={onPressTitle}>
          {titleText}
          {"n"}
        </Text>
        <Text numberOfLines={5}>{bodyText}</Text>
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  baseText: {
    fontFamily: "Cochin"
  },
  titleText: {
    fontSize: 20,
    fontWeight: "bold"
  }
});

We can add the Text component to add some text.

We add the onPress prop and pass in an event handler to add an event handling for touching the text.

Conclusion

We can add URIs for images and background images with React Native.

Also, we can add View components for containers and Text component for text.

Categories
React Native

React Native — Flat Lists

React Native is a mobile development that’s based on React that we can use to do mobile development.

In this article, we’ll look at how to use it to create an app with React Native.

FlatList

We can add the FlatList component to display a simple list view.

It’s cross-platform and works with horizontal mode.

Also, it has header, footer, and separator support.

We can pull it to refresh the data.

We can load data as we scroll with it.

And it also has multiple columns support.

For example, we can write:

import React from 'react';
import { SafeAreaView, FlatList, StyleSheet, View, Text } from 'react-native';
const DATA = Array(100).fill().map((_, i) => ({ id: i, title: i }))

const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);

const renderItem = ({ item }) => (
  <Item title={item.title} />
);

export default function App() {
  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={DATA}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 0
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  title: {
    fontSize: 32,
  },
});

to create our list.

We create the Item component that renders the View and Text component for a list item.

The renderItem function is a function to render a Item .

Then we add a SafeAreaView and a FlatList to let us render a scrollable list.

We pass an array in the data prop.

And the renderItem prop has the renderItem function we created earlier.

keyExtractor is a function to get the unique key for an item.

We can make our items selectable by using the TouchableOpacity component:

import React from 'react';
import { SafeAreaView, FlatList, StyleSheet, Text, TouchableOpacity } from 'react-native';
const DATA = Array(100).fill().map((_, i) => ({ id: i, title: i }))

const Item = ({ item: { title }, onPress, style }) => (
  <TouchableOpacity onPress={onPress} style={[styles.item, style]}>
    <Text style={styles.title}>{title}</Text>
  </TouchableOpacity>
);

export default function App() {
  const [selectedId, setSelectedId] = React.useState(null);
  const renderItem = ({ item }) => {
    const backgroundColor = item.id === selectedId ? "lightgreen" : "pink";
    return (
      <Item
        item={item}
        onPress={() => setSelectedId(item.id)}
        style={{ backgroundColor }}
      />
    );
  };

return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={DATA}
        renderItem={renderItem}
        keyExtractor={item => item.id}
        extraData={selectedId}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: 0
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  title: {
    fontSize: 32,
  },
});

The Item component is rendered with the TouchableOpacity component to let us show something different when it’s selected.

In the App component, we have the renderItem function which takes the item and set the background color according to which one is pressed.

We determine which one is pressed with the onPress prop, which takes a function that calls setSelectedId to set the selected item’s ID.

Then in the style prop, we set the backgroundColor property to the color we want according to whether the selectedId is the same as the current item’s id .

Now when an item is selected, the item has a lightgreen background.

Otherwise, it has a pink background.

Conclusion

We can add a FlatList component to display a scrollable list of items.

Categories
React Native

Getting Started with Mobile Development with React Native

React Native is a mobile development that’s based on React that we can use to do mobile development.

In this article, we’ll look at how to use it to create our first app.

Getting Started

To get started with React Native, we’ve to install the Expo CLI.

We run:

npm install -g expo-cli

to install it.

Node 12 LTS or later is required.

Then we can create a React Native project by writing:

expo init react-native-example

We also have to install Android SDK with an Android device or emulator.

We can install Genymotion which is faster than the Android emulator.

To install it, we can go to https://www.genymotion.com/ to download it.

Then we go to:

cd react-native-example
yarn android

to start the Expo CLI.

In the react-native-example project, we have App.js which has:

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

The View component has the main app view.

The Text component has text.

StatusCar has the status bar.

The StyleSheet.create method creates the styles.

Then we applied it with the styles prop.

We can use flexbox and other CSS properties for our layouts.

Images

We can add images by using the Image component.

For example, we can write:

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Image source={require('./assets/fork.jpg')} />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

We add the Image component with the source prop to set the image.

We can also add icons from a URL. For example, we can write:

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Image source={{ uri: 'https://reactjs.org/logo-og.png' }}
        style={{ width: 400, height: 400 }} />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

We can also set other HTML request data like the headers and body when we get the image.

For example, we can write:

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Image
        source={{
          uri: 'https://reactjs.org/logo-og.png',
          method: 'POST',
          headers: {
            Pragma: 'no-cache'
          },
          body: 'Your Body goes here'
        }}
        style={{ width: 400, height: 400 }}
      />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Conclusion

We can get started with mobile development with React Native in a few minutes.

And we can use Genymotion or a real device to preview our app fast.

Categories
Express MongoDB Nodejs Vue 3

Create a Full Stack Web App with the MEVN Stack

The MEVN stack is a set of technologies for full-stack apps.

MEVN stands for MongoDB, Express, Vue.js, and Node.js

In this article, we’ll take a look at how to create a simple todo app with authentication with the MEVN stack.

Setting Up the Project

The first step to create a full-stack MEVN app is to set up the project.

First, we create a project folder, then add the backend and frontend folders inside it.

backend has the Express app.

frontend has the Vue.js app. We’ll use Vue 3 for the front end.

Next, to create our Express app, we run the Express Generator to create the files.

To do this, go into the backend and run:

npx express-generator

We may need admin privileges to do this.

Next, we go into the frontend folder.

We install the latest version of the Vue CLI by running:

npm install -g @vue/cli

Vue CLI 4.5 or later can create Vue 3 projects.

Then in the frontend folder, we run:

vue create .

Then we choose the ‘Vue 3 (default)’ option.

Next, we have to install the packages.

We stay in the frontend folder and run:

npm i axios vue-router@4.0.0-beta.12

to install the Axios HTTP client and Vue Router 4.x, which is compatible with Vue 3.

In the backend folder, we run:

npm i cors momgoose jsonwebtoken

to install the CORS and Mongoose packages to enable cross-domain communication and work with MongoDB.

jsonwebtoken lets us add JSON web token authentication into our app.

Create the Back End

Now we’re ready to create the back end app to work with MongoDB and add authentication.

First, we create db.js in the backend folder and add:

const { Schema, createConnection } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/mevn-example', { useNewUrlParser: true });

const userSchema = new Schema({
  name: String,
  password: String
});

const User = connection.model('User', userSchema);

const todoSchema = new Schema({
  name: String,
  done: Boolean,
  user: { type: Schema.Types.ObjectId, ref: 'User' },
});

const Todo = connection.model('Todo', todoSchema);

module.exports = {
  User,
  Todo
}

We connect to the MongoDB database with Mongoose and create the models.

To create the models, we use the Schema constructor to create the schema.

We add the properties and their data types.

The user property references the Object ID from the User model so that we can link the todo entry to the user.

Then we call connection.model to use the schema to create the model.

Then, we create constants.js in the backend folder and write:

module.exports = {
  SECRET: 'secret'
}

We put the secret for the JSON web token into this file and use them in the route files.

Next, we go into the routes folder and add the files for the API routes.

We create todos.js in the routes folder and write:

var express = require('express');
var router = express.Router();
const { Todo } = require('../db');
const jwt = require('jsonwebtoken');
const { SECRET } = require('../constants');

const verifyToken = (req, res, next) => {
  try {
    req.user = jwt.verify(req.headers.authorization, SECRET);
    return next();
  } catch (err) {
    console.log(err)
    return res.status(401);
  }
}

router.get('/', verifyToken, async (req, res) => {
  const { _id } = req.user;
  const todos = await Todo.find({ user: _id })
  res.json(todos);
});

router.get('/:id', verifyToken, async (req, res) => {
  const { _id } = req.user;
  const { id } = req.params;
  const todo = await Todo.findOne({ _id: id, user: _id })
  res.json(todo);
});

router.post('/', verifyToken, async (req, res) => {
  const { name } = req.body;
  const { _id } = req.user;
  const todo = new Todo({ name, done: false, user: _id })
  await todo.save()
  res.json(todo);
});

router.put('/:id', verifyToken, async (req, res) => {
  const { name, done } = req.body;
  const { id } = req.params;
  const todo = await Todo.findOneAndUpdate({ _id: id }, { name, done })
  await todo.save();
  res.json(todo);
});

router.delete('/:id', verifyToken, async (req, res) => {
  const { id } = req.params;
  await Todo.deleteOne({ _id: id })
  res.status(200).send();
});

module.exports = router;

The verifyToken middleware has the jwt.verify method to verify the token with our SECRET key.

If it’s valid, we set the req.user to the decoded token.

Then we call next to call the router handlers.

Then we have the GET / route to get the user data from req.user and then call Todo.find to find the todo entries for the user.

Todo is the model we created earlier.

The GET /:id route gets the Todo entry by the ID.

findOne gets the first result in the todos collection.

req.params gets the URL parameters from the URL parameter.

Then POST / route gets the name from the req.body property, which has the request body daya.

And we get the _id property from the req.user property to get the user data.

We use both to create the todo entry.

user has the Object ID for the user.

The PUT /:id route is used to update the user.

We get the name and done from req.body , which has the request body.

req.params has the id property which has the ID of the todo entry.

We use the id to find the todo entry.

And update the name and done property to update the entry.

Then we call todo.save() to save the data.

The DELETE /:id route lets us delete a todo entry by its ID.

We call deleteOne with the _id to do that.

The verifyToken will make sure the token is valid before we run the route handlers.

req.headers.authorization has the auth token. req.headers has the HTTP request headers.

Next, we create users.js in the routes folder and write:

var express = require('express');
var router = express.Router();
const bcrypt = require('bcrypt');
const { User } = require('../db');
const { SECRET } = require('../constants');
const jwt = require('jsonwebtoken');
const saltRounds = 10;

router.post('/register', async (req, res) => {
  const { name, password } = req.body;
  const existingUser = await User.findOne({ name });
  if (existingUser) {
    return res.json({ err: 'user already exists' }).status(401);
  }
  const hashedPassword = await bcrypt.hash(password, saltRounds);
  const user = new User({
    name,
    password: hashedPassword
  })
  await user.save();
  res.json(user).status(201);
});

router.post('/login', async (req, res) => {
  const { name, password } = req.body;
  const { _id, password: userPassword } = await User.findOne({ name });
  const match = await bcrypt.compare(password, userPassword);
  if (match) {
    const token = await jwt.sign({ name, _id }, SECRET);
    return res.json({ token });
  }
  res.status(401);
});

module.exports = router;

It’s similar to the todos.js .

We have the POST /register route to get the name and password properties from the JSON request body.

Then we check if the user with the given name exists.

Then we get the bcrypt.hash to hash the password with the secret.

And then we save the user with the name and then hashedPassword.

The POST /login route gets the name and password from the JSON request body.

We use the bcrypt.compare method to compare the password.

Then we create the token and return it in the response if validation is successful.

Otherwise, we send a 401 response.

Finally, we bring everything together in app.js in the backend folder.

We replace what’s there with:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require('cors')

var todorouter = require('./routes/todo');
var usersRouter = require('./routes/users');

var app = express();
app.use(cors())
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/todos', todorouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

// render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

We add the cors middleware so that we can communicate with the front end.

We have the todorouter and usersRouter to add the routes to our app.

Vue 3 Front End

Now that we finished the back end, we work on the front end.

First, in the components folder, we create TodoForm.vue and write:

<template>
  <div>
    <h1>{{ edit ? "Edit" : "Add" }} Todo</h1>
    <form @submit.prevent="submit">
      <div class="form-field">
        <label>Name</label>
        <br />
        <input v-model="form.name" />
      </div>
      <div>
        <label>Done</label>
        <input type="checkbox" v-model="form.done" />
      </div>
      <div>
        <input type="submit" value="Submit" />
      </div>
    </form>
  </div>
</template>

<script>
import axios from "axios";
import { APIURL } from "../constants";

export default {
  name: "TodoForm",
  data() {
    return {
      form: { name: "", done: false },
    };
  },
  props: {
    edit: Boolean,
    id: String,
  },
  methods: {
    async submit() {
      const { name, done } = this.form;
      if (!name) {
        return alert("Name is required");
      }
      if (this.edit) {
        await axios.put(`${APIURL}/todos/${this.id}`, { name, done });
      } else {
        await axios.post(`${APIURL}/todos`, { name, done });
      }
      this.$router.push("/todos");
    },
    async getTodo() {
      const { data } = await axios.get(`${APIURL}/todos/${this.id}`);
      this.form = data;
    },
  },
  beforeMount() {
    if (this.edit) {
      this.getTodo();
    }
  },
};
</script>

We have a form to let us add or edit to todo entries.

It takes the edit prop to indicate whether we’re editing a todo or not.

We use v-model to bind the inputs to the reactive properties.

Then we check for the this.edit value.

If it’s true , we make a PUT request with the id of the todo entry.

Otherwise, we make a POST request to create a new todo entry.

The getTodo method lets us get the todo by the ID when we’re editing.

Next, in the src folder, we create the views folder to add the route components.

We create the AddTodoForm.vue file and add:

<template>
  <div>
    <TodoForm></TodoForm>
  </div>
</template>

<script>
import TodoForm from "@/components/TodoForm";

export default {
  components: {
    TodoForm,
  },
};
</script>

We register the TodoForm component and render it in the template.

Next, we create the EditTodoForm.vue in the views folder and add:

<template>
  <div>
    <TodoForm edit :id='$route.params.id'></TodoForm>
  </div>
</template>

<script>
import TodoForm from "@/components/TodoForm";

export default {
  components: {
    TodoForm,
  },
};
</script>

We pass the edit and id props into the TodoForm so it can get todo entry by ID.

$route.params.id has the Object ID of the todo entry.

Next, we create the Login.vue file in the views folder to add a login form.

In this file, we add:

<template>
  <div>
    <h1>Login</h1>
    <form @submit.prevent="login">
      <div class="form-field">
        <label>Username</label>
        <br />
        <input v-model="form.name" type="text" />
      </div>
      <div class="form-field">
        <label>Password</label>
        <br />
        <input v-model="form.password" type="password" />
      </div>
      <div>
        <input type="submit" value="Log in" />
        <button type="button" @click="$router.push('/register')">
          Register
        </button>
      </div>
    </form>
  </div>
</template>

<script>
import axios from "axios";
import { APIURL } from "../constants";

export default {
  data() {
    return {
      form: { name: "", password: "" },
    };
  },
  methods: {
    async login() {
      const { name, password } = this.form;
      if (!name || !password) {
        alert("Username and password are required");
      }
      try {
        const {
          data: { token },
        } = await axios.post(`${APIURL}/users/login`, {
          name,
          password,
        });
        localStorage.setItem("token", token);
        this.$router.push("/todos");
      } catch (error) {
        alert("Invalid username or password.");
      }
    },
  },
};
</script>

We have a form that takes the username and password and let the user log in.

When we submit the form, the login method is called.

In the method, we check if the name and password have values.

If they both are nonempty, we proceed with making the login request.

If it succeeds, we go to the /todos route.

Otherwise, we should an error message.

Similarly, we create Register.vue component for the registration form.

Then we fill it with the following code:

<template>
  <div>
    <h1>Register</h1>
    <form @submit.prevent="register">
      <div class="form-field">
        <label>Username</label>
        <br />
        <input v-model="form.name" type="text" />
      </div>
      <div class="form-field">
        <label>Password</label>
        <br />
        <input v-model="form.password" type="password" />
      </div>
      <div>
        <input type="submit" value="Register" />
        <button type="button" @click="$router.push('/')">Login</button>
      </div>
    </form>
  </div>
</template>

<script>
import axios from "axios";
import { APIURL } from "../constants";

export default {
  data() {
    return {
      form: { name: "", password: "" },
    };
  },
  methods: {
    async register() {
      const { name, password } = this.form;
      if (!name || !password) {
        alert("Username and password are required");
      }
      try {
        await axios.post(`${APIURL}/users/register`, {
          name,
          password,
        });
        alert("Registration successful");
      } catch (error) {
        alert("Registration failed.");
      }
    },
  },
};
</script>

We get the username and password the same way.

If they’re both non-empty, then we make a request to the register route.

Once that succeeds, we show a success message.

Otherwise, we show a failure message, which is in the catch block.

Next, we have a component to show the todo items.

We create Todo.vue in the views folder and write:

<template>
  <div>
    <h1>Todos</h1>
    <button @click="$router.push(`/add-todo`)">Add Todo</button>
    <button @click="logOut">Logout</button>
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Done</th>
          <th>Edit</th>
          <th>Delete</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="t of todos" :key="t._id">
          <td>{{ t.name }}</td>
          <td>{{ t.done }}</td>
          <td>
            <button @click="$router.push(`/edit-todo/${t._id}`)">Edit</button>
          </td>
          <td>
            <button @click="deleteTodo(t._id)">Delete</button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import axios from "axios";
import { APIURL } from "../constants";

export default {
  data() {
    return {
      todos: [],
    };
  },
  methods: {
    async getTodos() {
      const { data: todos } = await axios.get(`${APIURL}/todos`);
      this.todos = todos;
    },
    async deleteTodo(id) {
      await axios.delete(`${APIURL}/todos/${id}`);
      this.getTodos();
    },
    logOut() {
      localStorage.clear();
      this.$router.push("/");
    },
  },
  beforeMount() {
    this.getTodos();
  },
};
</script>

<style scoped>
th:first-child,
td:first-child {
  width: 60%;
}

th {
  text-align: left;
}
</style>

We have the Add Todo button that goes to the add-todo route when we click it.

This will be mapped to the AddTodoForm.vue component.

The Logout button calls the logOut method when it’s clicked.

It just clears local storage and redirect the user to the login page.

The getTodos method gets the todo entries for the user.

The user identity is determined from the decoded token.

The v-for directive renders the items and display them.

The Edit button goes to the edit form.

And the Delete button calls deleteTodo when we click it.

deleteTodo makes a DELETE request to the todos route.

We call getTodos after deleting a todo entry or when we load the page.

Next in src/App.vue , we add our router-view to show the route:

<template>
  <router-view></router-view>
</template>

<script>
export default {
  name: "App",
};
</script>

<style>
.form-field input {
  width: 100%;
}

#app {
  margin: 0 auto;
  width: 70vw;
}
</style>

Then we create constants.js in the src folder and add:

export const APIURL = 'http://localhost:3000'

to let us use the APIURL everywhere so that we don’t have to repeat the base URL for the back end API when we make requests.

Finally, in main.js , we write:

import { createApp } from 'vue'
import App from './App.vue'
import { createRouter, createWebHashHistory } from 'vue-router';
import Login from '@/views/Login';
import Todo from '@/views/Todo';
import Register from '@/views/Register';
import AddTodoForm from '@/views/AddTodoForm';
import EditTodoForm from '@/views/EditTodoForm';
import axios from 'axios';

axios.interceptors.request.use((config) => {
  if (config.url.includes('login') || config.url.includes('register')) {
    return config;
  }
  return {
    ...config, headers: {
      Authorization: localStorage.getItem("token"),
    }
  }
}, (error) => {
  return Promise.reject(error);
});

const beforeEnter = (to, from, next) => {
  const token = localStorage.getItem('token');
  if (token) {
    next()
    return true;
  }
  next({ path: '/' });
  return false
}

const routes = [
  { path: '/', component: Login },
  { path: '/register', component: Register },
  { path: '/todos', component: Todo, beforeEnter },
  { path: '/add-todo', component: AddTodoForm, beforeEnter },
  { path: '/edit-todo/:id', component: EditTodoForm, beforeEnter },
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

const app = createApp(App);
app.use(router);
app.mount('#app')

to add the vue-router and the Axios request interceptor to add the auth token for authenticated routes.

beforeEnter is a route guard that lets us restrict access for authenticated routes.

We check if the token is present before we redirect the user to the page they’re going to.

Otherwise, we redirect to the login page.

routes has all the routes. path has the route URLs an component has the component we want to load.

beforeEnter is the route guard that loads before the route loads.

createRouter creates the router object.

createWebHashHistory lets us use hash mode for URLs.

So URLs will have the # sign between the base URL and the rest of the URL segments.

Then we call app.use(router) to add the router and make the this.$route and thuis.$router properties available in components.

Now we can run npm run serve from the frontend folder and node bin/www to start the back end.

Conclusion

We can create a simple MEVN stack app with the latest JavaScript frameworks and features without much trouble.

Categories
JavaScript Basics

Highlights of JavaScript — Form Validation and Event Handling

To learn JavaScript, we must learn the basics.

In this article, we’ll look at the most basic parts of the JavaScript language.

Dropdown Validation

We can add validation to select dropdown fields.

For example, we can write the following HTML:

<form onSubmit="return checkForSelection();">
  <select id="fruits">
    <option value="" selected="selected">
      SELECT A FRUIT</option>
    <option value="apple">apple</option>
    <option value="orange">orange</option>
    <option value="grape">grape</option>
  </select>
  <br>
  <input type="submit" value="Submit Form">
</form>

And then add the following JavaScript:

const checkForSelection = () => {
  if (document.getElementById("fruits").selectedIndex === 0) {
    alert("Please select a fruit.");
    return false;
  }
}

We added a select element and when we click on Submit Form, the checkForSelection function is run.

We add the return keyword so that we stop the default submit behavior and let the checkForSelection run.

Radio Button Validation

Also, we can add validation to a group of radio buttons.

For instance, we can write the following HTML:

<form onSubmit="return validateRadios();">
  <input type="radio" name="r1" value="apple"> apple<br>
  <input type="radio" name="r1" value="orange"> orange<br>
  <input type="radio" name="r1" value="grape"> grape<br>
  <input type="submit" value="Submit Form">
</form>

And the following JavaScript:

const validateRadios = () => {
  const radios = document.getElementsByName("r1");
  for (const r of radios) {
    if (r.checked) {
      return true;
    }
  }
  alert("Please check one.");
  return false;
}

We have a form with a bunch of radio buttons with the same name attribute value.

They should have the same name so that they’re considered to be in the same group.

When we click the Submit Form button, the validateRadios function runs.

In the function, we get all the radio buttons by calling getElementsByName with the name attribute value of the radio buttons.

We loop through the radio buttons with the for-of loop.

Then if one is checked, we return true .

Otherwise, we show an alert and return false is none of them are checked.

Email Validation

We can validate email address in a form field by checking the format of the entered text with a regex.

For example, we can write the following HTML:

<form onSubmit="return validateEmail();">
  Please enter your email.<br>
  <input type="text" id="emailField">
  <input type="submit" value="Submit">
</form>

And write the following JavaScript code:

const validateEmail = () => {
  const emailField = document.getElementById("emailField");
  const regex = /^[\w\-\.\+]+\@[a-zA-Z0-9\. \-]+\.[a-zA-z0-9]
  if (!(regex.test(emailField.value))) {
    alert("Please correct email address");
    return false;
  }
}

We have the validateEmail function that gets the value of the input with ID emailField .

Then we call the regex.test method to check if the inputted value matches the regex format.

If it doesn’t then we show an alert and return false .

The return keyword lets us prevent the default submit behavior from running.

Handling Events

We can attach event listeners to HTML elements.

For example, we can write the following HTML:

<input type="button" value="Click" id='button1'>

And write the following JavaScript:

const b1 = document.getElementById("button1");
const sayHello = () => {
  alert('hello');
}
b1.onclick = sayHello;

We get the button with getElementById .

Then we set the onclick property to the sayHello event handler function.

The functions show an alert with the alert function.

Now when we click on the button, we show the alert.

Conclusion

We can add form validation and the add event handlers to HTML elements so that we can do what we want when the user does something to the element.