1

I am working on a application originally created with backbone and jQuery, however due to client requirement, new modules are built with angular. Routing of the application is handled with backbone route and we have successfully integrated angular modules.

The actual problem is, I need to retrieve the current instance of a module in angular and execute a function from the controller of that module based on actions handled by a backbone controller.

Here is what my angular module and controller looks like:

//In chat.module.js
( function () {
angular
    .module( 'chat.module', [] );
})();

//In chat.controller.js
(function () {
angular
        .module('chat.module')
        .controller('chat.controller', ['profileFactory', '$filter', '$q', '$timeout', 'Position', 'Chat', chat]);

function chat(profileFactory, $filter,  $q, $timeout, Position, Chat) {
    var vm = this;
    vm.initChatFlag = false;

    vm.initChat = initChat;
    vm.setInformation = setInformation;


    function setInformation() {
        //handle business logic here
    }

    ...

In backbone, the module is created as follows:

        chatmodule: function () {
        var self = this;
        var element = angular.element(document.querySelector('#modalCallback'));
        var chat = angular.element(document.querySelector('#chatModule'));
        var isInitializedChat = chat.injector();

        var isInitialized = element.injector();
        if (!isInitialized) {
            angular.bootstrap($('#modalCallback'), ['app']);
        }
        if (!isInitializedChat) {
            angular.bootstrap($('#chatModule'), ['app']);
        }

        //TODO: chat.controller.setInformation() get access to fields like chat.controller.initChatFlag etc

The main app module is defined thus:

    (function(){
    angular
        .module('app',[
            'callback',
            'ui.bootstrap',
            '720kb.datepicker',
            'ngLocale',
            'directives.module',
            'interceptor',
            'directive.loading',
            'angularUtils.directives.dirPagination',
            'blog.module',
            'profile.module',
            'filters.module',
            'chat.module',
            'ui.toggle',
        ]);
})();
2
  • When using multiple framework, using a well-defined and isolated API is the way to go (like in JC Ford's answer). Get all the business logic out of the frameworks if possible. Commented Mar 28, 2018 at 16:54
  • Thanks for the recommendation, I find using redux and react more convenient for scenarios like this one. I'll take that into account and propose changes to my team. Commented Mar 28, 2018 at 20:36

2 Answers 2

1

The AngularJS $injector is where a lot of the magic happens, so if you expose that outside of the AngularJS code you can hook it up to non-AngularJS code like the following:

//A simple AngularJS service:
app.service('myService', function() {
  this.message = "This is my default message.";
});

//Expose the injector outside the angular app.
app.run(function($injector, $window) {
  $window.angularInjector = $injector;
});

//Then use the injector to get access to the service.
//Make sure to wrap the code in a `$apply()` so an 
//AngularJS digest cycle will run
function nonAngularEventHandler() {
  angularInjector.invoke(function(myService, $rootScope) {    
    $rootScope.$apply(function() {
      myService.message = "Now this is my message."
    });
  });
}

Edit: Alternatively, simplify the call like so.

//Instead of exposing the $injector directly, wrap it in a function
//which will do the $apply() for you.
app.run(function($injector, $window, $rootScope) {

  $window.callInMyAngularApp = function(func) {
    $rootScope.$apply(function() {
      $injector.invoke(func);
    });
  }

});

//Then call that function with an injectable function like so.
function nonAngularClick() {
  callInMyAngularApp(function(myService) {    
      myService.message = "Now this is my message."
  });
}

//And remember if you're minifying, you'll want the minify-safe
//version of the injectable function like this
function nonAngularClick() {
  callInMyAngularApp(['myService', function(myService) {    
      myService.message = "Now this is my message."
  }]);
}

Update: (last one I promise!) The above will work fine, but you might want to consider exposing a well-defined API instead of a generic injectable interface. Consider the following.

//Now I have a limited API defined in a service
app.service("myExternalApi", function($rootScope, myService) {
  this.changeMyMessage = function(message) {
    $rootScope.$apply(function() {
      myService.message = message;
    });
  };
});

//And I just expose that API
app.run(function($window, myExternalApi) {
  $window.myExternalApi = myExternalApi;
});

//And the call from outside of angular is much cleaner.
function nonAngularClick() {
  myExternalApi.changeMyMessage("Now this is my message.");
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the reply. My function is inside a controller and I wont to call the same function without having to rewrite it again. This is not clear to me from your response. Do I create lets say 'chat.service.js' and put methods to expose inside that file, then call chat.service in the controller or call controller in chat.service? Kind of lost
AngularJS is designed to be state-driven, so if you can move the functionality to a service and let the controller just respond to changes in its state, then the approach in my answer could work. Otherwise, the best approach would probably be using the event system. Use the "External API" pattern above to encapsulate broadcasting events from $rootScope. Listen for those events in your controller.
Thanks a lot for the recommendation. I'll consider that and restructure my project accordingly.
0

I was able to get access to the controller using answer from this post - https://stackoverflow.com/a/21997129/7411342

    var Chat = angular.element(document.querySelector('#chatModule')).scope();

    if(!Chat) return;

    if(Chat.chatCtrl.initChatFlag) {
        Chat.chatCtrl.setInformation();
    }else{
        console.log('Chat has not been initialized');
    }

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.