0

Here is the html:

<body ng-app="myCatApp">
        <div ng-controller="catagoryController">
            <add-remove-lister list="catagories"></add-remove-lister>
        </div>
    </body>

Here is the JS:

    app.controller('catagoryController', ['catagoryList', '$scope', function(catagoryList, $scope) {
      $scope.catagories = catagoryList.catagoryList;
    }]);

    app.directive('addRemoveLister', [function () {
        return {
            scope: {
                list: '='
            },
template: function(tElement, tAttrs, scope) {
            templateHTML = '<ul>';
            var list = scope.list;
            for (o = 0; o < list.length; o++) {
                var curObject = scope.list[o];
                templateHTML +='<li ng-repeat="listItem in list"><button type="button" ng-hide="listItem.userSelected" ng-click="addToList()">Add</button>';
                templateHTML +='<button type="button" ng-hide="listItem.userSelected" ng-click="removeFromList()">Remove</button>{{listItem.text}}';
                for (var prop in curObject) {
                    if (curObject.hasOwnProperty(prop) && prop.constructor === Array) {
                        templateHTML += '<add-remove-lister list="listItem.'+ prop +'"></add-remove-lister>';
                    }
                }
                templateHTML += '</li>';
            }
            templateHTML += '<ul>';
            return templateHTML;
        }
        }
    }]);

The code is not working correctly. When I set a breakpoint in the directive, I can see that list is just the string "catagories". I would like it to be the categories object that is on the controller scope...

Let me expand a little bit on what I am trying to do:

I am trying to build a directive that will take any array and produce a list from it. The assumptions are: 1) That all elements in the array will be objects having at least the properties {text : 'text', userSelected: 'bool'}

When the directive encounters an object in the list that has a property with that is itself an array (which is also assumed to contain objects with the above two properties), it needs to recursively call itself on that array.

The directive also needs to display buttons next to each list item to allow the user to change the userSelected property on the object (thereby adding it to the users "options")

8
  • 1
    You can access scope.list in the controller or link phase of this directive. Commented Feb 11, 2015 at 4:07
  • @Rebornix What about in my template function? Can I access it there? Commented Feb 11, 2015 at 4:17
  • Yup, in your template, you can use Angular expression like {{list}} or ng-model="list" ,etc. Commented Feb 11, 2015 at 4:18
  • @Rebornix No, I mean in my template function if I return{ template: function() {}}, Can I get a reference to the list object in the this function? Commented Feb 11, 2015 at 4:20
  • @Rebornix Can you check my edit? I can't seem to access the scope object in my template function. Commented Feb 11, 2015 at 4:23

2 Answers 2

1

Try this for your template function

template: function(tElement, tAttrs, scope) {
        templateHTML = '<ul>';
        templateHTML +='<li ng-repeat="listItem in list"><button type="button" ng-hide="listItem.userSelected" ng-click="addToList()">Add</button>';
        templateHTML +='<button type="button" ng-hide="listItem.userSelected" ng-click="removeFromList()">Remove</button>{{listItem.text}}';
        templateHTML += '<add-remove-lister ng-repeat="(key, val) in listItem" ng-if="val.length" list="val"></add-remove-lister>';
        templateHTML += '</li>';
        templateHTML += '<ul>';
        return templateHTML;
}

Note that you can probably do the above with just a template, the template function is not really necessary.

The main reason for a template function is to allow you to modify the DOM of the original HTML, or to pull sections from the original element to do manual transclusion.

Also, there are a few problems I can see already in your directive (the functions you reference in your template have to be defined on your directive's scope, and since you are using an isolated scope, you can't reference functions on your parent scope. You'll have to pass those methods in as attributes of the directive as well, or use some other mechanism to add or remove elements.

Here is a Working Plunk.

Sign up to request clarification or add additional context in comments.

4 Comments

Hi, can you expand a little bit on tranclusion. I have run across it a few times now and had trouble understanding it.
Can you take a look at my edit? I don't think this solution will work (my fault for not fully spelling out what I am trying to accomplish).
Got it. The problem is you want to recursively include a reference to your directive in the directive itself. I'd post a new answer, but it's already on StackOverflow here. The 'answer' is going to end up being close to Rebornix's second solution, although it can be simplified quite a bit
OK, thanks a lot. At work now, will take a look at it today or tomorrow. Really appreciate it.
0

You can access list in following ways

app.directive('addRemoveLister', [function () {
    return {
        restrict: 'E',
        scope: {
            list: '='
        },
        template: "test {{list}}",
        link: function (scope, element, attrs) {
          console.log(scope.list);
        },
        controller: function (scope) {
          console.log(scope.list);
        }
    }
});

Or you can compile your dynamic template in linking phase

app.directive('addRemoveLister', function ($compile) {
  var getTemplate = function(list) {
        var templateHTML = '<ul>';
        for (o = 0; o < list.length; o++) {
            var curObject = scope.list[o];
            templateHTML +='<li ng-repeat="listItem in list"><button type="button" ng-hide="listItem.userSelected" ng-click="addToList()">Add</button>';
            templateHTML +='<button type="button" ng-hide="listItem.userSelected" ng-click="removeFromList()">Remove</button>{{listItem.text}}';
            for (var prop in curObject) {
                if (curObject.hasOwnProperty(prop) && prop.constructor === Array) {
                    templateHTML += '<add-remove-lister list="listItem.'+ prop +'"></add-remove-lister>';
                }
            }
            templateHTML += '</li>';
        }
        templateHTML += '<ul>';
        return templateHTML;
  }

    return {
        restrict: 'E',
        scope: {
            list: '='
        },
        link: function(scope, element, attrs) {
            var el = $compile(getTemplate(scope.list))(scope);
            element.replaceWith(el);
        }
    };
});

4 Comments

That second way will work, but it's certainly the "hard way". Easier to just use a template function.
but seems we can't access scope objects in template function, is that right?
You can't, but after the template function runs, it gets compiled. I would suggest, in this example, a different approach to conditionally changing the template based on your model....do it the angular way.
Agree, combing Angular built in directives to generate the template is better.

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.