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")

pQuestions123
  • 4,471
  • 6
  • 28
  • 59
  • 1
    You can access `scope.list` in the controller or link phase of this directive. – Rebornix Feb 11 '15 at 04:07
  • @Rebornix What about in my template function? Can I access it there? – pQuestions123 Feb 11 '15 at 04:17
  • Yup, in your template, you can use Angular expression like `{{list}}` or `ng-model="list"` ,etc. – Rebornix Feb 11 '15 at 04: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? – pQuestions123 Feb 11 '15 at 04:20
  • @Rebornix Can you check my edit? I can't seem to access the scope object in my template function. – pQuestions123 Feb 11 '15 at 04:23
  • Sorry but no, you can't read scope in your template function. If your template depends on scope element, you compile your template in linking phase. – Rebornix Feb 11 '15 at 04:26
  • check update of my answer, you can get an idea of dynamic templating – Rebornix Feb 11 '15 at 04:28
  • Reading your code, it looks like you are doing a double look through the list - once with a for() loop (need to put var in front of loop variable, btw), and once with hg-repeat. I don't think this is what you intend, and the answer I'm posting will reflect that. – Joe Enzminger Feb 11 '15 at 04:43

2 Answers2

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.

Joe Enzminger
  • 11,110
  • 3
  • 50
  • 75
  • Hi, can you expand a little bit on tranclusion. I have run across it a few times now and had trouble understanding it. – pQuestions123 Feb 11 '15 at 11:52
  • 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). – pQuestions123 Feb 11 '15 at 12:16
  • 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](http://stackoverflow.com/questions/14430655/recursion-in-angular-directives). The 'answer' is going to end up being close to Rebornix's second solution, although it can be simplified quite a bit – Joe Enzminger Feb 11 '15 at 17:54
  • OK, thanks a lot. At work now, will take a look at it today or tomorrow. Really appreciate it. – pQuestions123 Feb 11 '15 at 18:49
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);
        }
    };
});
Rebornix
  • 5,272
  • 1
  • 23
  • 26
  • That second way will work, but it's certainly the "hard way". Easier to just use a template function. – Joe Enzminger Feb 11 '15 at 04:30
  • but seems we can't access scope objects in template function, is that right? – Rebornix Feb 11 '15 at 04:34
  • 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. – Joe Enzminger Feb 11 '15 at 04:36
  • 1
    Agree, combing Angular built in directives to generate the template is better. – Rebornix Feb 11 '15 at 04:39