Angular 1.5 components with ui-router resolve : Unknown provider
Asked Answered
C

2

15

I'm facing an issue with converting controllers to components preparing my application for Angular 2, but the problem the migration is not going well, I have the ui-router to route between states and using resolve in a few controllers, the version with controller is working but the version of components now working at all, I followed a lot of guides and seems I'm doing good for code but its not working for me.

I have the following module with controller:

(function () {
  'use strict';

  angular
    .module('app.sample', [])
    .config(config);

  /** @ngInject */
  $stateProvider
    .state('app.sample', {
      url    : '/sample',
      views  : {
        'content@app': {
          templateUrl: 'app/main/sample/sample.html',
            controller : 'SampleController as vm'
          }
        },
        resolve: {
          SampleData: function (myService) {
            return myService.getSample(); // I return a promise here
          }
       }
     });
  }
})();

Controller:

(function ()
{
    'use strict';
    angular
        .module('app.sample')
        .controller('SampleController', SampleController);

    /** @ngInject */
    function SampleController(SampleData)
    {
        var vm = this;
        vm.helloText = SampleData.data.helloText;
    }
})();

The above code working well, BUT After making it as a component its become like this:

(function () {
  'use strict';

  angular
    .module('app.sample', [])
    .config(config);

  /** @ngInject */
  function config($stateProvider) {
    // State
    $stateProvider
      .state('app.sample', {
        url: '/sample',
        views: {
          'content@app': {
            template: '<sample></sample>'
          }
        },
        resolve: {
          SampleData: function (myService) {
            return myService.getSample(); // I return a promise here
          }
        }
      });
  }
})();

Component:

(function () {
  'use strict';

  angular
    .module('app.sample')
    .component('sample', {
      templateUrl: 'app/main/sample/sample.html',
      bindings: {
      },
      controller: Sample
    });

  /** @ngInject */
  function Sample(SampleData) {
    var $ctrl = this;
    $ctrl.helloText = SampleData.data.helloText;
  }
})();

But now its not working and gives me the following error:

Error: [$injector:unpr] Unknown provider: SampleDataProvider <- SampleData
http://errors.angularjs.org/1.5.7/$injector/unpr?p0=SampleDataProvider%20%3C-%20SampleData
    at angular.js:68
    at angular.js:4502
    at Object.getService [as get] (angular.js:4655)
    at angular.js:4507
    at getService (angular.js:4655)
    at injectionArgs (angular.js:4679)
    at Object.invoke (angular.js:4701)
    at $controllerInit (angular.js:10234)
    at nodeLinkFn (angular.js:9147)
    at angular.js:9553

My dependencies inside bower.json:

"dependencies": {
    "angular": "1.5.7",
    "angular-animate": "1.5.7",
    "angular-aria": "1.5.7",
    "angular-cookies": "1.5.7",
    "angular-material": "1.1.0-rc.5",
    "angular-messages": "1.5.7",
    "angular-resource": "1.5.7",
    "angular-sanitize": "1.5.7",
    "angular-ui-router": "1.0.0-beta.1",
    "jquery": "2.2.4",
    "mobile-detect": "1.3.2",
    "moment": "2.13.0"
  }

Any idea what the problem, what I'm missing?

Cress answered 13/7, 2016 at 8:37 Comment(2)
Can you confirm you have the js that contains the service SampleData inside index.html?Stereobate
@Stereobate i'm already writing the js file, not moved to ts yet, still using angular 1.5 preparing to second phase with ts later, and I can use myService inside the controller, SampleData not independent service service, its a variable that resolved inside module, as you can see in first controller and module they are working.Cress
C
27

Finally solved it, I misunderstood that how the components are working.

First I change SampleData to sampleData, Pascal Case but with first letter small.

Then inside the module i changed the template to template: '<sample sample-data="$resolve.sampleData"></sample>'

and resolve to :

resolve: {
  sampleData: function (msApi) {
    return msApi.resolve('sample@get');
  }
}

And for component I changed the binding as well:

bindings: {
  sampleData: '='
},

And inside the controller of component I removed SampleData from signature and called it like this $ctrl.helloText = $ctrl.sampleData.data.helloText;.

So the final code now is like : For Module:

 (function () {
  'use strict';

  angular
    .module('app.sample', [])
    .config(config);

  /** @ngInject */
  function config($stateProvider) {
    // State
    $stateProvider
      .state('app.sample', {
        url: '/sample',
        views: {
          'content@app': {
            template: '<sample sample-data="$resolve.sampleData"></sample>'
          }
        },
        resolve: {
          sampleData: function (myService) {
            return myService.getSample(); // I return a promise here
          }
        }
      });
  }
})();

And component like this:

(function () {
  'use strict';

  angular
    .module('app.sample')
    .component('sample', {
      templateUrl: 'app/main/sample/sample.html',
      bindings: {
        sampleData: '='
      },
      controller: Sample
    });

  /** @ngInject */
  function Sample() {
    var $ctrl = this;
    $ctrl.helloText = $ctrl.sampleData.data.helloText;
  }
})();

And finally worked.

Edit: P.S.: Outside the question and answer scope, If you use component without state too, you need to get the data inside controller instead of resolve, so you can call components wherever you want.

Cress answered 13/7, 2016 at 9:57 Comment(4)
Thank you. I was still having some trouble getting this to work in my app so I did some searching and found this GitHub issue which has an example: github.com/angular-ui/ui-router/issues/2793.Leakage
@Leakage what the name that you are using? anyway avoid use data word as a prefix or standalone,Cress
@Cress I think I was probably missing something that was causing it to not work. A second example helped reaffirm what you had in your answer. Thanks a lot. Edit: Now that I think of it, one difference I saw in the GitHub example was their binding is < instead of =. I'm not sure if that fixed my issue or if it was something else.Leakage
@Leakage using '=' is two-way bindings, using '<'is one way, almost the same, but in two-way you can get the value back after modification in object lets say you want to fill a property with data after processing it inside the component to parent (child give value to parent or vice-versa), but one-way is getting data from the parent like a read-only property. if updated from the parent you need to handle this using this.$onChanges.Cress
H
-1
'use strict';
angular
    .module('app.sample')
    .controller('SampleController', SampleController);

/** @ngInject */
function SampleController(SampleData)
{
    var vm = this;
    vm.helloText = SampleData.data.helloText;
}

Instead of giving like above, try injecting 'SampleData' resolve in your controller, like below:

'use strict';
angular
    .module('app.sample')
    .controller('SampleController', ['SampleData', SampleController]);

/** @ngInject */
function SampleController(SampleData)
{
    var vm = this;
    vm.helloText = SampleData.data.helloText;
}

Hope that works for you

Herman answered 13/7, 2016 at 8:45 Comment(1)
Controller working good, my problem with the components, i'm removing using .controller to .componentCress

© 2022 - 2024 — McMap. All rights reserved.