<!DOCTYPE html>
<html>
  <head>
    <script src="https://code.angularjs.org/1.5.3/angular.js"></script>
    <script src="//unpkg.com/angular-ui-router@1.0.0-alpha.4/release/angular-ui-router.js"></script>
    <script src="//unpkg.com/oclazyload"></script>
    
    <script src="_main.js"></script>
    <script src="_lazyLoadHook.js"></script>
    <script src="home.comp.js"></script>
    
    <title>Plunk demonstrating Lazy Loaded Components</title>
    
    <link rel="stylesheet" href="styles.css" />
    
    <script src="//unpkg.com/ui-router-visualizer"></script>
    <script src="//unpkg.com/d3"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css" />
  </head>

  <body ng-app="app">
      
      <div ui-view>ui-view not populated</div>  
    
      <div class="header">
        Current URL: <b>{{$location.url() }}</b> <br>
        Current State: <b>{{$state.current.name }}</b> <br>
        Current Params: <b>{{$state.params | json }}</b><br>
        Go to state: <state-selector></state-selector>
      </div>
      
      <uir-transitions-view></uir-transitions-view>
      <uir-state-vis-container></uir-state-vis-container>
  </body>
</html>
"use babel";

// Here's a skeleton app.  Fork this plunk, or create your own from scratch.
var app = angular.module('app', ['ui.router', 'lazyHook', 'ui.router.visualizer']);

app.config($stateProvider  => {
  $stateProvider.decorator("views", (state, parentFn) => {
    // Call the parent views: builder. 
    // This thing takes a views: declaration and builds the internal view objects
    let viewsObj = parentFn(state);
    
    angular.forEach(viewsObj, (view, key) => {
      if (view.component) {
        // it has a component: declaration... let's 
        // decorate the results of the real template provider
        
        // First, save a copy of the stock ui-router templateProvider
        let realTemplateProvider = view.templateProvider
        // Then, override the view's templateProvider with our own
        view.templateProvider = [ "$injector", function($injector) {
          // Invoke the original templateProvider to get the template
          let realTemplate = $injector.invoke(realTemplateProvider);
          console.log('original template: ', realTemplate);
          // Use string replacement to add the ui-view
          let newTemplate =   realTemplate.replace("><", " ui-view><");
          console.log('replaced template: ', newTemplate);
          return newTemplate;
        }];
      }
    });
    
    return viewsObj;
  })
})

// Empty config block.  Define your example states here.
app.config(function($stateProvider, $urlRouterProvider) {
  $urlRouterProvider.otherwise('/home')
  
  $stateProvider.state({ 
    name: 'home', 
    url: '/home', 
    component: 'home'
  });
  
  $stateProvider.state({ 
    name: 'lazy', 
    url: '/lazy', 
    component: 'lazy',
    // This file is loaded before the state is entered
    lazyComponent: 'lazy.comp.js'
  });
  
  $stateProvider.state({ 
    name: 'lazy.foo', 
    url: '/foo', 
    component: 'foo',
    // This file is loaded before the state is entered
    lazyComponent: 'foo.comp.js'
  });
});

app.run(function($state, $location, $rootScope) {
  $rootScope.$state = $state;
  $rootScope.$location = $location;
});

console.log("Scripts loading... ");
body { 
  margin-top: 6em;
}
.link { 
    text-decoration: underline;
    color: blue;
}

.link:hover {
    cursor: pointer;
}

.header { 
  position: fixed;
  right: 0;
  top: 0;
  height: 6em;
  width: 100%;
  border-bottom: 1px solid gray;
}

"use babel";
let module = angular.module('app');

let template = `
<h1>lazy foo state loaded</h1>
<div ui-view></div>
`;

let component = {
  template: template,
  bindings: { input: '<' }
}

module.component('foo', component);
# UI-Router: Lazy load components

## Global hook

The global hook in `_lazyLoadHook.js` is invoked before any transition
which will *enter* a state with a `lazyComponent:` property.

The hook returns a promise to lazy load the file 
from the state's `lazyComponent:` property.

The transition waits for the hook's promise to settle, then continues or fails.
"use babel";

let module = angular.module('app');

let template = `
<h1>home state loaded</h1>

<a ui-sref="lazy">lazy</a>
<a ui-sref="lazy.foo">lazy.foo</a>
`;

let component = {
  template: template
}

module.component('home', component);
var lazymodule = angular.module('lazyHook', ['ui.router', 'oc.lazyLoad']);

lazymodule.run(function($transitions, $q, $ocLazyLoad) {
  
  // Register global hook to lazy load components
  
  // This function returns true if a state has a lazy component
  function hasLazyComponent(state) {
    return state.lazyComponent != null;
  }
  
  // This function lazy loads a state's component, 
  // then removes the lazyComponent metadata
  function loadComponent(state) {
      return $ocLazyLoad.load(state.lazyComponent)
          .then(() => delete state.lazyComponent)
  }
  
  // This function lazy loads any lazy components
  // for states being entered.
  
  function lazyLoadComponents($transition$) {
    let promises = $transition$.entering()
        .filter(hasLazyComponent)
        .map(loadComponent)
        
    // return a promise for all the lazy components loading
    return $q.all(promises);
  }
  
  // This criteria checks if any entering states have a lazy component
  let transitionCriteria = { entering: hasLazyComponent };
  
  // Register the global hook. 
  // This hook is run *before* the transition starts.
  // The transition waits for the hooks promise to settle before continuing.
  $transitions.onBefore(transitionCriteria, lazyLoadComponents)
});
"use babel";

let module = angular.module('app');

let template = `
<a ui-sref="home">home</a>

<h1>lazy state loaded</h1>
<a ui-sref=".foo" ui-sref-active="active">foo</a>
<div ui-view></div>
`;

let component = {
  template: template
}

module.component('lazy', component);