通过路由依赖注入将angularjs应用拆分为多个组件

split angularjs application into components with routing dependency injection

本文关键字:拆分 组件 应用 angularjs 路由 依赖 注入      更新时间:2023-09-26

对于给定的web应用程序-例如如果facebook-,我想分割成不同的模块

每个模块都只依赖于核心和不知道任何其他组件,所以在未来,如果我想添加或删除一个功能,我所要做的就是开发这个功能与它的html和js和指令里面的任何东西,只是添加依赖在核心模块和我很好去。

例如app将包含1. "path = "/components/Wall .js"2. 用户配置文件"/components/profile.js"3.聊天"/组件/chat.js"

都需要知道当前登录的用户详细信息,所以可能是核心模块将通过将用户详细信息暴露给$rootScope来处理这个问题。"/core.js "

那么整个应用用户必须登录才能访问,所以我将让core.js处理所有的身份验证

编辑以缩小问题范围

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

现在core 依赖 on wall,但在我的设计中,wall依赖于core。同时,核心路由被core改变。wall,因为wall应该声明自己的路由规则。

这个依赖注入是否可行?

所以我没有得到任何答案,但经过大量的玩耍,我想出了一个解决方案。

Angularjs模块的DI(依赖注入)的工作原理如下:

  1. 当你启动一个模块-自动使用ng-app="module"或手动触发angular.bootstrap(document,"module");时,发生的第一件事是它的所有依赖项都被循环,所有的配置块都运行。

//我创建了一个github示例https://github.com/alzalabany/angular-material-modular-starter/

示例:

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.

因此,由于我们试图在本质上是模块化的。核心,因为我解释了我的问题应该依赖于没有其他app module,但所有其他模块都依赖于它。为了做到这一点,我使用了一个中间模块,将它们连接在一起。

最终代码。-会尽快上传git -

///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
    });
});

从现在开始添加新的组件到我的应用程序是很容易的

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'});
});

该结构的好处:-1. 我有$user信息可用于所有模块,2. 模块将它自己推到侧边栏,3.路由是在每个模块的配置块中声明的。4. 添加新模块我创建了一个新文件夹,添加链接到链接模块"zalabany在这个例子中"。我准备好了,验证和一切:-)