angular-ui / bootstrap

PLEASE READ THE PROJECT STATUS BELOW. Native AngularJS (Angular) directives for Bootstrap. Smaller footprint (20kB gzipped), no 3rd party JS dependencies (jQuery, bootstrap JS) required. Please read the README.md file before submitting an issue!
http://angular-ui.github.io/bootstrap/
MIT License
14.29k stars 6.73k forks source link

Uglify fails - Unknown provider: aProvider <- a #2246

Closed mastix closed 10 years ago

mastix commented 10 years ago

Hi guys,

I ran into an issue yesterday when I've added the angular-bootstrap JS to my project (which has been set up using the Yeoman Angular Generator) and then it failed due to uglification.

I've added the lib via bower (uncompressed!) and it got included into the vendor.js for concatination and minification.

When the file (ui-bootstrap-tpls.js; uncompressed) is concatenated and bundled in one big vendor.js, everything looks fine, but as soon as the uglify task kicks in (with mangle: true = default), this fails (although the grunt ngmin task should rewrite the code to use Angular's inline annotation method):

The error message was pretty much this one:

Error: [$injector:unpr] Unknown provider: aProvider <- a

Pretty much like this bug: https://github.com/yeoman/generator-angular/issues/378

Is this a bug in the angular-bootstrap code and how it's implemented? Does anybody have an idea why this is happening and how it can be fixed (without turning mangle off)?

Just a hint would be cool! :+1:

I've set mangle to false in the uglify.js code... and everything works fine, but I'd really like to turn it back to true again. :)

Regards,

mastix

fno commented 10 years ago

I've encountered this problem literally 5 minutes ago too without any idea.

mastix commented 10 years ago

Haha... what a coincidence! :) You can set mangle to false, so variable names won't be shortened, then everything should work... but that leads to bigger JS files... :(

pkozlowski-opensource commented 10 years ago

@mastix @fno this problem does not happen with the minified version we are providing, check our demo page: http://angular-ui.github.io/bootstrap/

I'm not sure what are the exact minification steps you are taking, but if you can prepare a minimal project showing that uglify is breaking our deliverables I can surely take a look. Otherwise this one is hardly actionable....

pkozlowski-opensource commented 10 years ago

OK, here is more info: http://stackoverflow.com/questions/17238759/angular-module-minification-bug

and what can be done on the library level: https://github.com/gruntjs/grunt-contrib-uglify#no-mangling

I don't think we can do anything meaningful in the library so I'm going to close this issue. Will be happy to re-open if you guys think there is anything actionable on our side.

mastix commented 10 years ago

Hi,

sure, as soon as mangling is disabled, everything is cool, but that also increases the file size a lot.

I'm using ng-min before uglify (also on angular-bootstrap), but for some reason all of my controllers and included js files work fine, but the ui.bootrap file does not.

ng-min does the following:

angular.module('whatever').controller('MyCtrl', function ($scope, $http) { ... });

gets turned into:

angular.module('whatever').controller('MyCtrl', ['$scope', '$http', function ($scope, $http) { ... }]);

Now minifiers can handle AngularJS's dependency injection annotations since the attributes are added as string params in the array... but for some reason that does not work for angular-bootstrap.

So there might be actually something to check on your side!?

Thanks for your reply & help!!!

mastix

pkozlowski-opensource commented 10 years ago

