Direkt zum Inhalt

Example Integration of Drupal 8, Angular 2, Gulp, PostCSS, Bootstrap 3 SASS

The recent release of Drupal 8 and Angular 2 made me think, “What will I achieve if I used a combination of these technologies in conjunction with my favorite tools?”

You'll probably ask me, “Why have you decided to use such a combination?”

Well, besides the known advantages, from my point of view Drupal 8 and Angular 2 are promising, flexible, easily-scalable tools, and each have a big community and knowledge base.

Drupal 8 will be used to implement the back-end mechanisms of our application because it has extensive possibilities to work with third-party services. Moreover, it can easily establish connection with Angular 2 by means of RESTful Web Services API, which is now available in a drupal core.

I use Gulp for acceleration and optimization of the process. In fact, it acts as a link between other tools since it has plugins for SASS and PostCSS. It also performs automatization functions for repetitive tasks such as compilation SASS into CSS, TypoScript into JavaScript, library files generation and general folder structure.

PostCSS will serve as a tool which gives us an ability to create a custom set of extensions, meeting the needs of our work with CSS.

I use Bootstrap because it contains all necessary patterns and just because I like it. ;)

But, the main point of this article is not to describe technologies, its advantages or my preferences. I want to share my experience of integration of these tools. Hopefully it might be useful for someone. I won't describe the installation process of Drupal because it’s not so difficult and there is already  a lot of information on the internet.
I guess it is high time to move directly to the point.

An adjustment is carried out assuming that the node and npm have been already installed.

In order to install all necessary dependencies we can use the command:

npm i -g <dependency>

So, now we can install the following:

  • gulp
  • gulp-cli
  • typings
  • typescript
  • ts-node

Integration of Bootstrap 3 and Drupal 8.

You can ask, “What is the complexity? One needs only to download and install a theme. Easy!”

But, it's not that simple because the Bootstrap theme for Drupal is based on LESS, by default. Therefore, we need to make it work with SASS first.

First, let’s create the subtheme:

  1. Download theme and put it into ./themes/contrib, (I usually split them up like module directories.)
  2. Create the directory subtheme. Copy ./themes/contrib/bootstrap/starterkits/less to the  ./themes/custom.
  3. Rename the directory subtheme with a preferable name. For example: ‘less’ -> ‘my_theme’.
  4. Copy template files form ./themes/contrib/bootstrap/templates to ./themes/custom/my_theme/templates
  5. Create folders in theme root:
    - for images ./themes/custom/my_theme/images 
    - for javascript files ./themes/custom/my_theme/js 
  6. Replace THEMENAME for the name of the theme ‘my_theme’:
    ./themes/custom/my_theme/config/install/THEMENAME.settings.yml
    ./themes/custom/my_theme/config/schema/THEMENAME.schema.yml
    ./themes/custom/my_theme/THEMENAME.libraries.yml
    ./themes/custom/my_theme/THEMENAME.starterkit.yml
    ./themes/custom/my_theme/THEMENAME.theme 
  7. Open the file ./themes/custom/my_theme/config/schema/my_theme.schema.yml and replace strings contained THEMENAME to ‘my_theme’.
  8. In the filename ./themes/custom/my_theme/my_theme.starterkit.yml replace ‘starterkit’ to ‘info’, then open it and replace strings contained THEMENAME to ‘my_theme’. Also, don’t forget to change name and description.
  9. Finally, the subtheme has been created. Now, you can activate it.

Now, force our theme to work with SASS.

  1. Remove all unnecessary files, namely, the folder ./themes/custom/my_theme/less
  2. Create folders in the root of the theme:
    ./themes/custom/my_theme/bootstrap  
    ./themes/custom/my_theme/sass
  3. Download Official Sass port of Bootstrap
  4. Go to the downloaded directory  (I got this with name: ‘bootstrap-sass-master’) and copy the following files:
    form assets/fonts/bootstrap to ./themes/custom/my_theme/bootstrap/fonts
    form assets/javascripts/bootstrap to ./themes/custom/my_theme/bootstrap/js
  5. Go and pick any theme you like, for example, 'cosmo', then download the 3 files _bootswatch.scss, _variables.scss and bootstrap.css
  6.  Rename those files and put them into:  ./themes/custom/my_theme/sass/cosmo/cosmo_bootswatch.scss
    ./themes/custom/my_theme/sass/cosmo/cosmo_variables.scss
    ./themes/custom/my_theme/css/cosmo_bootstrap.css
  7. Create two files for custom code:
    ./themes/custom/my_theme/sass/style.scss
    ./themes/custom/my_theme/sass/variables.scss
  8. For file variables.scss add the code: 
    @import "cosmo/cosmo_variables";
  9. For file style.scss add the code:

     @import "variables";
  10. In order to make fonts work, we need to make a replacement in the file cosmo_bootstrap.css. Replace all matches of “../fonts” to “../bootstrap/fonts”.

  11. Open file ./themes/custom/my_theme/my_theme.libraries.yml and change its content so that it looks like this:

    global-styling:
      css:
        theme:
          css/cosmo_bootstrap.css: {}
          css/style.css: {}
    
    bootstrap-scripts:
      js:
        bootstrap/js/affix.js: {}
        bootstrap/js/alert.js: {}
        bootstrap/js/button.js: {}
        bootstrap/js/carousel.js: {}
        bootstrap/js/collapse.js: {}
        bootstrap/js/dropdown.js: {}
        bootstrap/js/modal.js: {}
        bootstrap/js/tooltip.js: {}
        bootstrap/js/popover.js: {}
        bootstrap/js/scrollspy.js: {}
        bootstrap/js/tab.js: {}
        bootstrap/js/transition.js: {}
  12. Flush Drupal cache - Done!

