Unable to get AngularJS service working in Angular - angularjs

I searched a lot of posts and also the official Angular documentation, but I'm not able to get an AngularJS service running in Angular. I finally came to this page https://angular.io/api/upgrade/static/UpgradeModule#examples which seems to explain exactly what I need, but when doing all those steps I'm getting:
ERROR Error: Trying to get the AngularJS injector before it being set.
My impression is that this example is not quite complete. E.g. there is no hint were the (old) AngularJS framework must be loaded. My service looks like angular.module('my-module').service('my-service', ... thus angular needs to be defined, otherwise I'm getting an error. Furthermore many examples assume that the AngularJS code is written in TypeScript. In my case this is not true (just plain Javascript).
Unfortunately with Angular 9 there is an additional issue with the #angular/upgrade module which is not mentioned anywhere and can only be solved by disabling the new Ivy compiler in tsconfig.app.json, otherwise the compiler will throw Error: Error on worker #1: Error: getInternalNameOfClass() called on a non-ES5 class: expected UpgradeComponent to have an inner class declaration:
"angularCompilerOptions": {
"enableIvy": false
}
I'd really appreciate if somebody could post a complete example on what exactly must be done in order to run an AngularJS service in an Angular component.
UPDATE [6th July 2020]
Here you can find a GitHub repo which you can clone, to reproduce the behavior: https://github.com/berkon/angularjs-service-upgrade-test. I should also mention that I'm using the Electron framework and started based on this repo https://github.com/maximegris/angular-electron but I guess that shouldn't matter in this case.

Finally I got it working! It was really really cumbersome to figure this all out. A lot of things aren't mentioned in most tutorials and even in the official Angular guide there are only code snippets which make it hard for Angular newbies to guess where to put all that stuff. Also the bootstraping is not explained correctly. Furthermore all tutorials assume that the "old" AngularJS code is already written in TypeScript, which makes it even harder to find the right way/order to load/bootstrap/import all that stuff. Finally there seems to be an issue with the #angular/upgrade module in combination with the new Ivy compiler in Angular 9. It throws the error mentioned below. Thus it must be disabled to get things working. A real pain!!!
So roughly these are the steps:
install the angular and #angular/upgrade node modules
load all .js modules including AngularJS in the script section of angular.json
interrupt the regular Angular bootstrap process by removing the bootstrap section from #NgModule and bootstrap AngularJS via ngDoBootstrap manually. First bootstrap
AngularJS, afterwards bootstrap the AppComponent class. This way the service is available at AppComponent initialization. Otherwise you'll get an injection error!
Add a new provider in providers [] section to get access to the new service
Now the new (upgraded) service can be injected in the constructor of AppComponent
Its quite a lot of work to perform all steps below manually, but I listed them for reference. Here you can find a GitHub repo where you can clone a working app. Don't be surprised! This repo uses the Electron framework (electronjs.org). But don't worry this doesn't have any influence on my findings: https://github.com/berkon/angularjs-service-upgrade-test
And here is the step-by-step guide:
Prerequistes
execute npm install angular --save
execute npm install #angular/upgrade --save
in tsconfig.app.json add "enableIvy": false to angularCompilerOptions to avoid getting:
Error: getInternalNameOfClass() called on a non-ES5 class: expected UpgradeComponent to have an inner class declaration
add "node_modules/angular/angular.js" and the Javascript file which contains your AngularJS service (in this case "src/app/angular-js-service.js") to the scripts [] array in angular.json
app.module.ts
add ApplicationRef to the import brackets of #angular/core
add import { UpgradeModule } from '#angular/upgrade/static'
add UpgradeModule to imports [] array of #NgModule
remove bootstrap section completely from #NgModule and replace it with this: entryComponents: [AppComponent]
add this to the providers [] array in #NgModule and make sure to replace myService with the correct name of your service:
{ provide: 'myService', useFactory: (i: any) => { return i.get('myService') }, deps: ['$injector'] }
replace the constructor of AppModule with this:
constructor ( public upgradeModule: UpgradeModule ) {}
add this to the AppModule class and make sure to replace ajsAppModule with the name of your AngularJS main app module:
ngDoBootstrap ( appRef: ApplicationRef ) {
this.upgradeModule.bootstrap(document.body, ['ajsAppModule'], { strictDi: true } )
appRef.bootstrap ( AppComponent )
}
app.component.ts
add Inject to the import brackets at #angular/core
in the AppComponent class change the constructor to this and make sure to replace myService with the name of your AngularJS service
constructor ( #Inject('myService') myService: any ) {
myService.doSomething()
}

I had this same error and I solved it in my app, however I cannot remember exactly why this was happening (sorry, it was a long time ago). I wasn't upgrading services, instead I was downgrading.
Here's my app.module.ts I've added comments to the parts that were critical to get this working, I hope there may be a hint for you here. Note that I used the Angular CLI to generate the app.
setAngularJSGlobal(angular);
// Configure the angularjs app (yours might be defined elsewhere)
const app = angular.module('app', [MyFormsModule, AngularMaterialModule]);
app.run(RunAddressAutocompleteConfig);
app.run(RunDynamicQueryConfig);
// Downgrade Angular AppComponent so AngularJS can render it after bootstrapping
// my app used an Angular component as the root
app.directive('appRoot', downgradeComponent({ component: AppComponent }));
// Downgrade Angular services
app.factory('api', downgradeInjectable(ApiService));
app.factory('dynamicQuery', downgradeInjectable(DynamicQueryService));
#NgModule({
declarations: [
AppComponent,
FormDirective,
FormPageComponent,
FormsListPageComponent,
RouterLinkPreserveQueryParamsDirective,
FormEmptyStatePageComponent,
],
imports: [BrowserModule, UpgradeModule, AppRoutingModule, HttpClientModule, CommonModule],
// This was absolutely necessary for bootstrapping my app in this way
// I encountered errors otherwise
providers: [
{
provide: '$scope',
useExisting: '$rootScope',
},
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
entryComponents: [AppComponent],
})
export class AppModule implements DoBootstrap {
constructor(private readonly upgrade: UpgradeModule) {}
ngDoBootstrap(appRef: ApplicationRef) {
this.upgrade.bootstrap(document.body, [app.name], { strictDi: true });
appRef.bootstrap(AppComponent);
}
}
index.html
<body>
<app-root></app-root>
</body>

