MeteorJS - Role Based Routing example

This is a sample project / guide on setting up a Meteor app with top level routing based on the role of the user.

Overview

The app based on the following concepts.

  • Users can be ‘normal’ users, or can have an admin role.
  • The app has top level parts (routes).
    • Public area: Can be accessed by anyone, in order to authenticate
    • App area: This is the normal usage of the app that can be used both types of users.
    • Admin Portal area: This area should be accessible only by users with admin role.
  • In addition:
    • Public area is available both on web and cordova platforms
    • Admin Portal is availble only in Web platform. admin users that login in Cordova environment are redirected to App area.
    • App area is only available on Cordova platform. If any user tries to access it on Web, the user is redirected to a ‘block’ page.

The app can be adapted to switch various other cases, like adding more roles or removing the above restrictions.

Packages used

The app is based on the following atmosphere.js packages.

  • alanning:roles for managing roles of users
  • kadira:flow-router for routing.

It is based on React but it can easily be adapted to Blaze templates.

Router init after subscriptions complete

A basic concept that it demonstrates is how to ‘stall’ the router initialization until the needed subscriptions are finished. This is because we need specific information fetched from database before making a routing decision.

In the example below, the router initialization waits for 3 subscriptions to finish.

  • The me subscription: It fetches our full user document with all necessary fields.
  • The Roles.subscription: This subscription is created by the alanning:roles package. In this case is not necessary since we have the me subscription. It is put here as demo. In only the roles field is needed to make routing decision, the me subscription can be skipped.
  • The companies subscription: This demonstrates the case were there is need for other subscriptions needed for the routing decision. Again it is put here as demo, because it is not actually used in the routing decision.

Finally before the router is initialized, a check is performed in case it already has been initialized.

routes.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FlowRouter.wait();

Tracker.autorun(() => {
const mesub = Meteor.subscribe('me');
const companiesSubscription = Meteor.subscribe('companies');

const shouldInitializeRouter =
mesub.ready() &&
Roles.subscription.ready() &&
companiesSubscription.ready() &&
!FlowRouter._initialized; // eslint-disable-line no-underscore-dangle

if (shouldInitializeRouter) {
FlowRouter.initialize();
}
});

The top-level areas

We app is ‘partitioned’ in 3 areas.

  • Public Area: which will contain pages accessible by unauthenticated users. In this example, it has only one route. It is the /login which renders the Login page.
  • App area (/app): This area offers the App’s functionality for normal users.
  • Admin area (/admin): This area is the Admin Portal of the app. It should be accessible only by users with admin role.

These areas are translated in routes definitions as FlowRouter groups.

routes.js

1
2
3
4
5
6
7
8
9
10
11
12
13
const publicRoutes = FlowRouter.group({
name: 'publicRoutes'
});

const appRoutes = FlowRouter.group({
prefix: '/app',
name: 'appRoutes',
});

const adminRoutes = FlowRouter.group({
prefix: '/admin',
name: 'adminRoutes',
});

And the corresponding routes (only one page per area in this demo.)

routes.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
publicRoutes.route('/login', {
action() {
mount(LoginPage);
}
});

appRoutes.route('/', {
action() {
mount(DummyPage, { title: 'App for Users' });
}
});

adminRoutes.route('/', {
action() {
mount(DummyPage, { title: 'Portal for Admins' });
}
});

The root ‘/‘ path

The app’s entry point will be the root (/) path. So this path is not part of any group (or area). Is it used for entry point decision. It lands the user on the proper area based on the user’s authentication status and role.

router.js

1
2
3
4
5
FlowRouter.route('/', {
action() {
FlowRouter.go(getRouteBasedOnRole());
}
});

role-routing.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export const getRouteBasedOnRole = () => {
const user = Meteor.user();

// land unauthenticated users on login page
if (!user) {
return '/login';
}

// land Admins on Admin Portal
if (Roles.userIsInRole(user, ROLES.ADMIN)) {
return '/admin';
}

// No ROLE means normal user. Land on App area
return '/app';
};

