需要使用触摸屏从 AngularJS 指令中更新模型
Need to update model from within AngularJS directive using touch screen
我使用 ng-repeat 在表单上创建一系列输入字段:
<div class="col-sm-6 input" data-ng-repeat="v in values">
<div data-prefix-numeric-input data-prefix-name="{{ v.name }}">
<input type="number" id="{{v.name}}" data-ng-model="form.numberArray[v.name]" data-number-validation />
</div>
</div>
我使用一些 css 来隐藏标准 input:type=number 微调器,使用以下指令创建更适合在触摸屏设备上使用的按钮。
directives.directive('prefixNumericInput', ['$compile', function ($compile) {
function getInputElement(attrs, element) {
var inputTagType = attrs['cpNumericInput'].length ? attrs['cpNumericInput'] : 'input';
return element.find(inputTagType);
}
function getInputValue(element) {
var currentValue = element[0].valueAsNumber;
return isNaN(currentValue) ? 0 : currentValue;
}
return {
restrict: 'A',
templateUrl: 'html-templates/numeric-input.html',
replace: false,
transclude: true,
require: '^form',
scope: {
prefixName: '@'
},
link: function (scope, element, attrs, formController, transcludeFn) {
transcludeFn(function (clone) {
var placeholder = element.find('placeholder');
placeholder.replaceWith(clone);
});
var intervalId, inputElement = getInputElement(attrs, element);
inputElement.addClass('form-control');
// Add our buttons after the input's parent element
$(inputElement[0].parentElement).after($compile('<div class="float-right">' +
'<button type="button" class="btn btn-primary inc-button" data-cp-touchstart="incrementStart()" data-cp-touchend="stop()" data-ng-click="increment()">+</button>' +
'<button type="button" class="btn btn-primary inc-button" data-cp-touchstart="decrementStart()" data-cp-touchend="stop()" data-ng-click="decrement()">-</button>')(scope));
function increment() {
inputElement[0].value = 1 + getInputValue(inputElement);
scope.$parent.$digest();
}
function decrement () {
var currentValue = getInputValue(inputElement);
inputElement[0].value = currentValue > 0 ? currentValue - 1 : 0;
}
scope.increment = increment;
scope.decrement = decrement;
scope.incrementStart = function () {
increment();
intervalId = setInterval(increment, 100);
}
scope.stop = function () {
clearInterval(intervalId);
}
scope.decrementStart = function () {
decrement();
intervalId = setInterval(decrement, 100);
}
}
};
}]);
模板如下:
<div class="input-group">
<span class="input-group-addon">{{prefixName}}</span>
<placeholder></placeholder>
</div>
除一个问题外,此方法运行良好。我们只需要其中一个输入字段具有值。提交表单时,另一个指令是 运行(参见上面的数字验证)。这只是检查是否有任何模型值输入了大于零的值。如果是这样,它会验证有问题的控件。但是,如果用户使用触摸屏并在页面下方递增其中一个字段的值,则直到具有该值的字段的每个输入字段都被视为无效。这是因为在它实际到达具有值的字段之前,模型值似乎并未设置。我将模型数组 (form.numberArray) 转储到验证指令中的控制台,并且可以看到每次验证指令为 运行.
时都会附加每个值
我需要的是在触摸按钮时更新模型。我尝试使用 scope.$parent.$apply 但只是收到 'in progress' 错误。
非常感谢收到的任何建议、指导或建议。
这是我想出的解决方案。请注意,在代码中,我使用 'prefix' 作为任何前缀的占位符,您可以使用它来区分您的逻辑与 AngularJS 的逻辑。我有几个指令来处理触摸开始和结束:
directives.directive('prefixTouchstart', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('touchstart', function (event) {
scope.$apply(function () {
scope.$eval(attrs.prefixTouchstart);
});
});
}
}
});
directives.directive('prefixTouchend', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('touchend', function (event) {
scope.$apply(function () {
scope.$eval(attrs.prefixTouchend);
});
});
}
}
})
然后我使用以下指令创建输入字段。请注意,触摸其中一个按钮并按住手指将逐渐 increment/decrement 输入字段。该模型仅在触发触摸结束事件时更新。这样做的原因是在 setInterval 逻辑中更新模型时,AngularJS 不会反映 UI 中的这些更改,我们不能调用 $apply 因为我们只会得到 'in progress' 错误($apply 在 touchstart/touchend 指令中被调用。参见上面)。
directives.directive('prefixNumericInput', ['$compile', function ($compile) {
return {
restrict: 'E',
templateUrl: 'html-templates/numeric-input.html',
transclude: true,
scope: {
prefixName: '@',
prefixModel: '=',
prefixPlaceholder: '@'
},
link: function (scope, element, attrs, ctrl, transcludeFn) {
var intervalId, inputElement = element.find('input');
var currentValue = scope.prefixModel ? scope.prefixModel : 0;
inputElement.addClass('form-control');
// Add our buttons after the input's parent element
$(element).after($compile('<div class="float-right">' +
'<button type="button" class="btn btn-primary inc-button" data-prefix-touchstart="incrementStart()" data-prefix-touchend="stop()" data-ng-click="increment()">+</button>' +
'<button type="button" class="btn btn-primary inc-button" data-prefix-touchstart="decrementStart()" data-prefix-touchend="stop()" data-ng-click="decrement()">-</button>')(scope));
function decrementCurrentValue() {
if (currentValue > 0) {
--currentValue;
return true;
}
return false;
}
function decrement() {
if (decrementCurrentValue()) {
inputElement[0].value = currentValue;
}
}
scope.increment = function() {
scope.prefixModel = ++currentValue;
};
scope.decrement = function() {
if (decrementCurrentValue()) {
scope.prefixModel = currentValue;
}
};
scope.incrementStart = function () {
inputElement[0].value = ++currentValue;
intervalId = setInterval(function() {
inputElement[0].value = ++currentValue;
}, 100);
}
scope.stop = function () {
scope.prefixModel = currentValue;
clearInterval(intervalId);
}
scope.decrementStart = function () {
decrement();
intervalId = setInterval(decrement, 100);
}
}
};
}]);
以下是html模板:
<div class="input-group column calc-width">
<span class="input-group-addon">{{prefixName}}</span>
<input type="number" min="0" id="{{prefixName}}" data-ng-model="prefixModel" placeholder="{{prefixPlaceholder}}" />
</div>
创建字段的代码如下所示:
<div class="col-sm-6 input" data-ng-repeat="value in values">
<prefix-numeric-input data-prefix-name="{{ value.name }}" data-prefix-model="form.valueArray[value.name]" data-prefix-placeholder="Enter some sort of value here"></prefix-numeric-input>
</div>
我们正在使用 Bootstrap,因此 css 类 散落在整个逻辑中。希望这对某人有所帮助。
我使用 ng-repeat 在表单上创建一系列输入字段:
<div class="col-sm-6 input" data-ng-repeat="v in values">
<div data-prefix-numeric-input data-prefix-name="{{ v.name }}">
<input type="number" id="{{v.name}}" data-ng-model="form.numberArray[v.name]" data-number-validation />
</div>
</div>
我使用一些 css 来隐藏标准 input:type=number 微调器,使用以下指令创建更适合在触摸屏设备上使用的按钮。
directives.directive('prefixNumericInput', ['$compile', function ($compile) {
function getInputElement(attrs, element) {
var inputTagType = attrs['cpNumericInput'].length ? attrs['cpNumericInput'] : 'input';
return element.find(inputTagType);
}
function getInputValue(element) {
var currentValue = element[0].valueAsNumber;
return isNaN(currentValue) ? 0 : currentValue;
}
return {
restrict: 'A',
templateUrl: 'html-templates/numeric-input.html',
replace: false,
transclude: true,
require: '^form',
scope: {
prefixName: '@'
},
link: function (scope, element, attrs, formController, transcludeFn) {
transcludeFn(function (clone) {
var placeholder = element.find('placeholder');
placeholder.replaceWith(clone);
});
var intervalId, inputElement = getInputElement(attrs, element);
inputElement.addClass('form-control');
// Add our buttons after the input's parent element
$(inputElement[0].parentElement).after($compile('<div class="float-right">' +
'<button type="button" class="btn btn-primary inc-button" data-cp-touchstart="incrementStart()" data-cp-touchend="stop()" data-ng-click="increment()">+</button>' +
'<button type="button" class="btn btn-primary inc-button" data-cp-touchstart="decrementStart()" data-cp-touchend="stop()" data-ng-click="decrement()">-</button>')(scope));
function increment() {
inputElement[0].value = 1 + getInputValue(inputElement);
scope.$parent.$digest();
}
function decrement () {
var currentValue = getInputValue(inputElement);
inputElement[0].value = currentValue > 0 ? currentValue - 1 : 0;
}
scope.increment = increment;
scope.decrement = decrement;
scope.incrementStart = function () {
increment();
intervalId = setInterval(increment, 100);
}
scope.stop = function () {
clearInterval(intervalId);
}
scope.decrementStart = function () {
decrement();
intervalId = setInterval(decrement, 100);
}
}
};
}]);
模板如下:
<div class="input-group">
<span class="input-group-addon">{{prefixName}}</span>
<placeholder></placeholder>
</div>
除一个问题外,此方法运行良好。我们只需要其中一个输入字段具有值。提交表单时,另一个指令是 运行(参见上面的数字验证)。这只是检查是否有任何模型值输入了大于零的值。如果是这样,它会验证有问题的控件。但是,如果用户使用触摸屏并在页面下方递增其中一个字段的值,则直到具有该值的字段的每个输入字段都被视为无效。这是因为在它实际到达具有值的字段之前,模型值似乎并未设置。我将模型数组 (form.numberArray) 转储到验证指令中的控制台,并且可以看到每次验证指令为 运行.
时都会附加每个值我需要的是在触摸按钮时更新模型。我尝试使用 scope.$parent.$apply 但只是收到 'in progress' 错误。
非常感谢收到的任何建议、指导或建议。
这是我想出的解决方案。请注意,在代码中,我使用 'prefix' 作为任何前缀的占位符,您可以使用它来区分您的逻辑与 AngularJS 的逻辑。我有几个指令来处理触摸开始和结束:
directives.directive('prefixTouchstart', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('touchstart', function (event) {
scope.$apply(function () {
scope.$eval(attrs.prefixTouchstart);
});
});
}
}
});
directives.directive('prefixTouchend', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('touchend', function (event) {
scope.$apply(function () {
scope.$eval(attrs.prefixTouchend);
});
});
}
}
})
然后我使用以下指令创建输入字段。请注意,触摸其中一个按钮并按住手指将逐渐 increment/decrement 输入字段。该模型仅在触发触摸结束事件时更新。这样做的原因是在 setInterval 逻辑中更新模型时,AngularJS 不会反映 UI 中的这些更改,我们不能调用 $apply 因为我们只会得到 'in progress' 错误($apply 在 touchstart/touchend 指令中被调用。参见上面)。
directives.directive('prefixNumericInput', ['$compile', function ($compile) {
return {
restrict: 'E',
templateUrl: 'html-templates/numeric-input.html',
transclude: true,
scope: {
prefixName: '@',
prefixModel: '=',
prefixPlaceholder: '@'
},
link: function (scope, element, attrs, ctrl, transcludeFn) {
var intervalId, inputElement = element.find('input');
var currentValue = scope.prefixModel ? scope.prefixModel : 0;
inputElement.addClass('form-control');
// Add our buttons after the input's parent element
$(element).after($compile('<div class="float-right">' +
'<button type="button" class="btn btn-primary inc-button" data-prefix-touchstart="incrementStart()" data-prefix-touchend="stop()" data-ng-click="increment()">+</button>' +
'<button type="button" class="btn btn-primary inc-button" data-prefix-touchstart="decrementStart()" data-prefix-touchend="stop()" data-ng-click="decrement()">-</button>')(scope));
function decrementCurrentValue() {
if (currentValue > 0) {
--currentValue;
return true;
}
return false;
}
function decrement() {
if (decrementCurrentValue()) {
inputElement[0].value = currentValue;
}
}
scope.increment = function() {
scope.prefixModel = ++currentValue;
};
scope.decrement = function() {
if (decrementCurrentValue()) {
scope.prefixModel = currentValue;
}
};
scope.incrementStart = function () {
inputElement[0].value = ++currentValue;
intervalId = setInterval(function() {
inputElement[0].value = ++currentValue;
}, 100);
}
scope.stop = function () {
scope.prefixModel = currentValue;
clearInterval(intervalId);
}
scope.decrementStart = function () {
decrement();
intervalId = setInterval(decrement, 100);
}
}
};
}]);
以下是html模板:
<div class="input-group column calc-width">
<span class="input-group-addon">{{prefixName}}</span>
<input type="number" min="0" id="{{prefixName}}" data-ng-model="prefixModel" placeholder="{{prefixPlaceholder}}" />
</div>
创建字段的代码如下所示:
<div class="col-sm-6 input" data-ng-repeat="value in values">
<prefix-numeric-input data-prefix-name="{{ value.name }}" data-prefix-model="form.valueArray[value.name]" data-prefix-placeholder="Enter some sort of value here"></prefix-numeric-input>
</div>
我们正在使用 Bootstrap,因此 css 类 散落在整个逻辑中。希望这对某人有所帮助。