AngularJS Upgrade (1.5 to 1.6,1.7) Makes directive scope bindings undefined
Asked Answered
H

2

12

I have the following code:

angular
  .module('myApp')
  .directive('layout', function () {
      return {
          restrict: 'E',
          template: '<div ng-include="layoutCtrl.pageLayout"></div>',
          controller: 'LayoutController',
          controllerAs: 'layoutCtrl',
          bindToController: true,
          scope: {
              pageLayout: '=',
              pageConfiguration: '=',
              isPreview: '='
          }
      };
  });

angular
  .module('myApp')
  .controller('LayoutController', LayoutController);

function LayoutController($scope, LayoutDTO, LayoutPreviewDTO) {
    var self = this;
    self.layoutDTO = LayoutDTO;
    self.layoutPreviewDTO = LayoutPreviewDTO;
    var test = $scope;

    if(self.isPreview)
        self.layoutModel = new self.layoutPreviewDTO(self.pageConfiguration);
    else
        self.layoutModel = new self.layoutDTO(self.pageConfiguration);
}


<div>
    <layout page-layout="ctrl.layoutTemplateUrl" page-configuration="ctrl.pageConfiguration" is-preview="false"></layout>
</div>

In the angular 1.5.3 version this worked as expected, the variables in my Controller were coming in with values. Now, since I upgraded to 1.6.x, self.pageConfiguration is now undefined.

Nothing has changed except for the angular version.

How do I get a handle on the values passed into the directive in my controller?

Heptahedron answered 23/2, 2017 at 20:55 Comment(4)
Have you checked the Angular changelog for breaking changes that might cause this code "not to work"? And what exactly doesn't work - What error do you get? Did you also updated other ng native modules to the same version of angular?Lyra
self.pageConfiguration is now "undefined" instead of the values I passed in from the html <layout page-layout="ctrl.model.layoutTemplateUrl" page-configuration="ctrl.model.pageConfiguration" is-preview="false"></layout>Heptahedron
Unfortunately that's too broad. Can you make a fiddle that reproduce the issue? Because it's hard to debug without knowing how you call this directive, etc..Lyra
There is an edit link under the tags of the question - Click on it and update the answerLyra
P
18

The AngularJS team recommends that controller code that depends on scope bindings be moved into an $onInit function.

function LayoutController($scope, LayoutDTO, LayoutPreviewDTO) {
    var self = this;
    this.$onInit = function () {
        // bindings will always be available here
        // regardless of the value of `preAssignBindingsEnabled`.
        self.layoutDTO = LayoutDTO;
        self.layoutPreviewDTO = LayoutPreviewDTO;
        var test = $scope;

        if(self.isPreview)
            self.layoutModel = new self.layoutPreviewDTO(self.pageConfiguration);
        else
            self.layoutModel = new self.layoutDTO(self.pageConfiguration);
    };
}

$compile:

Due to bcd0d4, pre-assigning bindings on controller instances is disabled by default. It is still possible to turn it back on, which should help during the migration. Pre-assigning bindings has been deprecated and will be removed in a future version, so we strongly recommend migrating your applications to not rely on it as soon as possible.

Initialization logic that relies on bindings being present should be put in the controller's $onInit() method, which is guaranteed to always be called after the bindings have been assigned.

-- AngularJS Developer Guide - Migrating from v1.5 to v1.6 - $compile


UPDATE

The $compileProvider.preAssignBindingsEnabled flag has been removed from AngularJS V1.7.

The AngularJS team strongly recommends migrating your applications to not rely on it as soon as possible. AngularJS V1.6 goes end-of-life on 1July2018.

From the Docs:

Due to 38f8c9, directive bindings are no longer available in the constructor.

Previously, the $compileProvider.preAssignBindingsEnabled flag was supported. The flag controlled whether bindings were available inside the controller constructor or only in the $onInit hook. The bindings are now no longer available in the constructor.

To migrate your code:

  • If you specified $compileProvider.preAssignBindingsEnabled(true) you need to first migrate your code so that the flag can be flipped to false. The instructions on how to do that are available in the "Migrating from 1.5 to 1.6" guide. Afterwards, remove the $compileProvider.preAssignBindingsEnabled(true) statement.

— AngularJS Developer Guide - Migrating to V1.7 - Compile

Note:

On 1July2018, support for AngularJS 1.6 ends. For more information, see AngularJS MISC - Version Support Status.

Perpetuity answered 24/2, 2017 at 4:7 Comment(2)
OK, I will keep this in mind for future versions, thanks for pointing me to this articleHeptahedron
@Heptahedron you should mark this as the right answer.Shiism
H
1

I figured it out:

https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef

This defaults to false now, must set to true $compileProvider.preAssignBindingsEnabled(true);

Heptahedron answered 23/2, 2017 at 22:49 Comment(2)
this is a bad idea if you're trying to follow the migration path because all that does is switch it back to 1.5. As @Perpetuity mentioned, you should be using $onInit() to correct your bindingAppointive
The $compileProvider.preAssignBindingsEnabled flag has been removed from AngularJS V1.7.Perpetuity

© 2022 - 2024 — McMap. All rights reserved.