Route Guards

So the app’s entry will land user normally. But in case of web, user can directly enter the URL paths to land on. Protection is needed on pages from unauthorized access (authentication status and roles) and not-supported access (area app vs platform; remember that certain parts of the app should be available only on specific platforms).

The approach here is to write guard functions that will check specific conditions and block access to the route, by redirecting to the user elsewhere.

These functions will be bound to route groups by using the triggersEnter hooks of FlowRouter.

One guard function per case is created and then it is ‘hooked’ into the necessary routes group. Care must be taken in the order in which the guards are applied. It is important for delivering the guarding logic (review again the requirements at the start of this document).

If all the guards are executed and no redirect happens, then the route will be entered.

So the route groups now become:

routes.js

1
2
3
4
5
6
7
8
9
10
11
12
const appRoutes = FlowRouter.group({
prefix: '/app',
name: 'appRoutes',
triggersEnter: [authGuard, blockAppUsageInWeb]
});


const adminRoutes = FlowRouter.group({
prefix: '/admin',
name: 'adminRoutes',
triggersEnter: [authGuard, adminRoleGuard, blockAdminPortalInCordova]
});

The route guards in detail

The triggersEnter hooks are calling the guard functions with two parameters.

  • context which contains the current route properties
  • redirect. A function that can be used to redirect the browser to a new route.

Notice, that when a user is ‘kicked out’ of a route, the user is redirected to root route (/) so the proper routing decision is taken. This way the routing logic does not have to be implemented in each guard. Care must be taken though, so no ‘loops’ are created.

authGuard

If the user is not authenticated, is kicked out of the route

1
2
3
4
5
export const authGuard = (context, redirect) => {
if (!Meteor.userId()) {
redirect('/');
}
};

adminRoleGuard

If the user has not the admin role, is kicked out of the route.

1
2
3
4
5
6
7
export const adminRoleGuard = (context, redirect) => {
const user = Meteor.user();

if (!Roles.userIsInRole(user, ROLES.ADMIN)) {
redirect('/');
}
};

blockAdminPortalInCordova

This guard is used in Admin Portal group and after the user has been evaluated for the admin role. So if the admin user is in mobile platform, then redirect to App area, in order to use the app as normal user.

1
2
3
4
5
export const blockAdminPortalInCordova = (context, redirect) => {
if (Meteor.isCordova) {
redirect('/app');
}
};

Note: The user is not redirected to / because the root logic will land him again in this area.

blockAppAreaInWeb

This guard is used in App area. If the platform is web, the user is not allowed to use. The user is not redirected to / because the logic will land him again here. Thus the user is redirected in a ‘block’ page, to be informed that the usage is not allowed in web-platform.

1
2
3
4
5
6
export const blockAppAreaInWeb = (context, redirect) => {
// App (as normal user) is not allowed in Web
if (!Meteor.isCordova) {
redirect('/ban-access');
}
};

Meteor + React + F7 Integration Guide

https://github.com/killerchip/meteor-react-f7

This is project is both a starter template and an integration guide, for Meteor-React with Framework7-React.

In addition it incldues a preconfigured setup of ESLint and Prettier, for code quality and code style enforcement in your codebase.

  • Meteor is a full-stack framework for building web and hybrid mobile (web-based) apps, which supports React as its templating solution for front-end. On top of that you can use

  • Framework7 to give your mobile apps a close to native look and feel.

How to use this project

You can directly clone the project and start working on it.

1
git clone https://github.com/killerchip/meteor-react-f7.git
1
2
3
meteor run
meteor run android-device
meteor run ios-device

Manual integration and explanation

If you want to manually build this project, here are the steps taken:

Create a meteor project using CLI

1
meteor create --react <my-project>

Add necessary Headers in the project

client/main.hml

1
2
3
4
5
<head>
<title>meteor-react-f7</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
</head>

You need to add the viewport meta tag to make the app responsive and display correctly in mobile device.

Adding ESLint and Prettier

ESLint will suggest best practices for Javascript, Meteor, and React.

