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?