Hi I am using Angular with ES6, now I want to get rid of the $scope since Angular2 will not use it anymore and I want to create futureprove code,... this works:
let tab = this.tabManager.getArea.open({
controller: ['$scope', function (scope) {
console.log(scope);
scope.close = function () {
tab.close();
}
}],
template: '<component-name on-close="close()" ...></component-name>'
});
but how do I rewrite it without injecting the scope, I thought about something like this:
let tab = this.tabManager.getArea.open({
controller: [class {
constructor() {
console.log('construct');
}
close() {
console.log('close');
tab.close();
}
}],
template: '<component-name on-close="close()" ...></component-name>'
});
But it does not seem to work properly, the construtor is called up, however the binding for on-close does not seem to work.
the controller class in 1.6 exposes $ctrl object , can access "close()" using that.try once
Related
Same issue as here: AngularJS directive binding a function with multiple arguments
Following this answer: https://stackoverflow.com/a/26244600/2892106
In as small of a nutshell as I can.
This works:
<my-directive on-save="ctrl.saveFn"></my-directive>
With:
angular.module('app')
.controller('MyController', function($scope) {
var vm = this;
vm.saveFn = function(value) { vm.doSomethingWithValue(value); }
});
But when I convert to Typescript and use real classes, this breaks.
class MyController {
constructor() {}
saveFn(value) {
this.doSomethingWithValue(value);
}
}
In the Typescript version when debugging, "this" is referencing the Window global. So my scope is messed up somehow, but I don't know how to fix it. How can I get "this" to refer to MyController as expected?
So my scope is messed up somehow
Use an arrow function instead of a method.
Fixed
class MyController {
constructor() {}
saveFn = (value) => {
this.doSomethingWithValue(value);
}
}
More
https://basarat.gitbooks.io/typescript/content/docs/arrow-functions.html
so, I'm trying to use the 1.5 component feature, but coding with fat arrows. I am using babel to build the system
My component, stripped down to the bare minimum to show my problem, is thus:
angular.module('myApp')
.component('myComponent', {
controller: () => {
this.$onInit = () => {};
},
template: `<p>foobar1</p>`
});
when I try and load this component, I get an error complaining about
typeError: Cannot set property '$onInit' of undefined
so, when I look at the sources in chrome devtools, I see
angular.module('myApp').component('myComponent', {
/** #ngInject */
controller: function controller() {
undefined.$onInit = function () {};
},
template: '<p>foobar1</p>'
});
I would expect that I have done something very wrong, but can't see it ;)
anyone got any tips ?
thanks
Angular creates new instantion of controller for every component. In ES5 we dont have classes, so we pass construction function here.
But in es6 we have class, so you can use it instead
let myComponent = {
controller: myComponentController,
template: `<p>foobar1</p>`
};
class myComponentController{
constructor() {
this.answer = 41;
}
$onInit() {
this.answer++;
}
};
angular.module('myApp')
.component('myComponent', myComponent);
Pascal has also written something about it here: http://blog.thoughtram.io/angularjs/es6/2015/01/23/exploring-angular-1.3-using-es6.html
Basically we have a lot of controllers and templates and we wanted to dynamically load both the controller and the template for a given route.
Something along the lines of:
$routeProvider.when('/page/:page', {
templateUrl: fuction(params) {
return '/templates/pages/' + params.page;
},
controller: function(params) {
return params.page + 'Controller';
}
})
However we don't receive the params argument for the controller function. Is there any way to achieve this kind of flexibility?
I guess you can use the $injector.instantiate function which will invoke the given function using new with the dependencies injected. Since a controller is used as a constructor you just need to wrap the controller function like this:
controller: function () {
return $injector.instantiate(function (params) { ... });
}
This is just a guess, I don't guarantee it will work.
I've written a pretty simple test app as follows:
angular.module('tddApp', [])
.controller('MainCtrl', function ($scope, $rootScope, BetslipService) {
$scope.displayEvents = [
{
id: 1,
name: 'Belarus v Ukraine',
homeTeam: 'Belarus',
awayTeam: 'Ukraine',
markets: {home: '2/1', draw: '3/2', away: '5/3'},
display: true
}
];
$scope.betslipArray = BetslipService.betslipArray;
$scope.oddsBtnCallback = BetslipService.addToBetslip;
$scope.clearBetslip = BetslipService.clearBetslip;
})
.directive('oddsButton', function () {
return {
template: '<div class="odds-btn">{{market}}</div>',
replace: true,
scope: {
market: '#',
marketName: '#',
eventName: '#',
callback: '&'
},
link: function (scope, element) {
element.on('click', function() {
scope.callback({
name: scope.eventName,
marketName: scope.marketName,
odds:scope.market
});
});
}
};
})
.factory ('BetslipService', function ($rootScope) {
var rtnObject = {};
rtnObject.betslipArray = [];
rtnObject.addToBetslip = function (name, marketName, odds) {
rtnObject.betslipArray.push({
eventName: name,
marketName: marketName,
odds: odds
});
};
rtnObject.clearBetslip = function () {
rtnObject.betslipArray = [];
};
return rtnObject;
});
I've assigned an array to a controller variable. I've also assigned functions to modify the array. To add an object to the array the callback is called by a directive with isolate scope. There's some strange behaviour happening that I don't quite understand:
=> clicking the directive runs the callback in the service. I've done some debugging and it seems that the controller variable is updated but it doesn't show in the html.
=> clicking the button to clear the array isn't working as expected. The first time it's causing an element to display, after which it has no effect.
I think that this may have to do with the nested ng-repeats creating their own scopes
NB
I fixed the array not clearing by changing the function in the service to:
while (rtnObject.betslipArray.length > 0) {
rtnObject.betslipArray.pop();
}
// instead of
rtnObject.betslipArray = [];
This makes sense as the service variable was being pointed at a new object while the old reference would persist in the controller.
I got the html to update by wrapping the callback call in the directive in a scope.$apply().
This part I dont really understand. How can scope.$apply() called in the directive have an effect on the controller scope when the directive has an isolate scope? updated fiddle: http://jsfiddle.net/b6ww0rx8/7/
Any thought's greatly appreciated
jsfiddle: http://jsfiddle.net/b6ww0rx8/5/
C
I got it working here: http://jsfiddle.net/b6ww0rx8/8/
Added $q, $scope.$emit and $timeout clauses to help with communications between your directive / service and controller.
I would like to also say that I wouldn't assign service functions to a controller $scope, You should define functions in the controller that call service functions.
Instead of this:
$scope.clearBetslip = BetslipService.clearBetslip;
Do this:
$scope.clearBetslip = function(){
BetslipService.clearBetslip().then(function(){
$scope.betslipArray = BetslipService.getBetslipArray();
});
};
In an angular app running on 1.3.0-beta.15, I have a directive that looks like this:
.directive('digester', function() {
return {
scope: {
object: '=',
digestTrigger: '='
},
templateUrl: 'some-template.html,
link: function(scope) {
scope.digestTrigger = function() {
scope.$digest();
}
}
};
});
a template that looks like this:
<div>{{object.title}}</div>
a service somewhere that looks like this:
.service('serviceToCallDigest', function() {
return {
object: {
title: 'someTitle'
},
digestTrigger: function() {},
someFunction: function() {
this.object.title = 'newTitle';
this.digestTrigger();
}
};
});
and a controller that gets the service injected like this:
.controller('someCtrl', function($scope, serviceToCallDigest) {
$scope.serviceToCallDigest = serviceToCallDigest;
}
and wires up the directive like this:
<digester
digest-trigger="serviceToCallDigest.digestTrigger"
object="serviceToCallDigest.object">
</digester>
Whats happening here is, as the directive gets the digestTrigger injected (as an empty function) it sets the function to fire the directives local $digest() function, and by that enabeling code from outside the directive to trigger the local digest whenever appropriate.
I know that $apply() is adviced to use, which fires $digest() at the $rootscope level, but I'm in a perfomance critical situation, where I need to update directives locally.
This setup has worked fine until upgrading to 1.3.0-beta.16 or newer versions. Now the new object.title is not passed to the directive and updated in the view, when someFunction is called.
Does anyone know why this is the case and what to do to work around it?