Writing complex AngularJS directive in typescript - javascript

I've found the following directive to select objects from checkboxes:
https://vitalets.github.io/checklist-model/
My problem is that we are using typescript and i have absolutely no idea how to write the given directive in typescript.
I know that the basic style is the following
module myModule {
'use strict';
export function checklistModel(): ng.IDirective {
return {...};
};
};
My problem is that I need the $parse and $compile services to get injected. I've tried to put the code from the directive in the link but I have no idea how to get the directive working.
Could someone please give me a hint on how to inject services and which part of the given code goes in the link and/or the compile?

There is no specific issue with TypeScript regarding dependency injection. Simply define the dependencies you want to inject as parameters of checklistModel. If you want to ensure that the dependencies can be resolved after a minification of the javascript files, you can define the dependencies additionally via the $inject property (This will work in normal js as well):
module myModule {
'use strict';
checklistModel.$inject = ['$parse', '$compile'];
export function checklistModel($parse, $compile): ng.IDirective {
return {
link: function() { ... }
};
};
angular.module('myModule').directive('checklistModel', checklistModel);
};
Everything else is normal angular stuff. (Beside that, the type IDirective will tell you how the return-value should look like)
I hope this will help.

Related

jsDoc - set type without importing file

I'm using Angular 1 in VScode. I have a main file that defines all my services, controllers etc and require's them against Angular.
angular.module('myApp', [])
.service('myService', require('./myService.js')
.controller('myController', require('./myController.js');
and the service:
class MyService {
constructor() {}
}
module.exports = MyService;
and my controller
class MyController {
constructor(myService) {
/** #type {MyService} */
this.myService = myService;
}
}
module.exports = MyController;
As a result intellisense has no idea what myService is within the controller. I had hoped that the jsDoc comment would have solved this issue as both files are within the same work-space but it does not.
I have seen the type-def comment and tried writing a custom type in the service file :/** {MyService} CustomService */ then reference the CustomService type in my controller but that doesn't work either.
In Short
Is it possible, using jsDoc in VSCode to reference a class from a different file without requiring it into the current working file.
Thanks for any insight all.
No, as of VScode 1.14 this is not possible but we are tracking a discussion of this functionality here: https://github.com/Microsoft/TypeScript/issues/14377

Inject angularjs directive

I have this structure in my script A:
module.exports = angular.module('myApp').controller(..).directive(..)
I want to inject additional directive so that I have something like this:
module.exports = angular.module('myApp').controller(..).directive(..).directive(..)
I want to do this from the outside of the script A.
Any ideas how this can be achieved? I am still catching up with the angular, and any help is really appreciated! Thanks a lot!
If I understand correctly, you want to create your directive dynamically. (within different angular module) You can code in the way blow,
//dynamic directive dyDir.js
module.exports = function (app) {
app.directive(...)
};
your script
var dyDir = require('./dyDir.js');
var yourApp = angular.module('appName',[]);
yourApp.controller('testCtrl', ...)
dyDir(yourApp); //parse angular module instance as parameter
Although this would work, but I really don't think use angular.module and the commonjs module at the same time is a good practice, coz this would make the other developer so confused.
Hope this would solve your problem. : )
I got this solved in a following way - if it can be of any help to anyone:
Assume you have an existing module myModule, and two controllers myController1 and myController2 (code for the two controllers is in files controller1.js and controller2.js). This is your code in a file myapp.js:
module.exports = angular.module("myModule", [])
.controller('myController1', require('./controller1.js'))
.controller('myController2', require('./controller2.js'))
Assume you would like to inject additional directive into your module myModule. You would reuse that module.
You would create a new file with the following content:
require('./myapp.js');
require('./mydirective.js'); //this is your new directive
var app = angular.module("myModule"); //get an existing module
app.directive('directiveName', function() {
return {
...
}
})

angularjs defining services for the same module in different files

I have two files in which I define services in my angular app, but when I try to use them both in my directive, I get an error saying the service provider is not found for whichever directive I define second. It seems like one service is overwriting the other. If I change the module definition in service2.js to myapp.services2, then it works. I would think I could add multiple factories to the same module this way. Can someone point out what I'm doing incorrectly?
service1.js:
var services = angular.module('myapp.services',[]);
services.factory('Service1', function() {
// service code
});
service2.js:
var services = angular.module('myapp.services',[]);
services.factory('Service2', function() {
// service code
});
mydirective.js:
angular.module('myappdirective', []).directive('myapp', ['Service1', 'Service2',
function(service1,service2) {
// directive code
}]);
This is from the docs:
Beware that using angular.module('myModule', []) will create the module myModule and overwrite any existing module named myModule. Use angular.module('myModule') to retrieve an existing module.
Found here:
https://docs.angularjs.org/guide/module
This is possible, however will be error prone, hence not recommended
Make small modification to what you are already doing
Just do not re-declare the module variable in other files other than service1.js or put the module definition to a file of its own and include these JS file in the order of Module.js, services.js, directive.js then it will work

Understanding injection dependency in app and tests in AngularJS

I have a dependency injection (understanding) problem while testing a directive (AjaxLoader displayed only when there is a pending request).
App declaration :
angular.module('app', [
'directives.ajaxLoader',
'services.httpRequestTracker',
[...]
])
Directive code :
angular.module('directives.ajaxLoader', [])
.directive('ajaxLoader', ['httpRequestTracker',
function(httpRequestTracker) {
return {
templateUrl: 'common/ajaxLoader.tpl.html',
link: function($scope) { // This function can have more parameters after $scope, $element, $attrs, $controller
$scope.hasPendingRequests = function() {
return httpRequestTracker.hasPendingRequests();
};
}
};
}
])
Test code :
describe('ajaxLoader', function() {
beforeEach(function() {
module('directives.ajaxLoader', 'common/ajaxLoader.tpl.html');
});
describe('ajaxLoader directive', function() {});
});
From there, my directive works perfectly well in the browser, but tests fails with an error like :
Error: [$injector:unpr] Unknown provider: httpRequestTrackerProvider
<- httpRequestTracker <- ajaxLoaderDirective
Ok, so I need to inject my dependency somewhere. I have two solutions :
in my directive directly :
angular.module('directives.ajaxLoader', [
'services.httpRequestTracker'
])
in my test code directly :
beforeEach(function() {
module('directives.ajaxLoader', 'common/ajaxLoader.tpl.html', 'services.httpRequestTracker');
});
Both works, but I don't understand which one is the better and why ? And why is it working in my browser from the start and fails in my test ? In both case, all my directives and trackers are injected in my main app declaration
Thanks
Loading modules
It works in your application because services.httpRequestTracker is loaded. you did that by declaring it as a dependency of the main app module (your first code snippet).
However, when you test things, you want to mock everything that is not being tested to avoid biass. In your case, what if you had a problem in services.httpRequestTracker? ajaxLoader might be fine but your tests will fail.
Mocking
To mock everything else, you have two options:
spies (e.g. this http://angular-tips.com/blog/2014/03/introduction-to-unit-test-spies/ )
use dependency injection to substitute components
To use a dependency, you have to load the module with module().
you will have to load the dependency, but this might have a mock implementation.
Dependency Injection
Dependency injection lets you decouple classes. There is a service locator to resolve dependencies by name. That is, you say attribute a of class C is of type 'animal' (a string!). The service locator in the angular core finds which component implements it. A way of determining this is by looking up the loaded modules (e.g. dependencies of the main app module).
You didn't define any of this in your testing area (but it isn't a problem!). Karma uses a file karma.conf that contains a list of files to use. You might use this file to add libraries or mocked components.
With your particular problem:
The directive depends on httpRequestTracker. If you don't inject it there, it won't work (so it's ok).
In your test, you have to load both. That's why the first time it failed and the second it worked. However, instead of loading httpRequestTracker, I'd load a mock implementation of it.

Create a Reusable AngularJS provider/directive/factory, etc

I am attempting to create a number of AngularJS libraries that can be re-used across apps and modules. I thought i was doing everything correctly but am still having a problem.
First I create a file that defines a generic module (app.ui) and attaches a provider (LayoutManager) to it ... (I am using a jQuery plugin called "jQuery Layout". This provider allows the app to access and manipulate the layout parameters. I don't want to get hung up on the details of the plugin however. The question is more general, but thought I should at least provide some explanation)
angular.module("app.ui", [])
.provider('LayoutManager', [function () {
'use strict';
var appLayout = $('body').layout(),
moduleNavLayout = $('.module-nav-container').layout(),
moduleLayout = $('.module-content-container').layout();
return {
$get: function () {
return {
ApplicationLayout: appLayout,
ModuleNavigatonLayout: moduleNavLayout,
ModuleContentLayout: moduleLayout
}
}
}
}]);
Then I identify the module (app.ui) as a dependency of the "app" (ListMgrApp) I want to use it in.
angular.module("ListMgrApp", ["ui.router", "app.services", "app.ui"])
Then I inject (is that the correct terminology?) the specific provider (LayoutManager) into the application ...
angular.module("ListMgrApp", ["ui.router", "app.services", "app.ui"]).
config(['$stateProvider', 'LayoutManager',
function ($stateProvider, layout) {
'use strict';
// initialization code goes here
}]);
While it appears that the code inside the LayoutManager provider DOES execute, which I believe is due to it being included as a dependency for the app, I am still getting the following error from the application when it runs.
Uncaught Error: [$injector:modulerr] Failed to instantiate module ListMgrApp due to:
Error: [$injector:unpr] Unknown provider: LayoutManager
I have verified that the source code for all required files are being successfully down loaded.
What am I missing?
RESOLUTION
Thanks elclanrs for the answer! I just wanted to add what exactly I updated to make it work.
I added "Provider" to the name of the provider (LayoutManager) in the config() method of the app (ListMgrApp). I had originally thought I was supposed to also change the name of "LayoutManager" in the provider code but misread the original solution comment. Only change the name in the app config() method. I thought I would point it out here just in case someone else was a "skimmer" and missed it.
angular.module("ListMgrApp", ["ui.router", "app.services", "app.ui"]).
config(['$stateProvider', 'LayoutManagerProvider',
function ($stateProvider, layout) {
'use strict';
// initialization code goes here
}]);

Categories

Resources