Question

I am creating an ASP.net MVC application and in that I am displaying ng-grid with a column which has link as cell content.

Here's how I achieved it.
Controller (ng-grid):

columnDefs: [
 { field: 'FreightRules', visible: true, 
    cellTemplate: 
     '<div class="rulesDirective" freight-rules="{{row.entity.FreightItemGuid}}">
          </div>', 
    enableCellEdit: false, enableCellSelection: false }]

Directive:

directives.directive('rulesDirective', function () {
    return {
        restrict: 'C',
        replace: true,
        transclude: true,
        scope: { freightRules: '@freightRules' },
        template: '<div ng-switch on="freightRules | areRulesAvailable">' +
                  '<div ng-switch-when = true><a ng-click="getRulesTest(\'{{freightRules}}\')" href="#addFreightRuleModal">Rules</a></div>' +
                  '<div ng-switch-when = false>No Rules</div>' +
                  '<div ng-switch-default class="grid">Error</div>' +
               '</div>'
        }});

Filter:

filters.filter('areRulesAvailable', function () {
    return function (value) {
        if (value != '00000000-0000-0000-0000-000000000000') {
            result= true;
        }
        else {
            result = false;
        }
        return result;
    };
});

It works fine but when I click on the Rules link I am not able to display the dialog.

I tried to open the dialog by clicking on a button separately and it worked. Need help!

Was it helpful?

Solution

Since your directive has an isolated scope (see Isolating the Scope of a Directive), which I can tell because its declaration contains scope: {...}, the directive will limit the items on the scope to only what your directive defines.

It is possible for directives to inherit items from a parent scope, or even use the same scope as the parent, but it won't in this case.

When you have an isolated scope, the only way to define items on that scope are to place them there in a controller, link function, or define them as a scope option within your directive. Now, if getRulesTest() is specific only to your directive (not reusable outside of it, not defined on a parent controller, etc.), then just define it in your link function (or directive's controller if it has one, whatever makes sense):

directives.directive('rulesDirective', function () {
    return {
        restrict: 'C',
        replace: true,
        transclude: true,
        scope: { freightRules: '@freightRules' },
        link: function($scope) { // looky here
            $scope.getRulesTest = function() {
                 console.log('check me out');
            };
        },
        template: '<div ng-switch on="freightRules | areRulesAvailable">' +
                  '<div ng-switch-when = true><a ng-click="getRulesTest(freightRules)" href="#addFreightRuleModal">Rules</a></div>' +
                  '<div ng-switch-when = false>No Rules</div>' +
                  '<div ng-switch-default class="grid">Error</div>' +
               '</div>'
        }});

Since you never define it within the directive's scope anywhere in your example, I presume you are declaring it on a parent controller and you are thinking it should just call out of the directive's scope and find it on the parent scope. As I said above, this won't work. You can use a scope option that binds to a function using the & syntax. This lets your view declaratively assign a function from the parent scope for your directive to use when it needs to call out.

directives.directive('rulesDirective', function() {
  return {
    restrict: 'C',
    replace: true,
    transclude: true,
    scope: {
      freightRules: '=',
      getRulesTest: '&rulesTest' // <- this guy. the text on the left is what you want the item named on the scope locally. the text on the right, after the & is what the attribute on the element should be called. if you want them to be the same on both sides, simply enter '&'
    },
    template: '<div ng-switch on="freightRules | areRulesAvailable">' +
      '<div ng-switch-when="true"><a ng-click="getRulesTest({rules: freightRules})" href="">Rules</a></div>' +
      '<div ng-switch-when="false">No Rules</div>' +
      '<div ng-switch-default class="grid">Error</div>' +
      '</div>'
  };
})

We added the scope option to your scope declaration. Now your parent controller would add a function on the scope to be bound to:

.controller('MyCtrl', function($scope) {
   $scope.doSomethingWithRules = function(rules) {
         console.log('sup?');
   };
});

And the view would look like:

<div class="rules-directive" rules-test="doSomethingWithRules(rules)"></div>

The other thing that might be problematic is whatever you're doing with this line in the template:

ng-click="getRulesTest(\'{{freightRules}}\')"

Are you trying to pass that function the evaluated string result of {{freightRules}} (which doesn't seem very useful), or do you want to pass it in the actual freightRules object (which seems more likely)? If so, just change it to:

ng-click="getRulesTest({rules: freightRules})"

Edit

Here is a plunk with a working example.

I did catch a small error in my original answer on how to call function scope options. There are a couple of ways to call functions that you have bound, depending on whether or not you have parameters and how you want them matched. This part is a little confusing. If you're not following, just stick to option #2.

1. You want to just pass the name of the function it should call, with parameters determined ordinally.

Say you want them to simply state (note that we only supply the name of the rules, no parenthesis or parameter names):

<div class="rules-directive" ... rules-test="doSomethingWithRules">

Then your directive should invoke the function like this (note that we call a bare function, which generates an invoker that can be used to actually call the function, and then we call it with the actual parameters):

ng-click="getRulesTest()(rules)"

2. You want the invoker to pass in named arguments so you can change the order of or omit parameters.

This is best illustrated if you have multiple parameters. Say the directive supplies two parameters, paramA and paramB. You invoke the function in your directive using an object containing parameter names and values.

ng-click="getRulesTest({paramA: valuesForA, paramB: valuesForB })"

Then when using the directive, your markup looks like this:

<div class="rules-directive" ... rules-test="doSomethingWithRules(paramA, paramB)">

The reason this is preferable is in the situation where you want to reorder or omit a parameter in the function being invoked. Let's say doSomethingWithRules only needs paramB. Now I can do:

<div class="rules-directive" ... rules-test="doSomethingWithRules(paramB)">

I couldn't have done that with Option #1 above, because it always invokes with all of the parameters ordinally. So, Option #2 is preferable because it gives consumers of your directive more flexibility and saves them the hassle of having to remember what order the parameters are supplied to your function.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top