Setting up Angular 2 for TypeScript.

I didn't aim to make an application separate from Drupal, so, this solution cannot be called completely ‘headless’. I decided to make it a bit unusual, namely embed Angular directly into the Bootstrap theme and use its template system. This might be useful for developers who are doing both frontend and backend, as well as, for those who use Angular only partially, without creating one page application.

  1. Create Angular application folder:  
    ./themes/custom/my_theme/app
  2. We need to define a page which will initialize application.
    For "Hello world" example let’s take the file: ./themes/custom/my_theme/templates/system/html.html.twig and just add several lines of code after tag <body>:
    <my-app>Loading...</my-app>
    <script>
    System.config({
      baseURL: './themes/custom/my_theme/',
      packages: {
        app: {
          format: 'register',
          defaultExtension: 'js'
        },
        angular2: {
          defaultExtension: 'js'
        },
        rxjs: {
          defaultExtension: 'js'
        }
      },
      map: {
        app: 'app',
        angular2: 'node_modules/angular2',
        rxjs: 'node_modules/rxjs',
        typescript: 'node_modules/typescript/lib/typescript.js'
      }
    });
    System.import('app/build/main').then(null, console.error.bind(console));
    </script>
  3. Create configuration files in the root of theme:
    a) ./themes/custom/my_theme/typings.json
    {
      "ambientDependencies": {
        "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#4de74cb527395c13ba20b438c3a7a419ad931f1c"
      }
    }
    b) ./themes/custom/my_theme/package.json
    {
      "name": "my_theme",
      "version": "1.0.0",
      "license": "ISC",
      "scripts": {
        "postinstall": "typings install"
      },
      "dependencies": {
        "angular2": "2.0.0-beta.17",
        "systemjs": "0.19.31",
        "es6-promise": "^3.2.1",
        "es6-shim": "^0.35.1",
        "reflect-metadata": "0.1.2",
        "rxjs": "5.0.0-beta.6",
        "zone.js": "^0.6.12"
      },
      "devDependencies": {
        "typescript": "^1.8.10",
        "typings":"^1.3.0",
        "del": "^2.2.0"
      }
    }
    c) ./themes/custom/my_theme/tsconfig.json
    {
      "compilerOptions": {
        "outDir": "app/build",
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false
      },
      "exclude": [
        "node_modules",
        "typings/main",
        "typings/main.d.ts"
      ]
    }
  4. Create TypeScript files:
    a) ./themes/custom/my_theme/app/app.component.ts
    import {Component} from "angular2/core";
    import {OnInit} from "angular2/core";
    
    @Component({
        selector: 'my-app',
        template: '<h1>My First Angular 2 App</h1>'
    })
    export class AppComponent implements OnInit {
    
        ngOnInit() {
            console.log("Application component initialized ...");
        }
    }
    b) ./themes/custom/my_theme/app/main.ts
    import {bootstrap} from "angular2/platform/browser";
    import {AppComponent} from './app.component';
    bootstrap(AppComponent);