Prettier will suggest a unified code style (display) to make it easier to read.

Husky will enforce those rules upon commiting changes.

For more details you can refer to the following Posts:

An overview of steps are the following:

Install the necessary packages:

1
2
3
4
5
6
meteor npm install --save-dev babel-eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-plugin-jsx-a11y eslint-import-resolver-meteor eslint @meteorjs/eslint-config-meteor


meteor npm install --save-dev --save-exact prettier

meteor npm install --save-dev eslint-plugin-prettier eslint-config-prettier

Then make sure the following configuration files are put in your project’s root directory. They are a suggested configuration for those modules.

Finally put the following scripts in package.json, to be able to use these tools from command-line.

1
2
3
4
5
scripts: {
"lint": "node_modules/.bin/eslint . --ext .js,.jsx",
"pretty": "node_modules/.bin/prettier './**/*.{js,jsx,scss}'",
"pretty:write": "node_modules/.bin/prettier --write './**/*.{js,jsx,scss}'",
}

Add the following in the pacakge.json file to allow automatically prettier fixes when you commit your code.

1
2
3
4
"husky": {
"hooks": {
"pre-commit": "precise-commits --no-verify"
}

My personal preference is to do manual edits with my VSCode plugin.

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

Framework7 Integration

Install the following F7 packages.

1
meteor npm install framework7 framework7-react

According to documentation, you must initialize Framework7.

client/main.jsx

1
2
3
4
import Framework7 from 'framework7';
import Framework7React from 'framework7-react';

Framework7.use(Framework7React);

Also the Framework7 CSS must be imported. In case of Meteor app the easiest way to do it is just to import it in a file, and the Meteor builder will bundle it in the app.html file.

client/main.jsx

1
import '../node_modules/framework7/css/framework7.min.css';

Now you need to initialize the App component of framework7. So rename the default App React component to something else (e.g. Root).

client/main.jsx

1
2
3
4
5
import Root from '/imports/ui/Root.jsx';

Meteor.startup(() => {
render(<Root />, document.getElementById('react-target'));
});

And in the newly renamed Root.jsx file, initialize the framework7 App component.

imports/ui/Root.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React from 'react';
import Hello from './Hello.jsx';
import Info from './Info.jsx';

/* globals App, View, Page, Navbar */

const Root = () => (
<App
params={{
name: 'Meteor React F7',
id: 'net.killerchip.meteor-react-f7',
statusbar: {
androidBackgroundColor: '#FFF',
iosBackgroundColor: '#FFF'
}
}}
>
<View main>
<Page>
<Navbar title="MeteorJS - React - F7" />
<div>
<h1>Welcome to Meteor!</h1>
<Hello />
<Info />
</div>
</Page>
</View>
</App>
);

export default Root;

Framework7’s components are included by Meteor’s builder in the bundle itself. So you can’t (actually you should not) import the framework7 react components (e.g. App, View, etc.). But because ESLint will complain, you can use the /* globals */ directive to declare them. Consider them as a replacement for import statements for these components.

Finally a cordova-plugin-statusbar is added, and the correspoing App options to set its background color.

imports/ui/Root.jsx

1
2
3
4
statusbar: {
androidBackgroundColor: '#FFF',
iosBackgroundColor: '#FFF'
}

Setting up Prettier along ESLint

In my previous article “ESlint configuration on Meteor-React project” I showed you how you can install and configure ESLint for automatically enforcing code-style to your Javascript code. With Prettier you can take this one step further and have Prettier do the actual formatting part of your code style.

This is an article on how you can easily integrate Prettier into your project in combination with ESLint.

Prettier vs ESLint

ESLint is concerned both for code quality and code formatting styles.

Prettier is only concerned with the formatting styles of your code.

So eventually you can keep ESLint for code quality and use Prettier for formatting your code.

Install Prettier

You should install Prettier pinned to a specific version. The creators most probably will introduce stylistic changes between releases. So you don’t want to automatically update your Prettier package, before you check it.

1
npm install --save-dev --save-exact prettier

Integrating with ESLint

By integrating we mean that each time you run ESLint, it will automatically run Prettier checks and report any non-compliances along with the normal ESLint issues. For this you will have to install eslint-plugin-prettier.

1
npm install --save-dev eslint-plugin-prettier

Then bind the plugin to ESLint via ESLint configuration.

.eslintrc.json

1
2
3
4
5
6
{
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
}
}