@mastix our deliverables already got all the minification-safe annotations (if you can spot places where it is not the case it is a bug that should be fixed - but I don't think so since the minified version we provide does work) so there is no reason to pass this code through the ng-min. This might actually cause problems.

Then, I'm not saying that some parts of our code make it uglify - unfriendly. If this is the case I'm all for fixing things. But in order to act we need to know which part of the code is causing problems.

@mastix @fno for now I assume that this is your custom build that is causing problems here, but I would like to be proven otherwise. So if you see anything strange please do let us know.

fno commented 10 years ago

i dont know about the "custom" build as it seems to stem from a very popular approach using yeoman, bower and grunt while using the angular-generator.

when you install angular-bootstrap via bower into this setup, the ui-bootstrap-tpls.js is automatically added to the whole vendor.js pipeline (ngmin, uglify, etc) ... this seems to be the root cause of this.

so to reproduce:

npm install -g yo npm install -g generator-angular bower install angular-bootstrap grunt && grunt serve

as im fairly new to the js/angular ecosystem it seems not quite obvious as to where to place this problem exactly.

thanks for your time.

mastix commented 10 years ago

Don't have a solution yet... :( Kind of postponed it, until there's one available. This must be fixed before my project goes live - can't deliver my js with the long variable names! :)

fno commented 10 years ago

well you could move the ui-bootstrap-tpls.js out of the uglify/concatenation block in your index.html

mvhecke commented 10 years ago

What worked for me is to exclude Angular itself from the mangling if you concatenate everything into one file. I'm not sure if it's going to work in your case, but you could give it a try.

Grunt

grunt.initConfig({
  uglify: {
    options: {
      mangle: {
        except: ['angular']
      }
    }
  }
});

Gulp

.pipe(uglify({
    mangle: {
        except: ['angular']
    }
}))
pkozlowski-opensource commented 10 years ago

@mastix @fno I would really love to help here but you need to help me to help you... Couple of things:

mastix commented 10 years ago

@pkozlowski-opensource It's hard to upload it somewhere since it's kind of a process (grunt -> that minifies files) and not a resulting file.

I don't think that this is a "real" problem with your files, all I know is that when we I use ngmin on my files, they're getting converted correctly, so uglifying works like a charm there. You're file is part of this process as well (and I have no idea how to exclude it from the ngmin-step) and so ngmin is being called for that file as well.

I don't want this file to be served with a single request... it should be packaged into the whole js file.

It would be great if someone had an idea.

Thanks guys!

mastix commented 10 years ago

Btw - that's the error message: screen shot 2014-05-27 at 20 15 04

fno commented 10 years ago

@pkozlowski-opensource

as i already mentioned above its probably not your minified files but the way that your project is integrated into a very widespread development approach. to my knowledge other angular directives that install via bower dont exhibit this problem. i may be totally wrong here, but i think that @mastix has the same problem of attributing the real source of error here.

  1. start an ng project via yeoman: "yo angular mynewproject"
  2. install angular bootstrap via bower: "bower install angular-bootstrap"
  3. add the 'ui.bootstrap' dependencie in your app.js
  4. "compile" your project and serve it: "grunt serve:dist" (you can also grunt build and deploy to somewhere else, doesnt change the error)

what seems to be happening is that bower places the ui-bootstrap-tpls.js file into the block that is later minified and concatenated as "vendor.js". the error is fixed by moving it out of the block or disabling "mangle". i'm too much of an angularjs novice to correctly attribute the real source of error, its probably a configuration somewhere that decides where to automatically place the includes.

joshdmiller commented 10 years ago

@fno I am unable to duplicate this.

$ sudo npm install -g yo generator-angular
$ yo angular test
$ cd test
$ bower install --save angular-bootstrap
$ grunt serve:dist --force # to prevent imagemin errors

And everything works. I then added a directive to the main.html page and it showed it just fine. Also, while I agree with @pkozlowski-opensource about not needing to run the file through ng-min, I have done it many times as here without issue. So I don't know what's going on with your code. :-(

Is there any more information you can provide?

PeterTrotter commented 10 years ago

The problem seems to be occurring because of the concatenation.

In Index.html if you separate out ui-bootstrap-tpls.js

    <!-- build:js(.) scripts/vendor2.js -->
    <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
    <!-- endbuild -->

And in your root bower.json add

...
"overrides": {
  "angular-bootstrap": {
    "main": []
  }
}
...

Everything will work just fine and, for the most part, your files are uglified. Obviously this is just a workaround.

knutesten commented 10 years ago

I got a similar bug recently, it was not caused by ngmin, uglify or ui-bootstrap per se. I my case the bug was caused by me not using array syntax when passing a controller as a parameter in modal options.

Because of ngmin I am not used to using array syntax for my controllers, services etc. so i forgot to add array syntax to the modalOptions.controller property:

// causes bug when uglifying (ngmin will not change this to array syntax)
modalOptions.controller = function($scope) { /* ... */ };
$modal.open(modalOptions)
// will work with ngmin and uglify
modalOptions.controller = ['$scope', function($scope) { /* ... */ }];
$modal.open(modalOptions)
sethen commented 10 years ago

For me this was angular ui-router and I was injecting a property in the controller of a state and it wasn't having it.

michelbb commented 10 years ago

@knutesten Thank you very much for posting your solution. Solved the problem for me as well :+1:

bonpixel commented 10 years ago

@knutesten Rockin! This was the ticket for me also. :sparkles:

nicolasgarnil commented 9 years ago

@knutesten thanks Seems it's not an angular-ui issue.

ryanio commented 9 years ago

@knutesten Banged my head around for a few hours with this problem and couldn't stack trace the problem properly. Your solution was spot on. Thanks!

DFOXpro commented 9 years ago

This issue is generated in how the minifier system set the short variables, the angular engine define variables from controllers/services/etc name, the minifier does not edit string variable name but edit the variable param 'name' and the provider try to find a non existent variable. I get this error with grunt-uglify and my way to solve without disabling all mangle is declaring all the $varialbes:

uglify: {
  options: {
    mangle: {
      except: [
        '$anchorSmoothScroll',
        '$classroom',
        '$grade',
        '$lesson',
        '$filter',

]}}}

frhd commented 9 years ago

@sethen Same here. What was your fix?

yousfiSaad commented 9 years ago

Thank you @knutesten, it solved my problem :)

sethen commented 9 years ago

@frhd Sorry, just seeing this now.

I can't remember exactly what the issue was anymore (I commented that almost a year ago)... I remember it had something to do with the minification process in Yeoman somehow issuing a grunt build:dist I believe. I think ui-router will throw this error when you're doing something like:

.state('site.gateway.password-request', {
    url: '^/password/request',
    templateUrl: '/views/partials/templates/request.html',
    controller: function ($rootScope) {
        $rootScope.title = 'Password Request';
    }
})

This looks right, but you need to give the controller an array with your injected stuff first like:

.state('site.gateway.password-request', {
    url: '^/password/request',
    templateUrl: '/views/partials/templates/request.html',
    controller: [$rootScope, function ($rootScope) {
        $rootScope.title = 'Password Request';
    }]
})

If you don't give it an array first and issue a build, you will get the error. This may not help a lot, but that's all I can really remember.

joncodo commented 9 years ago

For me it was the resolves in the states for ui router

$stateProvider.state('deals', {
      url: '/deals',
      templateUrl: '/contest/states/deals.template.html',
      controllerAs: 'DealsCtrl',
      resolve: {
        deals: ['$http', function ($http) {
          return $http.get('/api/deal/findactive')
            .then(function (results) {
              return results.data;
            });
        }],

It needed the ['$http', fn

ryancarlson commented 9 years ago

@mastix @fno @pkozlowski-opensource I just hit the same issue using ui-bootstrap with these tools. The larger file size isn't an issue for me, so I'll work around it by not minifying that file. Curious though.. did resolving this issue get set aside for one reason or another?

aendra-rininsland commented 9 years ago

I thought this issue was a thing of the past with ngAnnotate so was really confused when I ran into it again recently. Seems I removed the array syntax from a ui-router resolve — which ngAnnotate doesn't handle.

Actually, ngAnnotate can handle ui-router resolves; what was causing the issue was defining a controller with cironunes/angular-off-canvas and not using the array syntax. Anyone still seeing this issue and is using ngAnnotate, look through your code and make sure ngAnnotate supports anywhere you're not using the array syntax. This part of ngAnnotate's README is really helpful.

tl;dr for anyone else also reaching this issue via Google — issue wasn't with ui.bootstrap, was with me expecting ngAnnotate to annotate an unsupported declaration form.

Shaolin23 commented 9 years ago

@knutesten thanks - that was my oversight as well.

glorat commented 8 years ago

Amazing I found the solution to my problem in google leading here... @knutesten has my problem and solution (thanks!). minify is mucking up the of inline array controller syntax in the middle of code.

This won't happen if one declares the controller as a named controller rather than an inline one, which will rarely happen going forward since the documentation these days seems to promote that.

ivomarsan commented 8 years ago

Nice Idea @knutesten :smiley: but I solve my problem with .pipe(uglify({ mangle: false }))

OBS: in gulp

hc000 commented 8 years ago

@knutesten this solved my problem!

icfantv commented 8 years ago

Just a note for all of you here - you should ALWAYS turn on strictDi mode for your angular application. You can do it either via the ng-strict-di attribute on your ng-app element or, if you're bootstrapping your application, you can follow the instructions here. This will protect you from these type of issues moving forward.

Unfortunately, not all angular examples are good about using the strict injection declaration format and so people learn incorrectly.

hc000 commented 8 years ago

I just want to point out that for all other users even with using ng-strict-di, it did not resolve this problem, or lead to this specific case.

jayconstanza commented 8 years ago

I had a big problem with that too and I want to thank you all because a mix of answers was my good cocktail :D

I had to uncomment uglify on the Gruntfile and add the option 'mangle' with the value 'false' as mentioned by @ivomarsan. I also made a separate build for "" and added the piece of code that @PeterTrotter said to the bower.json.

And that was the solution for me!

Thank you so much!!

mrdulin commented 8 years ago

image

I found two problems,

The first is angular-bootstrap modal directive resolve problem, yeoman with grunt can not annotate the modal resolve dependency. so I write it manually.

But, The annotate problem is still there when I exec grunt build image

I check the error stack in mangled code . I think the problem is angular-bootstrap modal, but i did't find the error code.

joshribakoff commented 8 years ago

This is a bug with ng-annotate, not ui-bootstrap. For me it started after upgrading ui.bootstrap though. If we look in the ng-annotate docs, we'll find:

ng-annotate understands $uibModal.open (and $modal.open) (angular-ui/bootstrap). https://github.com/olov/ng-annotate/blob/master/IMPLICIT.md

There must be a bug with ng-annotate's handling of the newer ui.bootstrap, that uses the uib prefix. That is all I changed to replicate the bug was $modal.open to $uibModal.open, and that caused ng-annotate to stop implicitly matching.

If I tag the inline controller with @ngInject, it works:

$uibModal.open({
                    /** @ngInject */
                    controller: function($scope, $uibModalInstance) {

Or changing to this also works:

angular.controller('MyCntrl', function($scope, $uibModalInstance) {
});

$uibModal.open({
                    controller: 'MyCntrl'
});

In summary, this is a bug in ng-annotate, which is able to handle $modal.$open, angular.controller, but apparently not $uibModal.open -- it is not in any way ui.bootstrap's problem. It doesn't matter if you use ui-bootstrap.js, ui-bootstrap.min.js, or minify it yourself. The minification is not failing in ui.bootstrap's code, its failing to minify the code invoking ui.bootstrap (your code, not ui.bootstrap's).

Helpful tip for debugging, enable ng-strict-di & run your code through ng-annotate but do not minify. Then you will get more useful errors showing where the ng-annotate is skipping injections, causing the minification problems.

bennettatoms commented 8 years ago

Hi, all. I was also having this issue on only one of several modal instances throughout my application, and after some debugging I realized that my problem was not using strict dependency injection in my resolve block, i.e.:

  var modalInstance = $uibModal.open({
    templateUrl: 'preferences.html',
    controller: 'preferencesCtrl as ctrl', // this external controller was using explicit function annotation...
    resolve: {
      parent: [function() { 
        return ctrl;
      }],
      sectorList: ['preferencesService', function(preferencesService) { // but this was not!
        return preferencesService.getSectors();
      }]
    }
  });
northoutIshan commented 7 years ago

Finally my issue is resolved. Please refer this url https://stackoverflow.com/questions/17238759/angular-module-minification-bug

shivamag91 commented 6 years ago

@knutesten thanks, your solution really helped a lot. I was stuck from a while n tried almost every possible solution but not getting the reason behind. Now i get it . Thanks once again.

jhonnyizidoro commented 5 years ago

I'm using Laravel mix and fixed by inserting the config bellow


mix.options({
  uglify: {
    uglifyOptions: {
      mangle: false,
    }
  }
})