我已经创建了一个自定义指令来渲染问题的滑块(基本上是包装jquery ui滑块)。该指令采用ngModel并在用户使用滑块时更新它,并且有一个$ watch附加到父模型(传递给指令的ngModel只是父模型的一部分)。该指令在页面上有多个实例。
我遇到了手表的问题,因为看起来手表总是出现在页面的最后一个问题上。因此,例如,使用问题1上的滑块的10个问题的页面 - 在最后一个问题(问题10)上触发监视。我认为这个问题与指令/隔离范围和/或监视功能有关,但我无法解决。
this.app.directive('questionslider', () => {
var onChangeEvent = (event, ui) => {
updateModel(ui.value);
};
var onSlideEvent = (event, ui) => {
updateUi(event, ui);
};
var updateUi = (event, ui) => {
$(ui.handle).find(".ff-handle-glyph > div").css("top", (ui.value) * - 10);
}
var updateModel = (newValue) => {
// find value in values list...
angular.forEach(isolatedScope.model.PossibleValues, function(value) {
if (parseInt(value.Name) === newValue) {
isolatedScope.$apply(function() {
isolatedScope.model.Value = value;
});
}
});
};
var isolatedScope: any;
return {
restrict: 'AE',
replace: true,
template: '<div></div>',
scope: {
model: '=ngModel',
},
link: function(scope, element, attrs, ctrl) {
isolatedScope = scope;
scope.$watch(ngModelCtrl, function() {
// use provided defaultValue if model is empty
var value = isolatedScope.model.Value === null ? isolatedScope.model.DefaultValue : isolatedScope.model.Value;
element.slider({
min: 0,
max: isolatedScope.model.PossibleValues.length,
value: value.Name,
change: onChangeEvent,
slide: onSlideEvent
});
}
}
};
};
在控制器中添加监视的代码
this.$scope.questions.forEach(function(question) {
this.$scope.$watch(
function() { return question; },
function(newVal, oldVal) { this.updateQuestion(newVal, oldVal) },
true
);
});
UpdateQuestion函数(现在只输出当前问题)
function updateQuestion(newVal, oldVal) {
// prevent event on initial load
if (newVal === oldVal) {
return;
}
console.log(newVal);
}
实例化questionliders的ng-repeat标记
<div data-ng-repeat="question in Questions">
<h4>{{question.QuestionText}}</h4>
<p>{{question.RangeMinText}}</p>
<questionslider ng-model="question"></questionslider>
<p>{{question.RangeMaxText}}</p>
</div>
问题JSON看起来像这样
{
"DefaultValue": {
"Id": "5",
"Name": "5"
},
"Id": "1",
"IsAnswered": false,
"PossibleValues": [
{
"Id": "1",
"Name": "1"
},
{
"Id": "2",
"Name": "2"
},
{
"Id": "3",
"Name": "3"
},
{
"Id": "4",
"Name": "4"
},
{
"Id": "5",
"Name": "5"
},
{
"Id": "6",
"Name": "6"
},
{
"Id": "7",
"Name": "7"
},
{
"Id": "8",
"Name": "8"
},
{
"Id": "9",
"Name": "9"
},
{
"Id": "10",
"Name": "10"
}
],
"QuestionText": "hows it haning?",
"RangeMaxText": "not good",
"RangeMinText": "Very good",
"Type": 0,
"Value": null
}
],
"Title": "Question title",
"Type": 0
}
所以问题是,无论我使用slider指令更新哪个问题,它始终是传递给updateQuestion的最后一页。
更新 我尝试使用$ watchCollection,但似乎没有任何事情可以触发事件。
this.$scope.$watchCollection(
'questions',
function (newVal, oldVal) {
// prevent event on initial load
if (newVal === oldVal) {
return;
}
for (var i = 0; i < newVal.length; i++) {
if (newVal[i] != oldVal[i]) {
this.$log.info("update : " + newVal.Id);
}
}
}
);
我也试过
function() { return questions; }
作为第一个表达式。仍然没有运气。
也许为每个问题使用单独的控制器是我唯一的选择,但它似乎有点解决方法。
更新 所以我尝试为每个问题使用单独的控制器,在控制器中为每个问题添加一个监视器,奇怪的是即使这是再现相同的情况。它仍然是传递给监视功能的页面上的最后一个问题。
标记
<div data-ng-repeat="question in Questions" data-ng-controller="QuestionInstanceController">
<h4>{{question.QuestionText}}</h4>
<p>{{question.RangeMinText}}</p>
<questionslider ng-model="question"></questionslider>
<p>{{question.RangeMaxText}}</p>
</div>
控制器代码
app.controller('QuestionInstanceController', function($scope) {
console.log($scope.question); // outputs correct question here!
$scope.$watch(
function() { return $scope.question; },
function(newValue, oldValue) {
console.log(newValue); // always outputs last question on page
},
true);
}
}
它必须与我的自定义指令有关,我是否在页面上有多个实例时覆盖以前的实例?
答案 0 :(得分:0)
我希望你的$ scope。$ watch逻辑让你失望。最简单的方法可能是使用$ watchCollection:
来监视整个问题$scope.$watchCollection(questions, function(newValue, oldValue) {
for(index=0; index<newValue.length; index++) {
if (newValue[index] !== oldValue[index]) {
console.log("Question " + index + " updated to: " + newValue[index]);
}
}
});
否则,您可以为ng-repeat循环中的每个项目创建一个单独的控制器,并在那里有一个处理更改的手表。我也不喜欢这个解决方案,因为它有点啰嗦。首先,一个新的控制器来处理问题:
app.controller('QuestionCtrl', function($scope) {
$scope.$watch('question', function(newValue, oldValue) {
if (newVal === oldVal) {
return;
}
console.log(newVal);
}
});
稍微修改您的视图以包含对新QuestionCtrl的引用:
<div data-ng-repeat="question in Questions" ng-controller='QuestionCtrl'>
<h4>{{question.QuestionText}}</h4>
<p>{{question.RangeMinText}}</p>
<questionslider ng-model="question"></questionslider>
<p>{{question.RangeMaxText}}</p>
</div>
This article提供了有关使用带有ng-repeat的控制器的更多信息。
我希望其中一个可以帮助你。
答案 1 :(得分:0)
所以我自己找到了解决方案。问题是我有一个&#34; var isolatedScope;&#34;在每次运行link()时分配的指令中。我认为指令中声明的vars在每个实例上都是隔离的,但实际上它们会在指令的每个实现中被覆盖。
因此解决方案是将onChange的实现直接移动到滑块组件的init,从而避免以后需要访问范围变量
this.app.directive('questionslider', () => {
var onChangeEvent = (event, ui) => {
};
var onSlideEvent = (event, ui) => {
updateUi(ui.value, ui.handle);
};
var updateUi = (value, element) => {
}
var onStartSlide = (event, ui) => {
}
var onEndSlide = (event, ui) => {
}
return {
restrict: 'AE',
require: 'ngModel',
replace: true,
templateUrl: 'templates/questionSlider.html',
scope: {
model: '=ngModel'
},
link: (scope: any, element, attrs, ngModelCtrl: ng.INgModelController) => {
scope.$watch(ngModelCtrl, () => {
// use provided defaultValue if model is empty
var value = scope.model.Value === null ? scope.model.DefaultValue : scope.model.Value;
var hasNoValue = scope.model.Value === null;
if (hasNoValue) {
element.find('.ui-slider-handle').addClass('dg-notset');
}
element.slider({
min: parseInt(scope.model.PossibleValues[0].Name),
max: scope.model.PossibleValues.length,
value: parseInt(value.Name),
slide: onSlideEvent,
start: onStartSlide,
stop: onEndSlide,
animate: true,
change: (event, ui) => {
// find value in values list...
angular.forEach(scope.model.PossibleValues, (val) => {
if (parseInt(val.Name) === ui.value) {
scope.$apply(() => {
scope.model.Value = val;
});
}
});
onChangeEvent(event, ui);
}
});
});
}
};
});