I had this directive which has a controller. It looked like this:
.directive('pkSlider', ['$timeout', function ($timeout) {
return {
restrict: 'A',
scope: {
question: '=pkSlider',
options: '=',
onSelect: '&'
},
controller: 'PKSliderController',
templateUrl: function (element, attrs) {
if (attrs.type === 'image')
return 'assets/templates/directives/pk-image.html';
return 'assets/templates/directives/pk-slider.html';
},
link: function (scope, element, attrs, controller) {
// Get our visible answers
scope.answers = controller.getVisibleAnswers(scope.question.answers);
// Increase the answers if they are less than the slides to show
if (scope.answers.length <= scope.options.slidesToShow)
scope.answers = scope.answers.concat(scope.answers);
// Watch our answers
scope.$watch('answers', function (answers) {
// When we have our answers
if (answers.length) {
// Extend our options
angular.extend(scope.options, {
event: {
// Set our first slide and sort/filter our products
init: function (event, slick) {
// Get the actual index stored in the answer
var index = scope.answers[slick.currentSlide].index;
// Set our current slide to the physical current slide, this is for the images to be shown
scope.currentSlide = slick.currentSlide;
// Invoke our methods using the answer index
controller.afterChange(scope.question, index);
scope.onSelect({ index: index, direction: slick.direction });
},
// Set our current slide and sort/filter our products
afterChange: function (event, slick, currentSlide) {
// Get the actual index stored in the answer
var index = scope.answers[currentSlide].index;
// Set our current slide to the physical current slide, this is for the images to be shown
scope.currentSlide = currentSlide;
// Invoke our methods using the answer index
controller.afterChange(scope.question, index);
scope.onSelect({ index: index, direction: slick.direction });
}
}
});
// We have loaded
scope.loaded = true;
}
});
}
};
}]);
and the controller looked like this:
.controller('PKSliderController', ['$timeout', '$interval', 'TrackingProvider', 'AnswerProvider', function ($timeout, $interval, tracking, provider) {
var self = this,
timer,
setActiveImage = function (answers, activeIndex) {
// If we have a current timer running, cancel it
if (timer)
$timeout.cancel(timer);
// For each answer
answers.forEach(function (answer, index) {
// Get our images
var images = answer.images;
// If we have an image
if (images) {
// Get our image
var image = images[0];
// If we are active
if (index === activeIndex) {
// For each text
image.imageText.forEach(function (text) {
text.active = false;
// Activate our text after the delay
$interval(function () {
text.active = true;
}, text.delay, 1);
});
}
}
});
};
// Get our answers
self.getVisibleAnswers = provider.getVisible;
// Updates the question with your selected answer
self.afterChange = function (question, answerIndex) {
// Get our answer
var answers = question.answers,
answer = answers[answerIndex];
// Set our active image
setActiveImage(answers, answerIndex);
// This is for the last step, because some options might not actually be available
if (answer) {
// Set our selected answer
question.radioChoice = answer.text;
}
};
}])
I have been converting my code according to the angular style guide and found a new option for directives that I didn't realise existed called bindToController. So I am trying to implement this. I have converted my directive to this:
(function () {
'use strict';
angular.module('widget.directives').directive('pkSlider', pkSlider);
pkSlider.$inject = ['$timeout'];
function pkSlider($timeout) {
return {
restrict: 'A',
scope: {
question: '=pkSlider',
options: '=',
onSelect: '&'
},
controller: 'PKSliderController',
controllerAs: 'controller',
bindToController: true,
templateUrl: getTemplate
};
function getTemplate(element, attrs) {
if (attrs.type === 'image')
return 'app/directives/pkImage.html';
return 'app/directives/pkSlider.html';
};
}
})();
Which is already much cleaner. I have then tried to convert my controller:
(function () {
'use strict';
angular.module('widget.directives').controller('PKSliderController', PKSliderController);
PKSliderController.$inject = ['$scope', '$timeout', '$interval', 'answerProvider'];
function PKSliderController($scope, $timeout, $interval, answerProvider) {
var self = this,
timer;
// Bindings
self.afterChange = afterChange;
$scope.$watch('controller.answers', watchAnswers);
// Init
init();
function init() {
// Get our answersw
self.answers = answerProvider.getVisible(self.question.answers);
// Increase the answers if they are less than the slides to show
if (self.answers.length <= self.options.slidesToShow)
self.answers = self.answers.concat(self.answers);
};
// Watches the answers for any changes
function watchAnswers(answers) {
// When we have our answers
if (answers.length) {
// Extend our options
self.options = angular.merge(self.options, {
event: {
// Set our first slide and sort/filter our products
init: function (event, slick) {
// Get the actual index stored in the answer
var index = self.answers[slick.currentSlide].index;
// Set our current slide to the physical current slide, this is for the images to be shown
self.currentSlide = slick.currentSlide;
// Invoke our methods using the answer index
controller.afterChange(self.question, index);
self.onSelect({ index: index, direction: slick.direction });
},
// Set our current slide and sort/filter our products
afterChange: function (event, slick, currentSlide) {
// Get the actual index stored in the answer
var index = self.answers[currentSlide].index;
// Set our current slide to the physical current slide, this is for the images to be shown
self.currentSlide = currentSlide;
// Invoke our methods using the answer index
controller.afterChange(self.question, index);
self.onSelect({ index: index, direction: slick.direction });
}
}
});
// We have loaded
self.loaded = true;
}
};
// Updates the question with your selected answer
function afterChange(question, answerIndex) {
console.log('we are changing');
// Get our answer
var answers = question.answers,
answer = answers[answerIndex];
// Set our active image
setActiveImage(answers, answerIndex);
// This is for the last step, because some options might not actually be available
if (answer) {
// Set our selected answer
question.radioChoice = answer.text;
}
};
// Sets the active image
function setActiveImage(answers, activeIndex) {
// If we have a current timer running, cancel it
if (timer)
$timeout.cancel(timer);
// For each answer
answers.forEach(function (answer, index) {
// Get our images
var images = answer.images;
// If we have an image
if (images) {
// Get our image
var image = images[0];
// If we are active
if (index === activeIndex) {
// For each text
image.imageText.forEach(function (text) {
text.active = false;
// Activate our text after the delay
$interval(function () {
text.active = true;
}, text.delay, 1);
});
}
}
});
};
};
})();
But I am having issues with the watch. It doesn't appear to change the options. I have added the options to my view and it returns this:
{"slidesToShow":1,"centerPadding":0,"event":{}}
which is interesting, because I only ever pass this:
{ slidesToShow: 1, centerPadding: 0 }
So it is updating a bit, but not actually binding the methods to the event object. Does anyone know why or even how to get this to work?
It was my fault, it was creating everything correctly. I was not binding it to "controller" in the view.