Disable ESLint formatting rules

Since now you will be using Prettier for code styling (not quality), you should disable the styling rules of ESLint so you don’t have conflicts between the packages. You don’t want ESLint to complain about styles that were fixed by Prettier.

The easiest way to do this, is to install eslint-config-prettier.

1
npm install --save-dev eslint-config-prettier

and in .eslintrc.json

1
2
3
{
"extends": [..., "prettier"]
}

Re-enabling some ESLint Rules

Now Prettier has disabled styling rules from ESLint but in addition some other quality rules, that MAY conflict. So you may want to re-enable these rules again in ESLint. In addition there are some configurations/rules between the two packages that need to be configured properly so they work together.

I urge you to read the special rules documentation and re-enable rules and make adjustments to your needs.

ESLint rules can be re-enabled in .eslintrc.json config file. Prettier configuration can be stored in .prettierrc.json file.

My preffered .eslintrc.json (see curly, max-len, no-confusing-arrow rules):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
{
"extends": [
"@meteorjs/eslint-config-meteor",
"prettier"
],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
],
"no-console": "off",
"import/prefer-default-export": "off",
"curly": [
"error",
"all"
],
"max-len": [
"error",
{
"code": 80,
"ignoreUrls": true
}
],
"no-confusing-arrow": [
"error",
{
"allowParens": false
}
]
}
}

And my preffered .prettierrc.json file:

1
2
3
4
{
"tabWidth": 4,
"singleQuote": true
}

Calling Prettier manually

You can setup an npm script to scan your code and prettify it:

package.json

1
2
3
4
5
{
"scripts": {
"pretty:write": "node_modules/.bin/prettier --write './**/*.{js,jsx,scss}'"
}
}

It write directly to the files and make changes. So better to have committed you changes before you ‘prettify’ them. So you can revert back.

If you want to exclude specific folders/files from the script, you can setup a .prettierignore file:

.prettierignore

1
2
dist/
node_modules/

Git Hooks

A recommended approach is to ‘hook’ Prettier with git so you can prettify your files when you commit your changes.

There are various solutions to this and you can explore them here: https://prettier.io/docs/en/precommit.html

Precise-Commits

The precise-commits package will trigger Prettier only for lines that you changed during commit. This can be a very convinient approach for large projects with an established code base, which you want to prettify in stages.

You can install it along with husky package to hook it into git.

1
npm install precise-commits husky@next --save-dev

And then hook it to git:

package.json

1
2
3
4
5
6
7
{
"husky": {
"hooks": {
"pre-commit": "precise-commits"
}
}
}

Each time you commit your changes, the script will run and prettify your changes (thus needing an additional commit).

VSCode plugin

If you are using VSCode editor you can alwasy install the appropriate plugin so you prettify your code while you are writing it.

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

ESlint configuration on Meteor-React project

The following are simple instructions / recommendation on setting up ESlint rules for a Meteor/React project.

It is based on the recommendations of Meteor Guide with additional modifications that found out working for my cases.

The lint configuration is based on Airbnb lint configuration and React-plugin.

Install ESLint

You can install ESLint for your project by issuing the command:

1
meteor npm install --save-dev babel-eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-meteor eslint-plugin-react eslint-plugin-jsx-a11y eslint-import-resolver-meteor eslint @meteorjs/eslint-config-meteor

Configure ESLint

I prefer the usage of .eslintrc.json file in the root folder of my project, in order to create/override my own rules. So the basic contents of the file are:

.eslintrc.json

1
2
3
4
5
6
{
"extends": "@meteorjs/eslint-config-meteor",
"env": {
"meteor": true
}
}

It extends the rules/configuration installed by the npm packages, plus enables global variables recognition of Meteor environment.

