Webpack

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path              = require('path');
const process           = require('process');

const host = '0.0.0.0';
const port = 8080;

module.exports = {
  context: path.join(__dirname, 'src'),
  entry: {
    app: [
      `webpack-dev-server/client?http://${host}:${port}/`,
      './boot'
    ]
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].bundle.js',
    sourceMapFilename: '[name].bundle.js.map'
  },
  module: {
    loaders: [{
      test: /\.scss$/,
      loaders: ['style-loader', 'css-loader?sourceMap', 'sass-loader?sourceMap']
    }, {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        presets: ['es2015']
      }
    }],
  },
  devtool: "source-map",
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Contacts',
      template: 'index.ejs',
      env: process.env
    })
  ],
  devServer: {
    host: host,
    port: port,
    inline: true,
    stats: {
      colors: true,
      chunks: false
    },
    proxy: {
      '/api': {
        target: 'http://api',
        pathRewrite: {'^/api' : ''}
      }
    }
  }
};
            
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <contacts-app></contacts-app>
  </body>
</html>
            

boot.js

import angular from 'angular';
import App from './app.module';

angular.element(document).ready(() => {
  angular.bootstrap(document, [App.name]);
});
            

app.module.js

import angular from 'angular';

import ContactsService from './contacts.service';
import ContactsAppComponent from './contacts-app.component';
import ContactFormComponent from './contact-form.component';
import ContactsListComponent from './contacts-list.component';

export default angular.module('contacts', [])
  .factory('ContactsService', ContactsService)
  .component('contactsApp', ContactsAppComponent)
  .component('contactForm', ContactFormComponent)
  .component('contactsList', ContactsListComponent);
            

contacts-app.component.js

export default {
  template:
`
<contact-form model="$ctrl.model"></contact-form>
<contacts-list></contacts-list>
`,
  controller: ($scope) => {
    $scope.model = {};
  }
};
            

contact-form.component.js

export default {
  template:
`
<form>
  <label for="fname">First name</label>
  <input type="text" name="fname" id="fname" ng-model="$ctrl.model.fname"><br>
  <button type="submit" ng-click="onClickedSubmit($ctrl.model)">
    Add
  </button>
</form>
`,
  controller: ($scope, $window, ContactsService) => {
    $scope.ngModel = $scope.ngModel || {};
    $scope.onClickedSubmit = (model) => {
      console.debug(model);
      ContactsService.create(model);
    };
  },
  bindings: {
    model: '='
  }
};
            

contacts-list.component.js

import './contacts-list.style.scss';

export default {
  template:
`
<div class="contacts-list">
  <h4>Contacts</h4>
  <p ng-if="!contacts.length">You have no contacts.</p>
  <ul ng-if="contacts.length">
    <li class="contact-item" ng-repeat="contact in contacts" ng-bind="contact.fname"></li>
  <ul>
</div>
`,
  controller: ($scope, ContactsService) => {
    $scope.contacts = [];
    ContactsService.retrieve().then((response) => {
      $scope.contacts = response.data;
    });
  }
};

            

contacts.service.js

export default ($http) => {
  return {
    create: (model) => {
      return $http.post('http://0.0.0.0:80/api/contacts', model);
    },
    retrieve: () => {
      return $http.get('http://0.0.0.0:80/api/contacts');
    }
  };
};
            

contacts-list.style.scss

$border-color: #0066EE;

.contacts-list {
  .contact-item {
    border: $border-color solid .1rem;
    padding: .2rem;
  }
}