2019. 3. 12. 19:35ㆍProgramming/AngularJS
angularJS 1.x
에서 input
이나 select
를 사용하여 UI를 구현하다보면, 사용자가 특정 값
을 선택했을 때 이전 값
으로 되돌려야 하는 경우가 있다. 보통 이런 경우에는 ng-change
나 ng-click
을 이용해서 구현하게 된다.
물론 사용자에게 특정 값을 선택할 수 없는 이유를 알려주기 위한 방법은 많다. 툴팁을 이용할수도 있고, 페이지의 눈에 띄는 곳에 문구를 출력하는 방법도 있다. 다만 툴팁이나 문구의 경우 사용자가 동작을 멈추고, 툴팁이나 문구를 찾아야 한다. 사용자가 잘못된 값을 선택했을 때 다이얼로그를 띄우게되면, 사용자가 의도한 동작의 흐름에 따라 자연스럽게 잘못된 값임을 인지할 수 있다.
우선 이전 값
을 가져오도록 하자. ng-change
나 ng-click
에서 Controller
내의 함수를 호출할 때, 다음처럼 파라메터를 전달하면 이전 값
을 가져올 수 있다. - ng-change get new value and original value, StackOverflow
<select ng-model="selectedValue" ng-change="change(selectedValue, '{{selectedValue}}')">
// change함수에는 DOM에서 가지고 있는 현재 값, 즉 이전 값이 전달된다.
Controller
내의 change
에 break point
를 걸고 값을 확인해보면, 첫번째 파라메터로 현재 값
, 두번째 파라메터로 이전 값
이 문자열로 전달되는 걸 확인할 수 있다. 이제 현재 값
을 이전 값
으로 변경하면, 목표를 달성할 수 없는 걸 확인할 수 있다. 그렇다. 렌더링이 제대로 되지 않는다. 나의 경우는 라디오박스에 동일한 처리를 하려고 했으나, 동일한 name
값을 가진 모든 라디오 박스가 선택 해제되는 걸 목격했다.
바인딩 된 값들이 업데이트되면,
$digest
함수와$apply
함수가 호출된다. 이 때 바인딩 된 값들을 업데이트하면angularJS
가 변경사항을 DOM에 반영하지 못한다.$apply
함수를 호출하면$apply already in progress
출력되는 것을 확인할 수 있다.$apply
함수의 실행이 끝난 뒤 값을 업데이트하면 정상적으로 DOM에 반영되기 때문에,$timeout
에서 일정시간을 준 뒤 값을 업데이트하면 정상적으로 DOM에 값이 표시되는 것을 확인할 수 있다.
참고:
$timeout(function(){
$scope.currentValue = previousValue;
}, 100);
$timeout
을 이용해서 $appy
가 끝났음직한 시간에 값을 업데이트하면 해결되긴 하지만, 시간을 얼마나 줘야 할까? 사실 100ms를 지정해도 정상동작하고, 300ms를 지정해도 정상동작한다. 기능상에 문제가 없기는 하지만, 어쩐지 찜찜하다.
$scope.$$phase
를 조회하면 현재 $scope
의 현재 상태를 조회할 수 있다. 찜찜함을 해소하기 위해서 다음과 같은 코드를 작성했다.
//Call callback function after $apply or $digest.
function _callAfterRendering(param_callback) {
if (typeof param_callback != "function") {return false;}
if($scope.$$phase == '$apply' || $scope.$$phase == '$digest') {
$timeout(function(){
_callAfterRendering(param_callback)
}, 100);
} else {
param_callback();
}
}
_callAfterRendering(function_callback)
을 호출하게되면, $apply
/$digest
가 종료된 뒤 함수를 실행하게 된다.
위에서 작성한 _callAfterRendering을 많은 곳에서 사용할 경우에는, 다음처럼 모듈로 작성해놓도록 하자.
'use strict';
angular.module('module.callAfterRendering', [
])
.factory('callAfterRendering', function($timeout) {
var render = {};
render.apply = function(param_scope, param_callback) {
if (typeof param_callback != "function") {return false;}
//Call callback function after $apply or $digest.
if(param_scope.$$phase == '$apply' || param_scope.$$phase == '$digest') {
$timeout(function(){
render.apply(param_scope, param_callback);
}, 100);
} else {
param_callback();
}
}
return render;
})
컨트롤러 혹은 다른 모듈에서 사용할 때, 의존성을 설정해준 후 callAfterRendering.apply($scope
, callback_function
)을 호출하면 된다. 이 때 $scope가 렌더링중이 아닐때는 $$phase가 null값이기 때문에, scope를 전달하지 않을 시에는 단순히 0.1초 뒤에 callback함수를 실행하게 된다.
깜빡하고 callAfterRendering.apply를 호출할 시 callback_function만 전달하는 경우, apply의 첫번째 줄에서 param_callback의 type이 undefined가 되기 때문에 문제는 발생하지 않는다. 다만 에러가 발생하기 때문에 디버깅을 하기에는 조금 짜증날지도 모르겠다.
'Programming > AngularJS' 카테고리의 다른 글
[AngularJS 1.x] ng-options의 내용을 변경할 때 값이 초기화되는 경우 (0) | 2019.05.07 |
---|---|
[BootStrap] 탭 내용 전환하기 전에 다이얼로그를 띄우기 (0) | 2019.03.19 |
ng-include로 불러온 페이지의 Controller가 두 번 호출되는 문제 (0) | 2019.03.11 |
Unsupported Selector Lookup. Looking up elements via selectors is not supported by jqLite (0) | 2016.10.19 |
IE10에서 Select를 그리지 못하는 버그 (0) | 2016.02.20 |