Additional rules

You can override specific rules globally by modifying the .eslintrc.json file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"rules": {
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}

The above example sets indentation to 4 spaces (not tabs), uses single quotes '' and requires a semicolon ; at the end of each javascript statement.

Check via command line

You can check your code via command line. It can help even better if you setup a script in your package.json file:

package.json

1
2
3
scipts: {
"lint": "node_modules/.bin/eslint . --ext .js,.jsx"
}

So now invoking the following command, will scan all your .js and .jsx files.

1
npm run lint .

Disabling rules

ESLint rules have specific names, and you can disable them on various levels… from files to specific lines. You can disable/enable them by adding special comments inside your code files.

Disabling ESLint for entire file

1
2
3
/* eslint-disable */

alert('foo');

Disabling ESLint for block of code

1
2
3
4
5
/* eslint-disable */

alert('foo');

/* eslint-enable */

Disabling Specific rules

1
2
3
4
5
6
/* eslint-disable no-alert, no-console */

alert('foo');
console.log('bar');

/* eslint-enable no-alert, no-console */

Disabling for specific lines

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
alert('foo'); // eslint-disable-line

// eslint-disable-next-line
alert('foo');

/* eslint-disable-next-line */
alert('foo');

alert('foo'); /* eslint-disable-line */

alert('foo'); // eslint-disable-line no-alert

// eslint-disable-next-line no-alert
alert('foo');

alert('foo'); /* eslint-disable-line no-alert */

/* eslint-disable-next-line no-alert */
alert('foo');

Find more information at: ESlint website

Editor plugins

You can also use plugins to help you directly when you are writing your code with editors.

My personal favorite is VSCode and the ESLint plugin.

Find more instructions for other popular editos in Meteor Guide.

React - A Beginner's Cheatsheet

This a collection of cheatsheets for beginners in React. It is based on React JS Documentation. Keep it handy for when you want quickly to refer on how to do React stuff.

Find Github project with examples here…

Contents:

Starting a Project

Minimal HTML Page

Dowanlod the following page and start building:
Single File Example

It is for tests only. Not for production environment.

Rendering components

single-file-example.html

In HTML:

1
2
3
<body>
<div id="react-root"></div>
</body>

In JSX file:

1
2
3
4
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById('root');
);

Defining Components

Functional Components

functional-components.html:

Define a component as a function:

props is the properties passed to our component.

1
2
3
4
5
6
7
8
function Welcome(props) {
return <h1>Hello, {props.name}</h1>
}

ReactDOM.render(
<Welcome name="John Doe" />,
document.getElementById('root')
);

Class components

class-components.html:

1
2
3
4
5
class Goodbye extends React.Component {
render() {
return <h1>Goodbye, {this.props.name}</h1>
}
}

Assignable to variables

assignables.html:

1
2
3
4
5
6
const welcomeSirComponent = <Welcome name="Sir" />;

ReactDOM.render(
welcomeSirComponent,
document.getElementById('root')
);

Local State

states.html

Defining state

State can be initialized in constructor:

1
2
3
4
5
6
7
8
class TimeCounter extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
date: new Date()
};
...

Reading State

1
2
3
4
<div>
<h1>Counting: {this.state.counter}</h1>
<p>{this.state.date.toLocaleTimeString()}</p>
</div>

Updating state

Use setState:

1
2
3
this.setState({
date: new Date()
});

Relying on previous state to update state

In this case do not rely directly to this.state object. Use the setState with arrow function parameter:

1
2
3
this.setState(
(prevState, props) => ({counter: prevState.counter + props.incValue})
);

Event Handling

event-handling.html

Simple Case

Use preventDefault and not return false to prevent bubbling up the event.

You attach an event handler function and not a string in the onClick and the other event handler properties.

1
2
3
4
5
6
7
8
9
10
11
function ALink() {
function handleClick(e) {
e.preventDefault();
alert ('You clicked button: A');
console.log('Event', e);
}

return (
<a href="#" onClick={handleClick}>Link A</a>
);
}

