Full-Stack Engine

Creating a web app in 5 minutes with Angular 5 and Materialize CSS

There are several front-end frameworks out there, each one with its pros and cons and based on a different design paradigm.
In this guide, I pick one of the most popular front-end framework based on the material guidelines, which offers several nice and fancy components that allows you to create a modern, good looking with enhanced UX web application. The same I used for my wedding site.

See also on Angular

We're going to create a wedding page with a story and information sections using a fancy component called parallax (you can take a look at this post to see parallax in action).
The results look like the following image,

Peek-2017-11-21-06-19

The complete sources of this project are found here

For this project, we'll be using angular 5 and materialize CSS as the primary packages. However, materialize is a general framework that can be alongside any SPA JS library, however in its pure form uses JQuery to perform dynamic behaviors. So we'll also need another package that makes the glue between them, that's what angular2-materialize does for us. Once we have the basic setup, we can focus on the content and customizing styles to make it look even better.

Let's begin!. We'll first create a new project using angular cli, so let's install it globally as follows,

npm install -g @angular/cli

Now, we can create a new project like this from the command line,

ng new my-cool-project

By changing "my-cool-project" by the name of your preference.

Next, we'll install materialize and angular2-materialize,

npm install materialize-CSS --save
npm install angular2-materialize --save

angular2-materialize has a couple of dependencies on hammerjs and jquery for mobile and dynamic behaviors; we include them with,

npm install jquery@^2.2.4 --save
npm install hammerjs --save

We need to add the javascript files for these dependencies inside the apps section and then inside scripts subsection in file .angular-cli.json. The complete file looks like this,

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": {
    "name": "angular-materialize-starter"
  },
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "styles.css",
        "../node_modules/materialize-css/dist/css/materialize.css"
      ],
      "scripts": [
        "../node_modules/jquery/dist/jquery.js",
        "../node_modules/hammerjs/hammer.js",
        "../node_modules/materialize-css/dist/js/materialize.js"
      ],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ],
  "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "src/tsconfig.spec.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "e2e/tsconfig.e2e.json",
      "exclude": "**/node_modules/**"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  },
  "defaults": {
    "styleExt": "css",
    "component": {}
  }
}

The last step in the setup is to import Materialize module in the main app.module as folows,

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MaterializeModule } from 'angular2-materialize';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    MaterializeModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now it's time to put our hands on the application itself. Let's add these lines to app.component.html,

<nav class="white">
  <div class="nav-wrapper">
    <a *ngIf="wedding" href="#" class="page-title black-text">{{wedding.name | uppercase}}</a>

    <a materialize="sideNav" [materializeParams]="sidenavParams" [materializeActions]="sidenavActions"
    data-activates="nav-mobile" class="button-collapse hide-on-large-only">
      <i class="material-icons grey-text">menu</i>
    </a>

    <ul class="right hide-on-med-and-down">
      <li *ngFor="let menuItem of menuItems" routerLinkActive="active"><a class="black-text" [routerLink]="menuItem.route">{{ menuItem.name | uppercase }}</a></li>
    </ul>

    <ul id="nav-mobile" class="side-nav">
      <li>
        <a ><i class="material-icons" (click)="close()">close</i></a>
      </li>
      <li *ngFor="let menuItem of menuItems" routerLinkActive="active"><a class="black-text" [routerLink]="menuItem.route" (click)="close()">{{ menuItem.name | uppercase }}</a></li>
      <div class="divider">

      </div>
    </ul>
  </div>
</nav>

<router-outlet></router-outlet>

The previous lines work as the navigation menu in the top of the screen with a couple of routes for wedding story and information about the date and place where our party takes place. Note that there is a "ul" tag with class side-nav that works as a navigation side panel for mobile, as well as a button with materialize="sideNav" that shows a menu button on mobile to display that navigation panel.

Moreover, app.component.ts

import { Component, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
  wedding = { name: 'our wedding' };
  menuItems: MenuItem[];

  constructor() {
    this.sidenavActions = new EventEmitter<any>();
    this.sidenavParams = [];

    this.menuItems = [
          { name: "Our Story", route: "/our-story" },
          { name: "When & where", route: "/when-where" }
      ];
  }

  close() {
    this.sidenavActions.emit({ action: 'sideNav', params: ['hide'] });
  }

  sidenavActions: EventEmitter<any>;
  sidenavParams: any[];
}

export interface MenuItem {

  name: string;
  route: string;
}

This component adds the menu items for "story" and "when & where" sections in the main menu, as well as the sidenav actions, neede to open and close the navigation panel on the mobile viewport

Let's now add the corresponding CSS sheet for fancy font style,

.nav-wrapper {
  padding-left: 6em;
  padding-right: 6em;
}

.page-title {
  font-size: 17px;
  font-family: 'Lato', sans-serif;
  letter-spacing: .11em;
  line-height: 1em;
  text-rendering: optimizeLegibility;
}

Let's move now on adding the two section components, in the command line enter the following,

ng g component our-story
ng g component when-where

Then add RouterModule module and the corresponding routes in app.module.ts,

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MaterializeModule } from 'angular2-materialize';

