3

I'm having a pretty tricky problem that seems to be trivial but I'm not finding a good way to solve it.

Essentially, I'm building a todo list application using Angular and Angular-Material.

I have a main.html where, the important part is:

<md-content ng-show="mainCtrl.todos.length">
   <md-list class="todo_list" flex>
     <md-subheader class="md-no-sticky">{{mainCtrl.todoList.label}}</md-subheader>
       <acme-todo ng-show="mainCtrl.todos" ng-repeat="todo in mainCtrl.todos track by $index"
                  text="todo.content" index="$index"></acme-todo>
   </md-list>
</md-content>

Where acme-todo is a directive which contains a todo.html:

<md-list-item layout='row' layout-sm='column' layout-align='center center' layout-wrap>
    <i class='material-icons md-avatar'>border_color</i>
    <div class='md-list-item-text'><h3>{{mainCtrl.text}}</h3></div>
    <div class='md-secondary'>
        <md-button class='md-fab md-primary md-small box red-btn'
                   aria-label='delete' ng-click='mainCtrl.deleteTodo(mainCtrl.index)'>
            <i class='material-icons small-icon'>highlight_off</i>
        </md-button>
    </div>
</md-list-item>

Where todo.directive.js:

(function () {
    'use strict';

    angular
        .module('todoApp')
        .directive('acmeTodo', acmeTodo);

    function acmeTodo() {
        var directive = {
            restrict: 'EA',
            scope: {
                text :  '=',
                index : '='
            },
            templateUrl: 'todo.html',
            controller : 'MainController',
            controllerAs : 'mainCtrl',
            bindToController : true
        };

        return directive;
    }

})();

And finally, main.controller.js:

(function () {
    'use strict';

    angular
        .module('todoApp')
        .controller('MainController', MainController);

    function MainController() {

        var vm = this;
        vm.todos = [];

        . . .

        vm.addToDo = addToDo;
        vm.deleteTodo = deleteTodo;

        //vm.todo.content is the ng-model of the input-box
        function addToDo() {
            if( vm.todo && !_.isUndefined(vm.todo.content)){
                   pushNewToDo(vm.todo.content);
            }
        }

        function deleteTodo(index) {
            vm.todos.splice(index, 1);
        }

        function pushNewToDo(todo) {
            vm.todos.push({ content : todo });
            vm.todo.content = '';
        }

    }

})();

Everything works fine except when deleting a todo. When deleteTodo is called, the function correctly receives external $index from main's ng-repeat but, can't find out why, vm.todos is empty at that moment, so, nothing gets deleted.

If, instead, I manage to move all of todo.html content at acme-todo place's everything works fine.

I also tried setting $scope : true in the directive instead of passing both text and $index but I'm still encountering the same problem.

Here is a working Codepen which reproduces my issue. N.B. On the CodePen I'm using template instead of templateUrl and I changed the icons just to make things work properly there.

Here's a screen of the real application:

enter image description here

I know that, by default, if I do not specify any controller, the directive will still pick its scope, but

EDIT

Using a Service or a Factory also solves the problem like in this CodePen solves the problem, but I feel like adding way too much logic for such a small thing.

Why Am I getting an empty todos array even thought both controller and directive share the same scope? How can I fix that keeping the same approach (using a todo directive)?

1 Answer 1

1

You should separate app and directive controller because when you're initializing directive you're creating new controller, so new $scope and new todos array locally inside directive. After that you can pass delete function from the main controller to directive's $scope.

Take a look here: http://codepen.io/anon/pen/zZWyLV

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

Comments

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.