Event Handling in Class / bind method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class BLink extends React.Component {

constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
}

handleClick(e) {
e.preventDefault();
alert('You clicked button: B');
console.log(e);
console.log(this);
}

render(){
return (
<a href="#" onClick={this.handleClick}>Link B</a>
);
}

}

Event Handling / Arrow functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CLink extends React.Component {

//Arrow function as event handler
handleClick = (e) => {
e.preventDefault();
alert('You clicked button: C');
console.log(e);
console.log(this);
};

render(){
return (
<a href="#" onClick={this.handleClick}>Link C</a>
);
}

}

Event Handling / Arrow Callbacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DLink extends React.Component {

handleClick = (e) => {
e.preventDefault();
alert('You clicked button: D');
console.log(e);
console.log(this);
};

//Directly Arrow function in callback statement
render(){
return (
<a href="#" onClick={(e) => this.handleClick(e)}>Link D</a>
);
}
}

Passing Arguments with Arrow Callbacks

Event argument has to be passed to the event handler, along with other custom parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DLink extends React.Component {

handleClick = (e, name) => {
e.preventDefault();
alert(`You clicked button: D and the name is ${name}`);
console.log(e);
console.log(this);
};

render(){
return (
<a href="#" onClick={(e) => this.handleClick(e, 'John Doe')}>Link D</a>
);
}
}

Passing Arguments with Callback binding

The Event argument will be automatically be passed after any custom parameters in the .bind operation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ELink extends React.Component {

handleClick (name, e) {
alert(`You clicked button: E and the name is ${name}`);
console.log(e);
console.log(this);
};

render(){
return (
<a href="#" onClick={this.handleClick.bind(this, "Jane Doe")}>Link E</a>
);
}
}

Passing Up State

pass-up-events.html

You can pass up state from children components to parent. The parent component passes an even handler in props and the child component calls it up.

Parent passes event handler to child

1
<NameInput onChange={ (name)=> {this.setState({name})} } />

Child calls the parent handler on state changes

1
<input type="text" onChange={(e) => {this.props.onChange(e.target.value)}}></input>

Children Components

children-props.html

A component can handle children components by having direct access to their JSX via the props.children property.

A component can have ‘content’:

1
2
3
4
<Bordered>
<p>Dear John</p>
<p>I wish you Happy Birthday!!</p>
</Bordered>

And the Bordered component can access and manipulate the content:

1
2
3
4
5
6
7
8
9
10
11
12
class Bordered extends React.Component {
render(){
return (
<div>
Letter:
<div className="fancy-border content">
{this.props.children}
</div>
</div>
);
}
}

Multiple Children Components

You can pass them as custom JSX props.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class NewsPaperPage extends React.Component {
render() {

const columnLeft = (...);
const columnMiddle = (...);
const columnRight = ();

return (
<PaperPage
columnLeft={columnLeft}
columnMiddle={columnMiddle}
columnRight={columnRight}
/>
);

}
}

And the PaperPage Component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class PaperPage extends React.Component {
render(){
return (
<div className="paper-page">
<div className="column column-left">
{this.props.columnLeft}
</div>
<div className="column column-middle">
{this.props.columnMiddle}
</div>
<div className="column column-right">
{this.props.columnRight}
</div>
</div>
);
}
}

Responsive Layout Example

The project here demonstrates basic elements of a Responsive Design Layout as a reference.

It follows the mobile first approach. For small screens all columns are displayed on their own lines.
As the width grows, they are grouped in lines, but they are also re-ordered.



See the main.css for more details.

Tricks that are used:

  • Flexbox containers
  • Column widths are expressed in relative (%) widths
  • Order can change using flex ‘order’ property.
  • After the last breakpoint the layout stays the same. We just add margins to left and right

Responsive Side Menu

This is an example of how to create a side menu, without using any framework but pure HTML/CSS/JS

Find here my full project here

In our page we define a nav section which will be our menu.

1
2
3
4
5
6
7
8
9
10
11
<body>