import { AppComponent } from './app.component';
import { OurStoryComponent } from './our-story/our-story.component';
import { WhenWhereComponent } from './when-where/when-where.component';

@NgModule({
  declarations: [
    AppComponent,
    OurStoryComponent,
    WhenWhereComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot([
      { path: '', redirectTo: 'our-story', pathMatch: 'full' },
      { path: 'our-story', component: OurStoryComponent },
      { path: 'when-where', component: WhenWhereComponent }
    ]),
    MaterializeModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

After this, we can modify "our-story",

<div class="parallax-container">
  <p class="white-text right-align header">
    lobortis feugiat vivamus
  </p>
  <div class="valign-wrapper h100">
    <h1 class=" white-text overlay center-align">
      Lorem ipsum dolor sit amet
    </h1>
    <div class="parallax grey lighten-1" materialize="parallax">
      <img src="https://images.unsplash.com/photo-1470320691330-ae8e9288fb77?auto=format&fit=crop&w=1950&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D">
    </div>
  </div>
</div>

<div class="section white valign-wrapper">
  <div class="row container center-align">
    <h5 class="story-header">pretium quam vulputate dignissim suspendisse</h5>
    <br />
    <p class="grey-text text-darken-3 lighten-3">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Diam vel quam elementum pulvinar etiam non. Nibh sed pulvinar proin gravida hendrerit lectus a. Non nisi est sit amet facilisis.
      Tristique senectus et netus et malesuada fames.</p>
  </div>
</div>

<div class="parallax-container">
  <p class="white-text right-align header">
  </p>
  <div class="valign-wrapper h100">
    <h1 class=" white-text overlay center-align">
          etiam tempor orci eu lobortis
        </h1>

    <div class="parallax grey lighten-1" materialize="parallax">
      <img src="https://images.unsplash.com/photo-1470322450444-8ccc64987ccf?auto=format&fit=crop&w=1950&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D">
    </div>
  </div>
</div>
<div class="parallax-container">
  <p class="white-text right-align header">
  </p>
  <div class="valign-wrapper h100">
    <h1 class=" white-text overlay center-align">
          morbi tincidunt augue interdum velit
        </h1>

    <div class="parallax grey lighten-1" materialize="parallax">
      <img src="https://images.unsplash.com/photo-1492176861288-6b481cfad893?auto=format&fit=crop&w=1350&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D">
    </div>
  </div>

This markup is the response to show the parallax components with fancy overlayed headers as shown in the animation at the beginning. To work, we need some styles,

.parallax-container {
  height: 500px;
}

.h100 {
  height: 100%;
}

.header {
  font-family: 'Lato', sans-serif;
  font-size: 1em;
  font-style: italic;
  font-weight: bold;
  margin: 10px;
  text-rendering: optimizeLegibility;
}

.overlay,
.parallax-text {
  /*font-size: 80px;*/
  font-family: 'Lato', sans-serif;
  letter-spacing: .11em;
  font-size: 2.25em;
  text-rendering: optimizeLegibility;
}

@media (min-width: 993px) {
  .parallax-container {
    height: 900px;
  }
  .overlay,
  .parallax-text {
    /*font-size: 80px;*/
    font-family: 'Lato', sans-serif;
    letter-spacing: .11em;
    font-size: 7em;
    text-rendering: optimizeLegibility;
  }
  .header {
    font-family: 'Lato', sans-serif;
    font-size: 2.25em;
    font-style: italic;
    font-weight: bold;
    margin: 10px;
    text-rendering: optimizeLegibility;
  }
}

.overlay {
  width: 100%;
}

.section {
  height: 400px;
}

.story-header {
  font-family: 'Lato', sans-serif;
  font-size: 28px;
}

p {
  font-family: 'Fira Sans', sans-serif;
  font-size: 16px;
}

For "when-where" we can re-use our-story style, modify when-where.component.ts like this,

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-when-where',
  templateUrl: './when-where.component.html',
  styleUrls: ['../our-story/our-story.component.css']
})
export class WhenWhereComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

So, we just add the html contents,

<div class="parallax-container">
  <p class="white-text right-align header">
    bibendum arcu vitae elementum curabitur
  </p>
  <div class="valign-wrapper h100">
    <h1 class=" white-text overlay center-align">
      viverra aliquet eget sit amet
    </h1>
    <div class="parallax grey lighten-1" materialize="parallax">
      <img src="https://images.unsplash.com/photo-1497042915201-daf0dd6fdc09?auto=format&fit=crop&w=1950&q=60&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D">
    </div>
  </div>
</div>

<div class="section white valign-wrapper">
  <div class="row container center-align">
    <h5 class="story-header">sollicitudin ac orci phasellus egestas</h5>
    <br />
    <p class="grey-text text-darken-3 lighten-3">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Leo duis ut diam quam nulla. At quis risus sed vulputate odio ut enim blandit. Nullam eget felis eget nunc lobortis mattis
      aliquam faucibus. Consectetur libero id faucibus nisl tincidunt.</p>
  </div>
</div>

That's it!. We have our wedding page ready for our guests to get an invitation. Thanks for reading this post. See you next time, take care!.

Author image
Costa Rica
Passionate Software Developer with full-stack development experience.