3

for a given webapp -for example if facebook- , i want to split into different modules

each module are only depended on core, and unaware of anyother component -so that in future if i want to add or remove a feature all i have to do is develope this feature with its html and js and directives what ever inside it self, and just add dependency in core module and im good to go.

for example app will contain 1. wall "path = "/components/wall.js" 2. user profile " /components/profile.js" 3. chat "/components/chat.js"

all need to know current logged in user details, so may be the core module will handle this by exposing user details to $rootScope. "/core.js"

then entire app user must be logged in to access, so i will let core.js handle all the authentication

EDIT TO NARROW DOWN Question

var core = angular.module("core", ['core.wall']);

var wall = angular.module("core.wall", []);

Now core DEPENDE on wall, yet in my design , wall is the one that depends on core. yet at same time, core routing is altered by core.wall since wall should declare its own routing rules.

is this dependency injection doable ?

5
  • This is too broad of a topic for StackOverflow and you're asking too many different questions. There are many Angular style guides online, but ultimately these things come down to preference. Commented Oct 1, 2015 at 19:15
  • @AnidMonsur sorry i was a bit brain storming first. i added a small edit that conclude the question in 4 lines ! Commented Oct 1, 2015 at 19:20
  • This looks like circular dependency and Angular is likely to return an error if attempted. Commented Oct 1, 2015 at 19:27
  • @Boaz exactly, that why i tagged question circular-reference, so how does people organize code in such situation ? Commented Oct 1, 2015 at 19:29
  • 1
    There are a lot of similarities to this approach and John Papa's hot towel, but he injects each module into the main app module. You might be able to get some insight by taking a look at that. Commented Oct 1, 2015 at 19:31

1 Answer 1

1

so i didnt get any answers but after lots of playing around i figured out a solution.

Angularjs DI (dependency injection) for modules works as follows.

  1. when you bootstrap a module -automatic using ng-app="module" or manually by triggering angular.bootstrap(document,"module"); the first thing that happens is that all its dependencies are looped and all config blocks run.

//i created a github example https://github.com/alzalabany/angular-material-modular-starter/

example :

angular.module('zalabany',['app.core','app.wall','app.blog']);
angular.bootstrap(document, ['zalabany']);
//1-->app.core config run, app.wall config run, app.blog config run, zalabany.config runs last
//2-->then .run blocks run in same order.

so since we are trying to be modular in nature. the core as i explained my question should depend on no other app module yet all other modules depends on it. so to do this, i used an intermediate module, that link them together.

Final code. -will upload git soon-

///Linking module, sole function is to connect all modules sharing same $rootScope and making sure system loads in correct order
angular.module('zalabany',['app.core','app.wall','app.blog']);

//I'm the Core.js i depend on external libs only. i'm not aware of my own system modules.
angular.module('app.core', ['ui.router', 'ngAnimate', 'toastr', 'ngMaterial','ngMdIcons'])
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {
    ///http interceptor in case 403,401 on any request, redirect to login right away.
    $httpProvider.interceptors.push(function($q, $rootScope, $injector, $timeout, $window) {
        var toastr, $state, $http;
        //timeout is used to prevent circuler dependency error !
        $timeout(function() {
            toastr = $injector.get('toastr');
            $http = $injector.get('$http');
            $state = $injector.get('$state');
        });
        return {
            responseError: function(rejection) {
                if (rejection.data && rejection.data.hasOwnProperty('message')) {
                    toastr.error('request failed. try again later');
                }
                if (rejection.status === 401 || rejection.status === 403) {
                    console.log('rejected and redirecting', rejection);
                    $state.go('login');
                }
                return $q.reject(rejection);
            }
        };
    });

    $urlRouterProvider.otherwise("/login");

    $stateProvider
    .state('login', {
        url: "/login",
        templateUrl: "modules/core/login.html"
    });

    console.log('im config core');

})

.controller('loginCtrl', function($scope,$user,$rootScope){//remember $user is available every where
    var self=this;
    this.username = $user.username;


    if($user.token && $user.id){
        //validate token by sending get to auth.
        $http.defaults.headers.common["auth-token"] = $user.token;
        $http.defaults.headers.common["auth-uid"] = $user.id;

        $http.get($oauth).then(function(){$rootScope.Login($user);},$rootScope.Logout);

    }

    this.login= function(){
        $http.post($oauth,{username:self.username,password:self.password})
            .then(function(r){
                $rootScope.Login(r); //use login method on root to expose it for all modules
            });
    }
})

.run(function($rootScope, $state, $user,$http,$oauth) {
    ///$rootscope is shared between all modules. so i use it for sharing auth data since its just an object.
    $rootScope.$user = $user;

    $rootScope.$homepage = null;
    //default home page of appchild should overide it;
    //all children modules can edit this.


    ///FUNTION 1.
    ///LISTEN FOR ROUTE CHANGE AND PREVENT IF USER IS NOT LOGGED IN
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {

        if (!$rootScope.$user.hasOwnProperty('token') && toState.name !== 'login') {
            console.log('prevented');
            event.preventDefault();
            $state.go('login');
        }
    });


    $rootScope.Login = function(r){
    // login login ...
      $http.defaults.headers.common["auth-uid"] = r.token;
        angular.module('zalabany').value('$user',r);

        console.log('oki lets go hom',$state.go($rootScope.$homepage));
    }

    $rootScope.Logout = function(){
        window.localStorage.clear();
        $state.go('login');
    }

});


///BOOTSTRAP
$(function(){
    $.getJSON('PING API WITH TOKEN FROM LOCALSTORAGE', function(json) {
        $user = json || data;//json is our user record
    }).fail(function() {
        $user=data;
        window.localStorage.clear();
        console.log( "login error" );
    }).always(function() {
        angular.module('app.core')
                .value('$user',$user);
        angular.bootstrap(document, ['zalabany']); //bootstrap the link module
    });
});

from now on to adding new component to my app is as easy as

angular.module('app.wall', ['app.core'])
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {

    $stateProvider
    .state('wall', {
        url: "/wall",
        templateUrl: "modules/wall/wall.html"
    });
})

.run(function($rootScope, $state, $user,$http,$oauth) {
    $rootScope.$homepage = 'wall';//Set homepage to me
    // or i can use
    $rootScope.$homepage = $rootScope.$homepage || 'wall';//set homepage to me if no other modules declared it it.

   //Push me into the sidebar.
   $rootScope.$sidebar.push({order:1, link:'/wall',title:'Feed',icon:'fa fa-star'});
});

Benefit of this structure :- 1. i hv $user info available for all modules, 2. module push it self to sidebar , 3. routes are declared inside each module in its own config block. 4. adding new module i create new folder, add link to linking module "zalabany in this example". and i'm good to go, with authentication and everything :-)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.