<nav class="menu">
<h2>Menu</h2>
<p>Click outside the menu to close it.</p>
</nav>

<main class="page-content">
<!-- Page content goes here -->
</main>
</body>

In CSS we initially set the main page content height to 100%:

1
2
3
4
5
6
7
8
9
html,
body,
main {
/* We put "main" as 100% so
* when the user clicks anywhere
* on the page, the menu closes
*/
height: 100%;
}

We also do the same for the menu

1
2
3
4
5
6
7
8
9
nav {
background-color: darkblue;
color: white;
padding: 0.5em;

height: 100%;
width: 300px;
margin: 0;
}

Then we position the menu in its normal position and hide it (initially)

1
2
3
4
5
6
7
8
9
10
11
nav {
/* We set the side menu normal position initially */
position: absolute;
left: 0;
top: 0;

/* Here we hide the side menu by positioning it off canvas */
-webkit-transform: translate(-308px, 0);
transform: translate(-300px, 0);
transition: transform 0.3s ease;
}

Then by assigning the class open to the nav menu we bring it into display

1
2
3
4
nav.open {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}

Bootstrap Starter Templates

This is a collection of starter template HTML pages with Bootstrap 4.1.1 capabilities.

Use these pages to start a new page / project. It contains the necessary references to bootstrap related files.

Find the code at the correspoding GitHub Project

Bootstrap Full

bootstrap.full.html

This is the full package that includes the Bootstrap framework and additional Javascript files required.

See Bootstrap documentation for more details.

Bootstrap Full - No popper.js

bootstrap.full.no-popper.html

This is the full package that includes the Bootstrap framework and additional Javascript files required, except for Popper.

Use this if you do not want to use:

  • Tooltips and popovers for displaying and positioning (also requires Popper.js)
  • Dropdowns for displaying and positioning (also requires Popper.js)

See Bootstrap documentation for more details.

Bootstrap Full - CSS Only

bootstrap.full.css-only.html

This is the full package that includes the Bootstrap framework - CSS file only.

Use this if you do not want to use:

  • Alerts for dismissing
  • Buttons for toggling states and checkbox/radio functionality
  • Carousel for all slide behaviors, controls, and indicators
  • Collapse for toggling visibility of content
  • Dropdowns for displaying and positioning (also requires Popper.js)
  • Modals for displaying, positioning, and scroll behavior
  • Navbar for extending our Collapse plugin to implement responsive behavior
  • Tooltips and popovers for displaying and positioning (also requires Popper.js)
  • Scrollspy for scroll behavior and navigation updates

See Bootstrap documentation for more details.

Bootstrap - Grid only

bootstrap.full.grid-only.html

Use this if you do not want to use Grid and Flexbox only capabilities:

See Bootstrap documentation for more details.

Bootstrap - Reboot only

bootstrap.full.reboot-only.html

Use this if you do not want to use the Reboot only capabilites:

See Bootstrap documentation for more details.

ngx-model Recipes

Code recipes, tips-n-tricks and more for Angular’s ngx-model.

ngx-model offers simple state management, one way data flow, multiple model support and immutable data exposed as RxJS Observable.

The following recipes build on the core ngx-model and add commonly needed functionality. It focuses on hosting model as immutable classes that have both properties and methods, and not simple objects/interfaces.

Contents:

Find the full project here: https://github.com/killerchip/ngx-model-recipes/

From Mockup Design to HTML code

Here follows a checklist / guide on how to create HTML5 code from a mockup design.

Start from here:

And finish here:

Checklist

Page Layout

  • Identify Boxes and sub-boxes
  • Position Boxes / Use Grid system
  • Put (placeholder) content

Repeat the above steps progressively per ‘level’… from outer boxes to inner boxes. (See example below).

Box Layout

  • Use a CSS normalizer
  • Identify margins & paddings
  • Identify box styles
    • background color/style
    • border color/style
  • Identify content text styles
    • Font family, weight, color, style, etc…
    • Text positioning, overflow, etc…

Details…

Please visit the Github Project: From Mockup to Code