Related

angularjs directive not being loaded when angularjs-dragula package is required

I am in the process of migrating our .NET Framework project over to .NET Core. And where we previously relied on the BundleTable tools in .NET Framework. We are now using webpack.
I have a directive that uses a package 'angularjs-dragula'. The webpack entry definition is as follows
'bundles/grouping':
[
"./Scripts/angularjs-dragula.js",
"./App/components/grid.directive.js",
"./App/components/inline-edit.directive.js",
"./App/services/grouping.service.js",
"./App/components/grouping/grouping.directive.js"
],
I initialize the directive as follows:
(function () {
angular.module('App').requires.push(angularDragula(angular));
angular
.module('App')
.directive('appCustomGrouping', appCustomGrouping);
appCustomGrouping.$inject = ['urlService', 'groupingService', 'dragulaService' ];
function appCustomGrouping(urlService, groupingService, dragulaService) {
...
As it is, the page never loads grouping.directive. And there are no errors. Unless i remove the dragula file in the webpack entrypoint. The directive will then load, but complain:
ReferenceError: angularDragula is not defined[Learn More]
I have tried relying on webpack to import the package, and removed it from the entry definition. I installed angularjs-dragula into my node_modules, and used
var angularDragula = require('angularjs-dragula');
(function () {
angular.module('App').requires.push(angularDragula(angular));
angular
.module('App')
.directive('appCustomGrouping', appCustomGrouping);
appCustomGrouping.$inject = ['urlService', 'groupingService', 'dragulaService' ];
function appCustomGrouping(urlService, groupingService, dragulaService) {
...
However this results in the same behavior.
The angularjs-dragula package works, since we were using it before the move to webpack. However now it seems to be silently failing, and taking the rest of the directive with it?
How can I begin to diagnose this issue?
The AngularJS wrapper for Dragula is unusual in that it places on global scope a function named angularDragula. That function registers the dragula module with AngularJS when the function is invoked with angular as an argument. It returns a string with the module name "dragula".
angularDragula(angular)
angular.module("app",["dragula"])
.run(function(dragulaService) {
console.log(dragulaService);
})
<script src="//unpkg.com/angular/angular.js"></script>
<script src="//unpkg.com/angularjs-dragula/dist/angularjs-dragula.js"></script>
<body ng-app="app">
<h1>Hello AngularJS!</h1>
</body>
the page never loads grouping.directive
How can I begin to diagnose this issue?
I would use the Developer Console to insert breakpoints. Then examine variables.
The above example loads AngularJS with Dragula and successfully logs the dragularService.

Property 'hot' does not exist on type 'NodeModule'.ts(2339)

I have a purchased react template with the following lines, but its not clear to me whats the purpose of this, the template its in JS and I want to change it to Typescript
The following lines were present in the template
if (module.hot) {
module.hot.accept('./dashApp.js', () => {
const NextApp = require('./dashApp').default;
ReactDOM.render(<NextApp />, document.getElementById('root'));
});
}
However when renamed to .TS, I get this error:
Property 'hot' does not exist on type 'NodeModule'.ts(2339)
What does this code really does? in plain english
This code is related to Webpack's hot module replacement feature (HMR). module.hot works like this:
module.hot.accept(
dependencies, // Either a string or an array of strings
callback // Function to fire when the dependencies are updated
);
So the code you included does this: *when the code of ./dashApp.js or one of the module it requires/imports in the requirement/import tree gets updated, re-render the whole React app.
The hot property on node modules it not standard, thus the TS error - make sure you install the required type definitions! npm install --save-dev #types/webpack-env should do the trick.
Related reads:
Hot Module Replacement concept: high-level Webpack doc on HMR
Hot Module Replacement API low-level Webpack doc on HMR, explaining how to use it and how does module.hot.accept does
Property 'hot' does not exist on type 'NodeModule': Github issue resolving the TS error
Just to add to the accepted answer: After installing #types/webpack-env you might have to add "types": ["webpack-env"] to your tsconfig.json in compilerOptions. Only then it finally worked for me.
So your tsconfig.json would look like this:
{
"compilerOptions": {
...
"types": ["webpack-env"]
},
...
}

Failed to instantiate module ui-mask angulajs with rquirejs

I need to mask my input in my application. I was using ngMask in angularjs. But i found an issue in ngMask is that if we update the mid value the cursor move to end. I tried to resolve the issue but did not find anything. Even on github this issue has reported but still not resolved. Here is the link
ngMask Issue
Then i decided to use another mask library. I found ui-mask and trying to use in my application but i am getting error when i include in my project. My application is in angularjs and i am using requirejs as well to load the modules.
This is what i have done in main.js:
paths: {
...////
"angular-ui-mask": "../../lib/ui-mask/ui-mask.min",
},
'angular-ui-mask': {
deps: ['angular'],
exports: 'angular-ui-mask'
},
in app.js i have done:
var app = angular.module("app",
[
"ui.router",
"mobile-angular-ui",
"scrollable-table", //for alert triggering
"rzModule", //slider for SEMI diagram
"isteven-multi-select",
"ui.bootstrap",
"cgBusy", "dndLists", 'apm-kpi-widget',
'angular.filter',
"pascalprecht.translate",
"jQueryScrollbar",
"ui.checkbox",
"angular-ui-mask"
])
This is exact i was doing for ngMask. But in this case i am getting error
[$injector:modulerr] http://errors.angularjs.org/1.5.5/$injector/modulerr?
at angular.js:38
at angular.js:4587
at q (angular.js:322)
at g (angular.js:4548)
at bb (angular.js:4470)
at c (angular.js:1746)
at Object.yc [as bootstrap] (angular.js:1767)
at HTMLDocument.<anonymous> (angularAMD.js:449)
at i (jquery-1.12.3.min.js?v=1531811705531:2)
at Object.add [as done] (jquery-1.12.3.min.js?v=1531811705531:2)
What's the issue?

Uncaught Error: [$injector:modulerr] Failed to instantiate module yeomanTestApp due to: Error: [$injector:unpr] Unknown provider: e

I'm using yeoman generator for scaffolding angular web application with requirejs. Its working fine but when I tried to concat and minifying all the js file into a single file through grunt task runner its started giving me above mentioned error. I've researched online about the issue and common solution is I may be mis-spelled any service injecting in the module or service does not exists, I've cross checked again all the spelling, quotation marks etc everything seems fine but still I'm unable to resolve this issue.
Here is my app.js file where my main module with dependencies is listed.
return angular
.module('arteciateYeomanApp', [
'arteciateYeomanApp.controllers.MainCtrl',
'arteciateYeomanApp.controllers.AboutCtrl',
'arteciateYeomanApp.services.Xhr',
'arteciateYeomanApp.services.Common',
'arteciateYeomanApp.controllers.ArtworkCtrl',
'arteciateYeomanApp.controllers.AddAccountCtrl',
'arteciateYeomanApp.controllers.AddArtgroupCtrl',
'arteciateYeomanApp.controllers.AddArtistCtrl',
'arteciateYeomanApp.controllers.AddArtworkCtrl',
'arteciateYeomanApp.controllers.AddCampaignsCtrl',
'arteciateYeomanApp.controllers.AddGenreCtrl',
'arteciateYeomanApp.controllers.AddInstitutionCtrl',
'arteciateYeomanApp.controllers.AdminSignupCtrl',
'arteciateYeomanApp.controllers.ArtistInfoCtrl',
'arteciateYeomanApp.controllers.DirectUserSignupCtrl',
'arteciateYeomanApp.controllers.ErrorCtrl',
'arteciateYeomanApp.controllers.ForgotPasswordCtrl',
'arteciateYeomanApp.controllers.GroupBuyingCtrl',
'arteciateYeomanApp.controllers.LoginCtrl',
'arteciateYeomanApp.controllers.AdminLoginCtrl',
'arteciateYeomanApp.controllers.ResetPasswordCtrl',
'arteciateYeomanApp.controllers.SignupCtrl',
'arteciateYeomanApp.controllers.UnblockUserCtrl',
'arteciateYeomanApp.controllers.UpdatePasswordCtrl',
'arteciateYeomanApp.controllers.DashboardCtrl',
'ngRoute','ngResource']).config(.....);
here is grunt task which I'm running for minifying the js files.
registering task
grunt.registerTask('dev', ['requirejs' ]);
Here is task running script
requirejs : {
compile : {
options : {
baseUrl : "<%= yeoman.app %>/scripts",
mainConfigFile : "<%= yeoman.app %>/scripts/main.js",
name : "main",
out : "requireArterciate.js"
}
}
}
Please let me know if I'm doing something wrong here.
If you need to minify the angularjs code, then use the following standard format syntax to define the controller and to inject the dependencies. Refer Dependency Injection
angular.module('test').controller('testController', testController);
testController.$inject = ['$scope', '$rootScope'];
function testController($scope, $rootScope) {};

Can't resolve dependency for module function (restangular.IProvider)

I'm trying to setup restangular for a SPA project but I'm unable to resolve it as a dependency with grunt-tsng. I've installed it using bower (and tsd for typescript definitions from definitelyTyped).
grunt task configuration
tsng: {
options: {
extension: ".ng.ts"
},
dev: {
files: [
// TODO: Automate the generation of this config based on convention
{
src: ["Client/app/**/*.ts", "!**/*.ng.ts"],
dest: "Client/app"
}
]
}
},
Module app.ts
/// <reference path="../../typings/tsd.d.ts" />
module App {
var dependencies = [
"ui.router",
"ui.bootstrap",
"restangular"
...
];
function configuration(
$stateProvider: ng.ui.IStateProvider,
$urlRouterProvider: ng.ui.IUrlRouterProvider,
RestangularProvider: restangular.IProvider, <-- here it fails
...
ts.d.ts (reference file)
/// <reference path="restangular/restangular.d.ts" />
grunt error
Running "tsng:dev" (tsng) task
Warning: Error: Can't resolve dependency for module function App.config with name restangular.IProvider Use --force to continue.
Other dependencies, such as ui-router, ui-bootstrap etc. are working fine. What could be the cause of this? Are there any incompatibilities known with grunt-tsng?
Alright, I figured it out.
When executing grunt-tsng, it scans all dependencies/modules etc. to automagically hook them into angular so you don't have to do it yourself. Here two simple rules apply: Dependencies to inject that are your own (e.g. where you have typescript inplementation code present, such as classes in your modules) can be defined simply with their name. E.g.:
constructor(service: MyModule.IMyService) {}
External dependencies, e.g. from angular itself are declared with the '$' notation. grunt-tsng will assume their dependencies itself are resolved at runtime and thus just hooks them up with their name:
constructor($routeProvider: ng.IRouteService) {}
But now services in restangular aren't named '$restangular', but just 'restangular'. This leads to the following:
When declaring it as 'restangular', grunt-tsng will look for implementations in your modules, doesn't find any and fails
When declaring it as '$restangular', angular will look for providers called '$restangular', doesn't find any and fails
Conclusion:
This is retarded.

Resources