Angle Docs

AngleBootstrap 4 Admin Template + NextJS

Thank you for purchasing Angle - Bootstrap 4 Admin Template + NextJS.
If you have any question about this product and beyond the scope of this help file, please feel free to write us using the support button.


Thanks so much!

Introduction

This document aims to explain the best way to work with the product and its components.

- All the best and enjoy coding.

Getting started tips

  • Do not start from scratch, use an existing asset and modify it to learn how it works.
  • Explore the sources for ideas and sample code.
  • Use Firebug or Chrome Developer Tools to find bugs on your website. Using one of those tools will help you to save time analyzing the site and finding elements structure, like classes, id or tags
    • Quick tip: open your site with Chrome, press F12 and go to console tab, reload your page and if something goes wrong you will see your page errors in red text.
  • In case of errors, someone might have seen it too, you can try a Google search for a quick fix.

Important Opening the index.html with a double click (i.e. using file:// protocol) will show you only a blank page because there’s no server that response to the requests made for each view in order to display the app interface.

Technologies used

This template is based on the following technologies. Follow the links for each one of them in order to get more information:

Structure

Before starting to customize the template, here is the project files organization structure:

+---components
|   +---Charts
|   +---Common
|   +---Elements
|   +---Forms
|   +---Layout
|   +---Maps
|   +---Tables
+---pages
|   +---blog
|   +---charts
|   +---dashboard
|   +---ecommerce
|   +---elements
|   +---extras
|   +---forms
|   +---forum
|   +---maps
|   +---pages
|   +---tables
|   +---widgets
+---static
|   +---img
|   |   +---user
|   +---locales
|       +---en
|       +---es
+---store
|   +---actions
|   +---reducers
+---styles
    +---app
    |   +---charts
    |   +---common
    |   +---elements
    |   +---extras
    |   +---forms
    |   +---layout
    |   +---tables
    +---bootstrap
    |   +---mixins
    |   +---utilities
    |   +---vendor
    +---themes

This structure is based on the provided by the create-next-app project. There are more files in the structure, we will list the main files in order to describe the application workflow based on the order they are imported.

Main Folders

  • components/ This folder contains reusable components (not routes)
  • pages/ This folder contains all components that are used as routes among other from NextJS like _app.js
  • static/ As the name says, this folder contains only static served assets
  • store/ Here you can find Redux related files like actions, reducers, etc.
  • styles/ All styles used in the template

Main Files

  • pages/index.js is used only as the index of the site, and the only purpose is to redirect to a default route. You can customize this files like any other NextJS page.

  • pages/_app.js is the custom App component that is used to add custom behavior to the application, like Redux, Translated, dynamic Layouts, etc. In this file is also imported all the styles and required vendor assets like icons. polyfills, etc.

  • pages/_error.js is basically a custom Error page to make it compatible with the template layout.

  • polyfills.js contains required polyfills for older browsers, included necessaries for IE11+

  • server.js is a custom Express server to support custom routes on development.

  • next.config.js is part of the NextJs framework to configure Webpack internals. It's mainly used to support SASS and CSS stylesheets

  • now.json is used as configuration for Now serveless deployments

Build

Installing tools

The following steps are intended to be an orientation guide, if you are not experienced with this you will need to learn a bit more about it from Google :)

Once you have all tools installed

  • Open a console/terminal and run npm install to install application dependencies
  • Finally run npm run dev to start the application development server.

If everything goes fine, you should see the messages in the terminal telling you that the server is running on http://localhost:3000

Custom server

The Express server included in server.js can be started with the following command:

npm run dev-server

You will need this only for custom routes on development.

Building for production

The following will compile the application for the production:

npm run build

This will generate the compiled files under the folder .next

Finally, you can run the application with the command

npm run start

As a shorthand for both previous command, you can run the following:

npm run preview

Usage

Adding new pages

You can add new page by adding a new folder in pages

For example, components/MyComp

Then create there a new file named MyComp.js with the component code.

For example,

import React, { Component } from 'react';
import ContentWrapper from '@/components/Layout/ContentWrapper';

class MyComp extends Component {
    render() {
        return (
            <ContentWrapper>
                <div className="content-heading">MyComp Title</div>
                {/* view content here */}
            </ContentWrapper>
        );
    }
}

export default MyComp;

Important: The '@' in the path is used as an alias that resolves to the root folder of the project.

Routing

Now you have added a file under pages folder, NextJs will create automatically a route using the file name. Example

http://site.com/MyComp

If you want to have a lowercase route, just rename your files. Also, when you add a folder here it will be used as part of the route. For example, the path /pages/mycomp/mycomp will be loaded with the route http://site.com/mycomp/mycomp

See how to use Link component to customize routes.

Finally, you need to add an entry to the sidebar in order to provide a link for the new page created.

To do that, add in components/Layout/Menu.js a new menu entry with the information for your menu.

Example