Gulp adjustment.

  1. Open file ./themes/custom/my_theme/package.json
    To the 'scripts' object add commands we will use further:
    "clean":   "gulp clean"     // command serves to clear compiled files
    "compile": "gulp compile"   // command serves to compile files
    "build":   "gulp build"     // command serves to generate libraries and compile files

    To the 'devDependencies' object add required plugins:

    "gulp":              "^3.9.1"  // gulp plugin
    "gulp-typescript":   "^2.12.0" // typescript compiler
    "gulp-autoprefixer": "^3.1.0"  // adds all necessary browser prefixes
    "gulp-cssbeautify":  "^0.1.3"  // to make our CSS looks nice
    "gulp-postcss":      "^6.1.1"  // allows us to use PostCSS features
    "gulp-sass":         "^2.3.1"  // to compile SASS into CSS
    "gulp-sourcemaps":   "^1.6.0"  // during debug shows a string number in SASS 

    After all the changes, file package.json should be similar to:

    {
      "name": "my_theme",
      "version": "1.0.0",
      "license": "ISC",
      "scripts": {
         "clean": "gulp clean",
         "compile": "gulp compile",
         "build": "gulp build",
         "postinstall": "typings install"
      },
      "dependencies": {
        "angular2": "2.0.0-beta.17",
        "systemjs": "0.19.31",
        "es6-promise": "^3.2.1",
        "es6-shim": "^0.35.1",
        "reflect-metadata": "0.1.2",
        "rxjs": "5.0.0-beta.6",
        "zone.js": "^0.6.12"
      },
      "devDependencies": {
        "typescript": "^1.8.10",
        "typings":"^1.3.0",
        "del": "^2.2.0",
        "gulp": "^3.9.1",
        "gulp-typescript": "^2.12.0",
        "gulp-autoprefixer": "^3.1.0",
        "gulp-cssbeautify": "^0.1.3",
        "gulp-postcss": "^6.1.1",
        "gulp-sass": "^2.3.1",
        "gulp-sourcemaps": "^1.6.0"
      }
    }

     

  2. Create file ./themes/custom/my_theme/gulpfile.js and describe functions to automate repetitive tasks
    var gulp         = require('gulp');
    var postcss      = require('gulp-postcss');
    var sass         = require('gulp-sass');
    var autoprefixer = require('gulp-autoprefixer');
    var sourcemaps   = require('gulp-sourcemaps');
    var cssbeautify  = require('gulp-cssbeautify');
    
    var del = require("del");
    var tsc = require("gulp-typescript");
    var tsProject = tsc.createProject("tsconfig.json");
    
    /**
     * Default task invokes css compiler
     */
    gulp.task('default', ['css']);
    
    /**
     * Compile css files
     */
    gulp.task('css', function () {
      var processors = [
        autoprefixer
      ];
      return gulp.src('./sass/style.scss')
        .pipe(sourcemaps.init())
        .pipe(sass().on('error', sass.logError))
        .pipe(postcss(processors))
        .pipe(cssbeautify({indent: '  '}))
        .pipe(sourcemaps.write('./maps/'))
        .pipe(gulp.dest('./css/'));
    });
    
    /**
     * Watch on scss file and compile automatically
     */
    gulp.task('watch', function() {
      gulp.start('css');
      gulp.watch('./sass/style.scss', ['css']);
    });
    
    /**
     * Remove angular build directory.
     */
    gulp.task('clean', function (cb) {
      return del(["app/build"], cb);
    });
    
    /**
     * Compile TypeScript sources and create sourcemaps in build directory.
     */
    gulp.task("compile", function () {
      var tsResult = gulp.src(['node_modules/angular2/typings/browser.d.ts','app/*.ts'])
        .pipe(sourcemaps.init())
        .pipe(tsc(tsProject));
    return tsResult.js
      .pipe(sourcemaps.write("./maps/"))
      .pipe(gulp.dest("app/build"));
    });
    
    /**
     * Copy all resources that are not TypeScript files into build directory.
     */
    gulp.task("resources", function () {
      return gulp.src(["app/*", "!**/*.ts"])
        .pipe(gulp.dest("app/build"))
    });
    
    /**
     * Copy all required libraries into angular lib directory.
     */
    gulp.task("libs", function () {
      return gulp.src([
          'es6-shim/es6-shim.min.js',
          'systemjs/dist/system-polyfills.js',
          'angular2/bundles/angular2-polyfills.js',
          'systemjs/dist/system.src.js',
          'rxjs/bundles/Rx.js',
          'angular2/bundles/angular2.dev.js',
          'angular2/bundles/router.dev.js'
        ], {cwd: "node_modules/**"}) /* Glob required here. */
        .pipe(gulp.dest("app/lib"));
    });
    
    /**
     * Build the angular project.
     */
    gulp.task("build", ['compile', 'resources', 'libs'], function () {
      console.log("Building the project ...")
    });
    

That’s all! Now, to initialize all the tools we need to go to the theme root folder and run: 

npm install

After everything is done, we need to launch command: 

npm run clean & npm run build

Then, clear Drupal cache and go to the homepage to check the result. There, we should see the text generated by Angular application:

My First Angular 2 App

In the next article I’ll write about how to export data from drupal and how you can use it in Angular application.

Über den Autor:
Dmitry Antonenko
Dmitry Antonenko
Teamleitung / Drupal

Dmitry ist ein Drupal Urgestein mit jahrelanger Erfahrung und leitet unser Drupal-Team als Senior Entwickler und Software-Architekt.

Wie können wir Sie unterstützen?

Kontaktieren Sie uns

Copyright © 2018 BUZZWOO! GmbH & Co. KG