1

I've read some similar answers to my question, such as this one though can't translate it to my own requirements - comes from a lack of understanding...

I have a controller:

appControllers.controller('TransactionsCtrl', ['$scope', 'InfiniteScrollingDataService', function ($scope, dataService) {
    // setup our scope properties
    $scope.$root.title = 'Transactions';
    var urlQuery = {
        skip: 0,
        take: 25,
        search: '',
        order: 'DateTimeCreated',
        orderBy: 'desc',
        transactionTypeID: null,
        transactionStatusID: null
    };
    var apiUrl = 'api/transactions';
    $scope.transactions = new dataService(apiUrl, urlQuery);

    $scope.Filter = function (senderParent, type, id) {
        $scope.FilterApplied = true;
        console.log('filter in controller: ' + senderParent + ' ' + type + ' ' + id);
    }
}]);

And I have a directive:

appDirectives.directive('bootstrapListItems', ['$rootScope', function ($rootScope) {

    return {
        restrict: 'A',
        templateUrl: 'bootstrapDropDownItems.html',
        link: function (scope, element, attrs) {

            scope.type = attrs.useGlobaljsonObject;
            scope.parentElement = attrs.id;
            scope.items = [];

            var obj = $rootScope.globalJSON[scope.type];

            for (o in obj) {
                scope.items.push({ key: o, value: obj[o] })
            }
        }       
    }

}]);

And the template for my directive:

<script type="text/ng-template" id="bootstrapDropDownItems.html">

    <li class="active"><a href="#" class="lnkFilterList">- Any -</a></li>
    <li ng-repeat="item in items">
        <a href="#" class="lnkFilterList" ng-click="Filter(parentElement, type, item.key)">{{item.value}}</a>
    </li>

</script>

If I don't isolate the scope of the directive then the controller is called correctly, and i see the console logging out my arguments.

However, I (think) I need to isolate the scope as there will be multiples of this directive on the page.

when I add scope: {} to my directive the controller function is no longer called.

I also tried changing my ng-click to ng-click="$parent.Filter(.....)" - that didnt' seem to work either.

Can anybody please point me in the right direction?

1

2 Answers 2

3

ng-click="$parent.Filter(.....)" isn't working because you have it in an ng-repeat which also creates a (non-isolated) scope. In that case you would have to write

ng-click="$parent.$parent.Filter(.....)"

but don't do that...

You could emit an event in the click event handler and listen for it in your controller.

<script type="text/ng-template" id="bootstrapDropDownItems.html">

    <li class="active"><a href="#" class="lnkFilterList">- Any -</a></li>
    <li ng-repeat="item in items">
        <a href="#" class="lnkFilterList" ng-click="onClick(parentElement, type, item.key)">{{item.value}}</a>
    </li>

</script>

directive:

appDirectives.directive('bootstrapListItems', ['$rootScope', function ($rootScope) {

    return {
        restrict: 'A',
        templateUrl: 'bootstrapDropDownItems.html',
        link: function (scope, element, attrs) {

            scope.type = attrs.useGlobaljsonObject;
            scope.parentElement = attrs.id;
            scope.items = [];

            var obj = $rootScope.globalJSON[scope.type];

            for (o in obj) {
                scope.items.push({ key: o, value: obj[o] })
            }
            scope.onClick = function(){
                // pass an array of original arguments to the event
                scope.$emit('someEventName', Array.prototype.slice.call(arguments));
            };
        }       
    }

}]);

controller:

appControllers.controller('TransactionsCtrl', ['$scope', 'InfiniteScrollingDataService', function ($scope, dataService) {
    // setup our scope properties
    $scope.$root.title = 'Transactions';
    var urlQuery = {
        skip: 0,
        take: 25,
        search: '',
        order: 'DateTimeCreated',
        orderBy: 'desc',
        transactionTypeID: null,
        transactionStatusID: null
    };
    var apiUrl = 'api/transactions';
    $scope.transactions = new dataService(apiUrl, urlQuery);

    $scope.$on('someEventName', function(e, args){
         // call the $scope.Filter function with the passed array of arguments
         $scope.Filter.apply(null, args);
    });
    $scope.Filter = function (senderParent, type, id) {
        $scope.FilterApplied = true;
        console.log('filter in controller: ' + senderParent + ' ' + type + ' ' + id);
    }
}]);
Sign up to request clarification or add additional context in comments.

4 Comments

Hi. I like this solution as it will allow me to, within the directive, change some class info on the selected item. However, I have pretty much copy/pasted this and for some reason my arguments are empty in my $scope.$on('filterSelected', function(e, args){... and all are undefined in my $scope.Filter function. any idea where i may be going wrong?
Actually - if i change Array.prototype.slice(arguments) for just arguments it all works as prescribed. Not sure why (not used the slice before...)
Any idea why Array.prototype.slice(arguments) returns an empty array?
Because it should be called like this: Array.prototype.slice.call(arguments). Sorry, my mistake, I edited the answear.
1

Having the directive used in multiple places does not mean you can't use shared scope. That determination comes down to if the directives should always show the same state (thus each inheriting and manipulating the same properties of the controller), or if they each are intended to show their own versions of state (thus being isolate). Being that you are setting a controller level variable in $scope.FilterApplied I assume you just want to repeat the same controls on the page? If so, you can stick with an inherited scope.

If not, and instead each directive needs this functionality on it's own, you are probably better off moving the Filter code into a service that the directive and controller(s) both inject so that the utility functions are shared but the settings and state of the Filter are per scope rather than shared.

Ultimately it depends on how you are using it.

3 Comments

I'm assuming (due to lack of knowledge at this point) that i will need an isolate scope. There will be a few elements on the page that use this directive. Each showing different items in the list. Also when an item from a filter is selected i need to set class of the selected item. Still not sure if i need isolate scope in this case..
An inherit scope will simply allow it to inherit the scope of its parent rather than creating its own scope. If you are displaying the same list and it's determined by an object/property in the controller then inherit scope is your easiest path. Each instance of the directive can contain attributes to change which part of the list they display. An isolate scope would only be necessary if they manipulate and encapsulate their own copy of the data. Otherwise each time you change the controller list they all change together. It's just a prototype chain really.
Ah, ok. it's becoming a little clearer now. In these lists there are no two-way binding or anything like that. It's simply to tell the controller to go get some data from the web api with this filter applied. I like the solution that kamilkp gave though as i can do more inside the directive on the item that was selected - change class etc. Thank you for clarification though

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.