An example of how to use REQUIRE with AngularJS

This is an old WIP post but I figured I'd just go ahead and make it live. Maybe it'll help someone?

We use module.exports and require to include other JS/HTML/CSS files but I found this AngularJS template which uses import and export default. Let's compare the two:

Using Module.Exports && Require

App.js

The main file which represnts our top-level angular module.

'use strict';

var angular = require('angular');
var viewsModule = require('./views/index.views'); // This is a file which scans and includes everything in the views directory.

// Declare app level module which depends on views, and components
angular.module('aver.OneOfOurApps', [
  viewsModule
])

.config(function($locationProvider, $stateProvider) {
  $locationProvider.html5Mode({
    enabled: true
  });
});

Edit.js

A standard view controller representing a page on the application

'use strict';

var angular = require('angular');
var templateUrl = require('./edit.html');
var topBar = require('../../components/top-bar/top-bar.js');
var limiterCanvas = require('@aver/workbench-components/src/components/limiter-canvas/limiter-canvas.js');
var limiterEditorModule = require('../../yamsf-services/limiter-editor.js');
var limiterDTOCreationModule = require('../../factories/limiter-dto-creation/limiter-dto-creation.js');
var uiScaffolding = require('@aver/ui-scaffolding');

module.exports = angular.module('aver.limiter.edit', [
  topBar, limiterCanvas,
  limiterEditorModule, limiterDTOCreationModule, uiScaffolding
])

.config(function($stateProvider) {

  var editState = {
    name: 'edit',
    url: '/edit/:limiterId',
    controller: 'LimiterEditCtrl',
    controllerAs: 'LimiterEditCtrl',
    templateUrl: templateUrl,
    params: {
      limiterId: null
    }

  };
  $stateProvider.state(editState);

  var editVersionState = {
    name: 'editVersion',
    url: '/edit/:limiterId/:limiterUid',
    controller: 'LimiterEditCtrl',
    controllerAs: 'LimiterEditCtrl',
    templateUrl: templateUrl
  };
  $stateProvider.state(editVersionState);

})

.controller('LimiterEditCtrl', function(
  $scope, $window, $timeout, $location, $stateParams, $state, $document, $log,
  limiterEditorApi, alertService, limiterDTOCreation
) {
  var vm = this;

  vm.limiter = {
    id: ($stateParams.limiterId && $stateParams.limiterId.length > 0) ? $stateParams.limiterId : null
  };

  vm.limiterValidation = {
    validated: false
  };


  vm.$onInit = function(){
    if ($stateParams.limiterUid) {
      limiterEditorApi.getLimiterByUid($stateParams.limiterUid).then(
        setupLoadedLimiter,
        showLimiterVersionLoadError
      );
    } else if ($stateParams.limiterId) {
      limiterEditorApi.getById($stateParams.limiterId).then(
        setupLoadedLimiter,
        showLimiterLoadError
      );
    } else {
      updateLimiterValidation();
    }
  };

  function showLoadLimiterError(limiter){
    alertService.add(
      'danger',
      limiter,
      {timeout: 5000}
    );
  }

  function redirectToWorkbench(){
    $window.location.href = '/episode-workbench/';
  }

  function setLimiterOnView(limiter){
    vm.limiter = limiter;
  }

  function setupLoadedLimiter(limiter) {
    if(limiter.id){
      setLimiterOnView(limiter);
      setLimiterWidth();
    }else{
      showLoadLimiterError(limiter);
      redirectToWorkbench();
    }
    $timeout(updateLimiterValidation);
  }

  function showLimiterLoadError() {
    alertService.add(
      'danger',
      'Limiter with id '+$stateParams.limiterId+' not found',
      {timeout: 5000}
    );
    $location.path('../list');
  }

  function showLimiterVersionLoadError() {
    alertService.add(
      'danger',
      'Limiter version cannot be loaded.',
      {timeout: 5000}
    );
    $location.path('../../list');
  }

  function setLimiterWidth(){
    var width = $document[0].body.clientWidth;
    vm.limiter.width = width;
  }

  function setLimiterValidation(limiterValidation) {
    vm.limiterValidation = limiterValidation;
    vm.limiterValidation.validated = true;
  }

  function showLimiterValidationError() {
    alertService.add(
      'danger',
      'Unable to validate limiter',
      {timeout: 5000}
    );
  }

  function updateLimiterValidation() {
    var limiterDto = limiterDTOCreation.getLimiterDTO(vm.limiter);
    limiterEditorApi.validate(limiterDto, {isBackground: true}).then(
      setLimiterValidation,
      showLimiterValidationError
    );
  }

  angular.element($window).bind('resize', setLimiterWidth);
  $scope.$on('validate-limiter', updateLimiterValidation);

  $timeout(setLimiterWidth);
})

.name;

TopBar.js

'use strict';

var angular = require('angular');
var templateUrl = require('./top-bar.html');

function TopBarController(
  ... Controller dependencies ...
) {

  var ctrl = this;

  ctrl.$onDestroy = function() {
    ...
  };

  ctrl.$onInit = function(){
    ...
  };
}

module.exports = angular.module(
  'aver.limiter.top-bar',
  [... bunch of module depdendencies ...]
)
.component('topBar', {
  templateUrl: templateUrl,
  transclude: true,
  bindings: {
    ... some bindings ...
  },
  controller: TopBarController
})

  .name;

Using Import && Export

App.js

import 'jquery';
import 'bootstrap/dist/css/bootstrap.min.css';
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import AppComponent from './app.component.js';
import Common from './common/common';
import Components from './components/components';
import './styles.scss';

angular.module('myApp', [
  uiRouter,
  Common.name,
  Components.name
])
.directive('app', AppComponent);

/components/components.js

import angular from 'angular';
import Home from './home/home';
import Contact from './contact/contact';
import ContactAddress from './contact/contact.address';
import ContactForm from './contact/contact.form';
import Github from './github/github';
import Forms from './forms/forms';
import New from './new/new';

export default angular.module('app.components', [
  Contact.name,
  ContactAddress.name,
  ContactForm.name,
  Home.name,
  Github.name,
  Forms.name,
  New.name
]);

/src/components/contact/contact.js

import angular from 'angular';
import uiRouter from 'angular-ui-router';
import contactComponent from './contact.component';

let contactModule = angular.module('contact', [
  uiRouter
])

.config(($stateProvider) => {
  $stateProvider
    .state('contact', {
      url: '/contact',
      template: '<contact></contact>'
    });
})

.component('contact', contactComponent);

export default contactModule;

You can see that they're exporting the entire module, but then when they require it in another file, they use it's .name property to reference it as an angular dependency.

Completely unrelated but I also like how they use ES6 classes in their components. I'm not yet sure of the advantage of this versus using functions/objects.

class ContactFormController {
  constructor($scope, $log) {
    this.name = 'Contact Us';
    this.model = {};
    this.schema = {
      type: 'object',
      properties: {
        name: { type: 'string', minLength: 2, title: 'Name', description: 'Name or alias' },
        title: {
          type: 'string',
          enum: ['dr','jr','sir','mrs','mr','NaN','dj']
        }
      },
      'required': [
        'name'
      ]
    };
    this.form = [
      '*',
      {
        type: 'submit',
        title: 'Save'
      }
    ];

    this.onSubmit = function(form) {
      // First we broadcast an event so all fields validate themselves
      $scope.$broadcast('schemaFormValidate');

      // Then we check if the form is valid
      if (form.$valid) {
        $log('form is valid');
      } else {
        $log('form is invalid');
      }
    };
  }
}

export default ContactFormController;