0

I want to dynamically compile component for inserting this to specific DOM element (DOM also dynamically created by 3rd party library). So, I use $compile, $scope.

https://jsbin.com/gutekat/edit?html,js,console,output

// ListController $postLink life cycle hook

function $postLink() {
  ...

  $timeout(function () {
    ctrl.items.splice(0, 1);
    $log.debug('First item of array is removed');
    $log.debug(ctrl.items);
  }, 2000);
}

but below $onChanges life cycle hook in ListItemController isn't executed.

// ListItemController $onChanges life cycle hook

function $onChanges(changes) {
  if (!changes.item.isFirstChange()) {
    $log.debug(changes);  // Not executed
  }
}

I guess that angular.merge to pass item before ListItemController controller instance initialization is a major cause.

var itemScope = $scope.$new(true, $scope);
itemScope = angular.merge(itemScope, {
  $ctrl: {
    item: item
  }
});
2
  • $onChanges only listen to the property you put in the binding, since item is not in the binding, any changes to it will not trigger the $onChanges function, you should use $watch instead. Commented Dec 23, 2016 at 2:54
  • @AnthonyC Then, how to properly pass item object in listItem component bindings while compilation in ListController? Commented Dec 23, 2016 at 3:10

1 Answer 1

2

I modified you code a bit to demonstrate what is going on w/ the one way binding.

angular.module('app', [
    'list.component',
    'list-item.component'
]);

/**
 * list.component
 */
angular
    .module('list.component', [])
    .component('list', {
        controller: ListController,
        template: '<div id="list"></div>'
    });

ListController.$inject = ['$compile', '$document', '$log', '$scope', '$timeout'];
function ListController($compile, $document, $log, $scope, $timeout) {
    var ctrl = this;

    ctrl.$onInit = $onInit;
    ctrl.$postLink = $postLink;

    function $onInit() {
        ctrl.items = [
            {
                id: 0,
                value: 'a'
            },
            {
                id: 1,
                value: 'b'
            },
            {
                id: 2,
                value: 'c'
            }
        ];
    }

    function $postLink() {
        var index = 0;
        // Not entirely sure what you need to do this. This can easily be done in the template.
        /** ie:
         * template: '<div id="list" ng-repeat="item in $ctrl.items"><list-item item="item"></list-item></div>'
         **/

        var iElements = ctrl.items.map(function (item) {
            var template = '<list-item item="$ctrl.items[' + (index) + ']"></list-item>';
            index++;
            // you don't want to create an isolate scope here for the 1 way binding of the item.
            return $compile(template)($scope.$new(false));
        });

        var listDOM = $document[0].getElementById('list');
        var jqListDOM = angular.element(listDOM);

        iElements.forEach(function (iElement) {
            jqListDOM.append(iElement);
        });

        $timeout(function () {
            // this will trigger $onChanges since this is a reference change
            ctrl.items[0] = { id: 3, value: 'ss' };
            // this however, will not trigger the $onChanges, if you need to use deep comparison, consider to use $watch
            ctrl.items[1].value = 's';
            ctrl.items[2].value = 's';
        }, 2000);
    }
}

/**
 * list-item.component
 */
angular
    .module('list-item.component', [])
    .component('listItem', {
        bindings: {
            item: '<'
        },
        controller: ListItemController,
        template: '<div class="listItem">{{ $ctrl.item.value }}</div>'
    });

ListItemController.$inject = ['$log'];
function ListItemController($log) {
    var ctrl = this;

    ctrl.$onChanges = $onChanges;

    function $onChanges(changes) {
        if (!changes.item.isFirstChange()) {
            $log.debug(changes);    // Not executed      
        }
    }
}
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.