const Menu = [
    /* heading elements */
    {
        // text to show
        heading: 'Heading Text',
        // translation key
        translate: 'sidebar.heading.KEY'
    },
    /* menu items elements */
    {
        // text to show
        name: 'Item Text',
        // class name to show icon
        icon: 'icon-class',
        // route path (not used on items with submenu)
        path: 'routepath',
        // translation key
        translate: 'sidebar.item.KEY',
        // shows a Badge right next to the text
        label: { value: 10, color: 'success' },
        // list of submenu items
        submenu: [{
                ... Same format as menu items
            }
    }
    ...

Vendor assets

Vendor assets can be installed using the Node Package Manager (npm). The following command shows how to install a package so it becomes part of the project

npm install package-name --save

If you don't add the --save flag, the package won't be saved as a dependecy in package.json and it will be only available in your local project.

Once the command ends, files will be available in the node_modules folder and ready to be imported into your application code.

If the package is used globally you can import files using the file pages/_app.js

To use a package locally in your new component, you can import the files directly into the code in your component file.

Note about jQuery

Although jQuery can be imported at the top of any component, plugins that uses jQuery must be imported using require in the componentDidMount class method, because all jQuery plugins must be executed at the client and they usually fails to load at the server rendering stage.

import React, { Component } from 'react';
import $ from 'jquery';

export default class Comp extends Component {

    componentDidMount() {
        // jQuery plugin
        require('some-jquery-plugin');

        // Safe to init the plugin here
        // $(some-element).plugin(...);
    }
    ...
}

Layout settings

Layout can be changed via the following classes. This classes are applied to a custom component named SettingsProvider (components/Layout/SettingsProvider.js)which acts a wrapper to catch layout changes and inject classes that modifies the layout.

Class name Description
.layout-fixed Makes navbars become fixed while the user can scroll only content
.layout-boxed Limits the width of the main wrapper element
.aside-collapsed Condenses the sidebar showing only icons
.aside-collapsed-text Condenses the sidebar showing icons and Text
.aside-toggle used internally for mobiles to hide the sidebar off screen
.offsidebar-open used internally to display the offsidebar component (formally the right sidebar)

Layout setting are connect to Redux store, and reducers can be found in file store/reducers/settings.reducer.js

Translation

The translation system is custom made and the source code can be located in file components/Common/Translate.js.
This module is able to load translations files (dictionaries) located under folder static/locales and display a text associated to 'key' for the current active language.

The use is pretty similar to react-i18n but without all extra features such module provides.
This means, the custom Translate module provides,

  • a 't' method to get the text associated to a key and a language,
  • a 'changeLanguage' method to set another languages
  • a Provider to integrate with other React components and
  • a Trans component as an alternative to the 't' function.
import { withTranslation, Trans } from '@/components/Common/Translate';

class Comp extends Component {
    render() {
        // Translate 'translate.key' using Trans component
        <Trans i18nKey='translate.key'></Trans>
        // or translate using t function
        {this.props.t('translate.key')}
    }
}

export default withTranslation(Comp)

Note that the 't' method is automatically available in components wrapped with the withTranslation HOC . At the same time, the <Provider>component is necessary in order to allow language changes to take effect in all places where a translation is required. You can see example of this use in file pages/_app.js

Just for reference, this module exposes the following:

Name Type Description
store Object Contains loaded dictionaries
setDict method Set a dictionary for a specific language
getDict method Get a dictionary for specific language
fetchStore method Fetch a dictionary for specific language defined in JSON format
translateKey method Returns the translated text for given key and interpolates values in 'params'
Provider Component Component provider to pass down context to child components
withTranslation HOC Provide 'changeLanguage' and 't' methods
Trans Component Translate a given key

Why a custom made module?

The first option for translations was the i18next module but the main issue with the i18next ecosystem is that the static loader relies on 'fs' module and this is not suitable for serverless applications to be deployed to Now, which is the intention of this template. The react-i18next and next-18next are a bit hard to implement for SSR (considering the previous limitation with the 'fs' module) and their need of an Express middlewares also doesn't allow to easily create serverless applications. According to this issue, there's not intention to bring support for Now deployments.

The decision to prioritize a serverless implementation is because it has more limitations and considerations, than when using a custom server where one have more control on server side capabilities (e.g. routes)

So in other words, the custom Translate module was made to support serverless deployment, it does pretty much the same job proving different texts in different languages but in a simple manner and can be extensible adding more features. And, at the same time, if using a custom server it can be easily replaced with a more robust Next based implementation without too much hassle following the instructions in the next-i18next readme.

Themes

All available theme are managed by REDUX to determine which of the available themes is currently active.

To inject a theme, it's used a custom component ThemesProvider (components/Layout/ThemesProvider.js) which is connected to Redux store. Reducers can be found in file store/reducers/themes.reducers.js

Default Theme

To set a default theme open you need to set it in the initialState of themeReducer in file store/reducers/themes.reducers.js like this:

  const initialState = {
      path: 'themes/theme-e.css'
  }

Note that selected themes are automatically saved to localStorage, if you set one as default, but there's another already saved, the saved one will be used instead.

Dynamic Layouts

By default, the layout used for any page is the one displayed for the admin views, with sidebar, header, offsidebar and content.
As it's done for the user pages like login, register, etc. add a static 'Layout" property to the class or function (if using stateless component) with the layout component, like this:

import React, { Component } from 'react';
import BasePage from '@/components/Layout/BasePage';

class Login extends Component {
    ....
}
// Set a different layout for this page
Login.Layout = BasePage;

export default Login;

Horizontal Layout

You can enable the horizontal menu using a similar approach

import React, { Component } from 'react';
import BaseHorizontal from '@/components/Layout/BaseHorizontal';

class MyPage extends Component {
    ....
}
// Set a different layout for this page
MyPage.Layout = BaseHorizontal;

export default MyPage;

To set the Horizontal layout for all pages at once, edit and replace the default used in file pages/_app.js

Deploy serverless with Now

The template is ready to work as a serverless application and to deploy with Now v2. To do that follow this steps:

  • Go to Now website and download the now-cli

  • Edit next.config.js and uncomment the target: serverless entry

  • Edit now.json and configure your application name and routes

  • Once ready, run the command "now" in the root folder of the project, if everything goes fine you will start seeing the output and when done your app will be deployed and ready online.

For more information please visit Now documentation: https://zeit.co/docs/v2/deployments/basics/

Custom server

A custom server is included in the file server.js
This file contains mainly the definition of routes that are handled via Express. This is necessary because Next automatically will take control only over generated routes from pages directory.

For example, consider the route:

<Link href="/user/login" as="/login" />

When you use a different route path, like using the "as" property in the Link component, on the client it will be handled by the browser, and Next will display "/login" in the URL bar. But, if you hit reload, on the server Next will try to find a file /pages/login which is associated with /login route (the real path is /pages/user/login) so here is where a custom server is needed to catch those "special routes" and return the right file.
A similar situation happens when you need to use route params, like is used for the "Forum" routes.

When using Now deployments, this routes are configured using now.json file.

Seed Project

This project is an application skeleton. You can use it to quickly bootstrap your ReactJS webapp projects and dev environment for these projects. The seed app doesn't do much and has most of the feature removed so you can add them as per your needs just following the full-features version as example.

This project is provided in order to start with the template using a different approach. Usually, templates will come with all features working and you need to remove them one by one in the way you don't need them. With the seed project you can start adding custom features and others from the full project to make grow your app. Since the files and structure is the same for the full and seed versions, you can save time using comparison tools that allows to apply changes from full features project into the seed project.

Redux

Redux is used mainly to manage application settings to change the layout options and themes. It's organized by files per actions and reducers so you can easily remove them or add more according to your application needs.

Redux files are placed under src/store folder. In this folder you will find the following files:

+---store
    +---actions
    |   +---actions.js
    |   +---setting.actions.js
    |   +---theme.actions.js
    +---reducers
    |   +---reducers.js
    |   +---setting.reducer.js
    |   +---theme.reducer.js
    +---persisted.store.cookie.js
    +---store.js
    +---with-redux-store.js

The file store.js is where the Redux is initialized, this files is used by the HOC withReduxStore with must be used to wrap the App component defined in pages/_app.js Removing that will remove Redux from the project. The Provider component from Redux is also necessary to pass the store into the components tree.

Notice that for each folder, there's a main file (actions/actions.js, reducers/reducers.js) which are the ones who import and exports each scripts to the rest of the application.

// pages/_app.js

import App, { Container } from 'next/app';
// Redux support
import { Provider } from 'react-redux';
import withReduxStore from '../store/with-redux-store';
...
class MyApp extends App {
    ....
}
export default withReduxStore(MyApp);

We have created reducers for settings which contains state of the current layout options, like fixed, boxed, sidebar collapsed, and also the toggle for sidebar offcanvas on mobile and the sidebar user block. And for themes, which contains a single state with the current theme selected.

By using custom components (ThemesProvider and SettingsProvider) connected to the Redux store is possible to inject the changes when the states change.

Container components where omitted from the store structure as they don't match exactly our requirements but you can add them easily and import into your custom components.

Persisted states

In order to save the settings selected and the active theme, we have included a custom script named persisted.store.cookie.js. This script automatically saves the entire application state to the browser cookies.

The use of cookies makes possible to send the saved state to the server when the a pages is requested, this way we can initialize the store in the server and apply the changes according to the initial state provide, allowing to first server side render of the component with Redux states already applied.

Since not all states are suitable to be saved, we have used a black list that prevents some state to be saved, for example, the state used to toggle the sidebar on mobile.

// store/persisted.store.cookies.js
export const saveState = state => {
    try {
        let stateFilter = JSON.parse(JSON.stringify(state)); // deep clone
        ['offsidebarOpen', 'asideToggled', 'horizontal'] // states which we don't want to persist.
            .forEach(item => delete stateFilter.settings[item]);
        const rawState = JSON.stringify(stateFilter);
        saveCookie(REDUX_STORAGE_KEY, rawState, 100);
    } catch (err) {
        console.log(err);
        // Ignore write errors.
    }
};