I have a directive that is written in es6. I need some services to be injected into this directive controller.
In es5, I would do something like:
function MyDirective() {
function controller(commonService) {
commonService.doSomething(this.type);
};
return {
scope: {
type: '='
},
restrict: 'E',
controller: ['commonService', controller],
controllerAs: 'vm',
templateUrl: 'myTemplate.html',
bindToController: true
};
}
angular.module('myApp').directive('myDirective', ['commonService', MyDirective]);
That way, in ES5 everything used to works just fine.
while in es6, I do:
class MyDirective {
constructor(commonService) {
this.scope = {
type: '='
};
this.restrict = 'E';
this.controllerAs = 'vm';
this.templateUrl = 'myTemplate.html';
this.bindToController: true;
}
controller() {
commonService.doSomething(this.type);
}
}
angular.module('myApp').directive('myDirective', [('commonService') => MyDirective(commonService)]);
The problem now is: I can no longer inject commonService into my controller.
I have tried to use
this.commonService = commonService;
in the constructor function, but unfortunatlly, I don't have access to "this" inside the controller for some odd reason. (Isn't that the whole point of having a class in the first place?)
How do I inject my commonService into the controller function, or alternatively, how do I gain access to "this" from the controller function?
Thanks!
One option is to define the controller as a class.
The DEMO
class MyDirective {
constructor() {
this.scope = {
type: '#'
};
this.restrict = 'E';
this.controller = 'myDirectiveCtrl',
this.controllerAs = 'vm';
this.template = `
<fieldset>
myDir type={{vm.type}}
<br> Service {{vm.serviceType}}
</fieldset>`;
this.bindToController = true;
}
}
class MyDirectiveCtrl {
constructor(commonService) {
this.commonService = commonService;
}
$onInit() {
this.serviceType = this.commonService.doSomething(this.type);
}
}
MyDirectiveCtrl.$inject = ['commonService'];
angular.module('myApp',[])
.directive('myDirective', MyDirective)
.controller("myDirectiveCtrl", MyDirectiveCtrl)
.value("commonService", {
doSomething: (type) => ("--"+type+"--")
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp">
<h1>ES6 Directive Demo</h1>
<my-directive type="IDK"></my-directive>
</body>
Related
Im trying to convert this directive into a component in typescript. I saw a couple of videos and articles on how to do so, but most of them are in javascript so it is a bit unclear.
Here is the code:
export class TableRowBSGroupDirective implements ng.IDirective {
restrict: string = 'A';
scope: any = {
dirvm: '=',
grouplvl: '=',
classlvl: '#'
};
templateUrl: any = balanceSheetFSPolicy.dirvmConstant.TableRowGroupTmpl;
controller: any = ($scope: any) => {
balanceSheetFSPolicy.balanceSheetFSViewModel = $scope.dirvm;
$scope.balanceSheetFSPolicy = balanceSheetFSPolicy;
};
static factory(): ng.IDirectiveFactory {
const directive = function () {
return new TableRowBSGroupDirective();
};
return directive;
}
}
angular
.module('app.recon.statements')
.directive('tableRowBsGroup', TableRowBSGroupDirective.factory());
export class TableRowBSGroupCtrl {
// Dependency Injection
static $inject: [string] = [
'$scope'
// Apply all the dependancies for the component here
// $scope as an example
];
// Access Bindings
protected dirvm: any;
protected grouplvl: any;
protected classlvl: any;
constructor(private $scope: ng.IScope) {
// Place the Logics here which are in the controller function in your directive
}
public static factory() {
// Here goes your static function body
}
}
const options = {
templateUrl: //the path for the template,
bindings: {
dirvm: '=',
grouplvl: '=',
classlvl: '#'
},
controller: TableRowBSGroupCtrl
};
export default (ngModule: any) => {
ngModule.component('TableRowBSGroupCmp', options);
};
Students.html
<div class="row">
<student info="ui.Ram"></student>
</div>
Student.directive Template
<h1>{{ui.name}}</h1>
Route Config
app.config(urlRouter);
function urlRouter($routeProvider, $locationProvider) {
$routeProvider
.when('/students', {
templateUrl: 'app/views/students.html',
controller: 'prodCtrl',
controllerAs: 'ui'
})
}
Custom Directive
app.directive('student', "student");
function student() {
var directive = {};
directive.restrict = 'E';
directive.templateUrl = "Student.directive.html";
directive.scope = {
ui : "=name"
}
return directive;
});
Controller
app.controller('StudentController', StudentController);
function StudentController($scope) {
$scope.Ram= {};
$scope.Ram.name = "Mahesh";
};
When I do this way, name ("Mahesh") is reflected in UI.
I am thinking to do the same without injecting $scope in the controller.
Something like this.
function StudentController() {
var vm = this;
vm.Ram= {};
vm.Ram.name = "Mahesh";
return vm;
};
But the value is not reflecting.
You need to use controller as syntax in view to do that:
<div ng-app = "app" ng-controller = "StudentController as ctrl">
<student name = "ctrl.Ram"></student>
</div>
I have following (simplified) directive, and I want to write it with Typescript (still not Angular 2.0).
angular
.module('app.components')
.directive('list', List);
function List() {
return {
restrict: 'E',
templateUrl: 'modules/components/List/List.html',
controller: ListController,
controllerAs: 'vm',
require: ['ngModel', '?^form'],
bindToController: true,
link: LinkFunction,
scope: {
'id': '#',
'name': '#'
}
};
function LinkFunction(scope, element, attrs, controllers) {
var ngModelCtrl = controllers[0];
var formCtrl = controllers[1];
ngModelCtrl.$isEmpty = function(value) {
return !value || value.length === 0;
};
ngModelCtrl.$render = function() {
scope.vm.form = formCtrl;
scope.vm.model = ngModelCtrl;
scope.vm.selected = ngModelCtrl.$modelValue;
};
// ... more controller functions
}
})();
Now, how can I inject the controllers into my Typescript Code:
const template = require('./List.html');
export default class ListComponent{
static selector = 'list';
static definition: ng.IComponentOptions = {
template: template,
controller: ListComponent,
bindings: {
'id': '#',
'name': '#'
}
};
id;
name;
constructor(private controllers) { // <-- not working
'ngInject';
}
$onInit() {
let ngModelCtrl = this.controllers[0];
let formCtrl = this.controllers[1];
ngModelCtrl.$isEmpty = function(value) {
return !value || value.length === 0;
};
ngModelCtrl.$render = function() {
this.model = ngModelCtrl;
this.form = formCtrl;
this.selected = ngModelCtrl.$modelValue;
};
}
}
Hope someone knows the answer or can give me a hint how to achieve my goal, because I searched the web for hours, but didn't find any solution for this.
Use "require" like explained here https://docs.angularjs.org/guide/component
See Intercomponent Communication
I am relatively new to angularJS, I am trying to set up a page where inturn multiple pages are called depending upon the selection made previously.
All the pages have their own controller, so I am trying to set the controller and view src through the javascript and using them in HTML tags.
Following is what I am doing:
HTML page:
<div ng-if="sidebarName=='sidebar-device-wire'">
<div ng-controller="getSidebarCtlr">
<div ng-include src="sidebarSrc"></div>
</div>
</div>
javascript:
$scope.sidebarSrc="views/sidebars/sidebar-device.html";
$scope.sidebarCtlr="SidebarDeviceCtrl";
$scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
For some reason though, this does not work. i can get the HTML page but the controller is not being called. Can anyone please tell me what I am doing wrong?
I would also recommend to use ngRoute or ui.router because there are many features that aren't easy to implement from scratch (like named views, nested views / nested states or resolves) and these modules are well tested.
Not sure why your controller isn't running but I guess that the expression of the controller is evaluated before your controller that is setting the name is running. So it will be always undefined at compile time.
But if you really like to implement a very basic router you could do it like in the following demo (or in this fiddle).
Update 21.12.2015
Here are some router examples that I wrote for other SO questions:
simple ui.router example - jsfiddle
more complex nested state example ui.router - jsfiddle
dynamic link list with ngRoute - jsfiddle
Please also have a look at ui.router github pages to learn more about it.
angular.module('simpleRouter', [])
.directive('simpleView', simpleViewDirective)
.provider('simpleRoutes', SimpleRoutesProvider)
.controller('MainController', MainController)
.controller('HomeController', HomeController)
.config(function(simpleRoutesProvider) {
simpleRoutesProvider.state([{
url: '/',
templateUrl: 'home.html',
controller: 'HomeController'
}, {
url: '/view1',
templateUrl: 'view1.html'
}, {
url: '/view2',
templateUrl: 'view2.html',
controller: function($scope) {
$scope.test = 'hello from controller'
}
}]);
simpleRoutesProvider.otherwise('/');
})
function HomeController($scope) {
$scope.hello = 'hello from home controller!!';
console.log('home controller started')
}
function MainController($scope) {
$scope.hello = 'Main controller test';
}
function simpleViewDirective() {
return {
restrict: 'EA',
scope: {},
template: '<div ng-include="templateUrl"></div>',
controller: function($scope, $location, $controller, simpleRoutes) {
var childControllerInst;
$scope.templateUrl = simpleRoutes.currentRoute.templateUrl || simpleRoutes.otherwise.templateUrl;
$scope.$watch(function() {
return $location.path();
}, function(newUrl) {
//console.log(newUrl)
$scope.templateUrl = simpleRoutes.changeRoute(newUrl);
childControllerInst = $controller(simpleRoutes.currentRoute.controller || function() {}, {$scope: $scope});
});
$scope.$on('$destroy', function() {
childControllerInst = undefined;
})
},
link: function(scope, element, attrs) {
}
}
}
function SimpleRoutesProvider() {
var router = {
currentRoute: {
templateUrl: ''
},
states: [],
otherwise: {},
changeRoute: function(url) {
var found = false;
angular.forEach(router.states, function(state) {
//console.log('state', state);
if (state.url == url) {
router.currentRoute = state;
found = true;
}
});
if (!found) router.currentRoute = router.otherwise;
//console.log(router.currentRoute);
return router.currentRoute.templateUrl;
}
};
this.state = function(stateObj) {
router.states = stateObj;
};
this.otherwise = function(route) {
angular.forEach(router.states, function(state) {
if (route === state.url ) {
router.otherwise = state;
}
});
//console.log(router.otherwise);
};
this.$get = function simpleRoutesFactory() {
return router;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simpleRouter" ng-controller="MainController">
<script type="text/ng-template" id="home.html">home route {{hello}}</script>
<script type="text/ng-template" id="view1.html">view1</script>
<script type="text/ng-template" id="view2.html">view2 {{test}}</script>
<div simple-view="">
</div>
home
view1
view2
<br/>
{{hello}}
</div>
What's that code means? $scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
the ng-directive requires a Controller name, its argument type is string and you cannot pass a simple function, you need to register a valid controller associating it to a module via the controller recipe.
https://docs.angularjs.org/guide/controller
angular.module('test', []).controller('TestCtrl', function($scope) {
$scope.greetings = "Hello World";
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<article ng-controller="TestCtrl">{{ greetings }}</article>
</section>
You can see a snippet of some code i am working on below.
angular.module('myApp', [])
.directive('maskedInput', [
function() {
return {
scope: {
mymask: '=maskedInput'
},
link: function(scope, elem, attrs) {
console.log('attributes', attrs);
scope.$watch('mymask', function(newVal) {
console.log(newVal);
elem.inputmask(newVal);
}, true)
}
}
}
])
.controller('MainCtrl', ['$scope',
function($scope) {
$scope.mask = {
mask: '99999[-9999]',
greedy: false
};
}
]);
<script src="https:/cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script src="https://rawgit.com/RobinHerbots/jquery.inputmask/3.x/dist/jquery.inputmask.bundle.js"></script>
<body>
<div ng-app="myApp" ng-controller="MainCtrl">
<h3>Mask</h3>
<p>Mask: 99999[-9999]</p>
<input type="text" ng-model="test" masked-input="mask">
<br />
<input type="text" ng-model="mask.mask" />
<br />{{mask}}
<br/>{{test}}
</td>
</body>
I am working on recreating this in typescript. Here's what I have so far.
class InputMaskDirective implements ng.IDirective {
constructor () {
var directive: ng.IDirective = {};
directive.restrict = 'A';
directive.scope = { mymask : '=maskedInput' };
directive.link = (scope: any, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
scope.$watch('mymask', function (newVal) {
element.inputmask(newVal);
}, true);
};
return directive;
}
}
export = InputMaskDirective;
We are using requirejs as well, so when we instanciate the class in our app.ts file, we do something like
import MaskedInputDirective = require("src/common/directives/inputmaskdirective");
var application: angular.IModule = angular.module("application",[InjectionStuffHere]);
application.directive("maskedInput",[MaskedInputDirective]);
export = application;
The problem i forsee is that because of the "class" structure of requirejs and the typescript code, in theory a user can name the directive whatever they want.
Wouldn't that break the code with the scope? Since the scope is looking for the masked-input, the snake cased name of the directive, to be in the html, if the user named it something else, how would it know the correct name?
I've tried attrs to see if I can get something out of it, but no luck. Do I just have to hope that the users don't muck with it when they create the code?
I follow this structure when writing angular directives in typescript.
Factories and Directives are created as functions, where directive-functions return a :ng.IDirective instead.
function inputMaskDirective(): ng.IDirective {
return {
restrict: "A",
scope: {myMask: "=maskedInput"},
//and on
}
}
if you have any injects add them to the property$inject of the function
inputMaskDirective.$inject = ["someService"];