Search code examples
angularjsangularjs-scopeangular-bootstrap

Bind a controller with a given parent


Using Angular Bootstrap and the $modal service, I create a popup with the following template:

<div class="dialog modal-header" ng-show="header.length > 0">
    <h3 compile="header"></h3>
</div>
<div class="dialog modal-body">
    <div compile="bodyTemplate"></div>
</div>
<div class="dialog modal-footer">
    <button ng-repeat="button in buttons"
            ng-show="$parent.$eval(button.showExpression)"
            ng-class="button.class"
            ng-click="onButtonClick(button)">
        <div compile="button.template"/>
    </button>
</div>

I built a popup service which help set defaults on the scope, as well as load the bodyTemplate from a templateUrl.

// snippit of my popup service
function show(modalOptions, scope) {

    // ....
    // load template from modalOptions.bodyTemplateUrl
    // ....

    var extendedModalOptions = {};
    angular.extend(extendedModalOptions, defaultModelOptions, modalOptions);

    extendedModalOptions.controller = function ($scope, $modalInstance) {
        angular.extend($scope, scope);

        // .... 
        // add to $scope additional properties from the extendedModalOptions 
        // such as the buttons array and loaded bodyTemplate
        // ....
    }
    $modal.open(extendedModalOptions);
}

What I want is to inject a controller to use for the bodyTemplate, like this:

<div class="dialog modal-header" ng-show="header.length > 0">
    <h3 compile="header"></h3>
</div>

<!-- Note the addition of ng-controller here -->
<div class="dialog modal-body" ng-controller="bodyController">
    <div compile="bodyTemplate"></div>
</div>

<div class="dialog modal-footer">
    <button ng-repeat="button in buttons"
            ng-show="$parent.$eval(button.showExpression)"
            ng-class="button.class"
            ng-click="onButtonClick(button)">
        <div compile="button.template"/>
    </button>
</div>

This could work, however, the $parent of the bodyController scope is now the scope given to the $modal controller. I want to use a different parent.

I'd like to be able to use my popup service like this:

// from inside CustomerController
popup.show({
    header: 'Test Popup',
    parentScope: $scope, // the current scope of CustomerController
    bodyTemplateUrl: 'testPopupTemplate.html',
    bodyController: 'TestPopupController as testPopupVM'
});

This is where I'm a bit lost. I think my popup service could create the controller like this:

$scope.bodyController = $controller(extendedModalOptions.bodyController, {
    $scope: extendedModalOptions.parentScope
});

I'm not 100% sure, but I think that gets me the controller with the correct parent. The problem, however, is that the returned value is the new scope object, and not the constructor function.

According to the angular docs on ng-controller, it can only bind to a constructor function.

How can I add the the body controller to the popup template while using the supplied parent scope?


Solution

  • You are trying to invent your own $scope hierarchy while the one provided by Angular is just fine.

    By default, modals opened with $modal service gets new child scope from $rootScope. You can change this by setting a base modal scope explicit, i.e.

    $modal({
        scope: yourBaseScope
        // other modal options
    })
    

    Modal will call yourBaseScope.$new() upon creation and use it. Therefore, it will have access to all variables and funcitons you define in the yourBaseScope.

    Read more about modal's scope in docs.