controllerAs、bindToController 和 watches
controllerAs, bindToController and watches
我有一个带有控制器的指令。它看起来像这样:
.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;
}
});
}
};
}]);
控制器看起来像这样:
.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;
}
};
}])
我一直在根据 angular style guide 转换我的代码,并找到了一个我没有意识到的新指令选项,叫做 bindToController。所以我正在尝试实现这一点。
我已将我的指令转换为:
(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';
};
}
})();
这已经干净多了。
然后我尝试转换我的控制器:
(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);
});
}
}
});
};
};
})();
但是我的手表有问题。它似乎没有更改选项。我已将选项添加到我的视图中,它 returns this:
{"slidesToShow":1,"centerPadding":0,"event":{}}
这很有趣,因为我只通过了这个:
{ slidesToShow: 1, centerPadding: 0 }
所以它更新了一点,但实际上并没有将方法绑定到事件对象。有谁知道为什么甚至如何让它工作?
您只需要在 self.answers
上观看,因为它可以通过绑定 this
或 self
直接在控制器实例上使用,如下所示:
$scope.$watch(angular.bind(self, function () {
return self.answers;
}), watchAnswers);
这是我的错,它正在正确创建所有内容。我没有将它绑定到视图中的 "controller"。
我有一个带有控制器的指令。它看起来像这样:
.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;
}
});
}
};
}]);
控制器看起来像这样:
.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;
}
};
}])
我一直在根据 angular style guide 转换我的代码,并找到了一个我没有意识到的新指令选项,叫做 bindToController。所以我正在尝试实现这一点。 我已将我的指令转换为:
(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';
};
}
})();
这已经干净多了。 然后我尝试转换我的控制器:
(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);
});
}
}
});
};
};
})();
但是我的手表有问题。它似乎没有更改选项。我已将选项添加到我的视图中,它 returns this:
{"slidesToShow":1,"centerPadding":0,"event":{}}
这很有趣,因为我只通过了这个:
{ slidesToShow: 1, centerPadding: 0 }
所以它更新了一点,但实际上并没有将方法绑定到事件对象。有谁知道为什么甚至如何让它工作?
您只需要在 self.answers
上观看,因为它可以通过绑定 this
或 self
直接在控制器实例上使用,如下所示:
$scope.$watch(angular.bind(self, function () {
return self.answers;
}), watchAnswers);
这是我的错,它正在正确创建所有内容。我没有将它绑定到视图中的 "controller"。