1. 상속 , Prototypal 상속
오늘은 AngularJS의 상속에 대해 알아보자. 미리 말하지만 AngularJS의 상속의 의미는 자바보다 복잡하며 이를 제대로 이해하지 못 할경우 코딩시 차후 문제가 반드시 발생한다.
AngularJS에서 상속의 의미는 자바 보다는 자바스크립트를 따라간다. 따라서 자바스크립트 고수분들에게는 쉽게 다가올 것이다.
참고 (https://github.com/angular/angular.js/wiki/Understanding-Scopes)
위 링크를 한번 정독하는 것을 추천하다.
정독을 추천하지만 시간이 없는 사람들이 있을수 있기에 윗글을 내 나름대로 정리해보겠다.
1. 부모 scope에 object , primitive type(String, Boolean , Number)를 정의 할 경우 자식 scope로 상속 된다. 그러나 접근하여 값의 변경 또는 초기화에 있어 primitive type(String, Boolean , Number)는 예상하는 값의 변경이 일어나지 않고 자식 scope에서 새로운 변수를 선언하게 된다.
, 즉 자식 scope 에서 부모 scope로 접근해야 하는 대상에 있어서 반드시 Object로 정의 해주는것이 좋다. 이후 object_name.var_name 와 같은 형태로 값을 접근한다.
2. 위 1번 방법이 여렵다면 $parent 를 통해 부모 scope에 접근할 수 있으나 권장하는 방법은 아니다.
3. 부모 scope의 function 역시 자식 scope에 상속 된다.
위 와같은 상속 조건을 따르는 이유는 AngularJS가 자바스크립트 기반이기 때문이다. 그림을 통해 한번 공부해보자
먼저 그림은 아래와 같은 상황이다.
childScope.aString === 'parent string'
childScope.anArray[1] === 20
childScope.anObject.property1 === 'parent prop1'
childScope.aFunction() === 'parent output'
// 값 변경
childScope.aString = 'child string'
위 그림은 1의 설명을 보여주는 그림이라고 할 수 있다.
또,
// 정확한 접근 방식
childScope.anArray[1] = 22
childScope.anObject.property1 = 'child prop1'
// 틀린 접근 방식
childScope.anArray = [100, 555]
childScope.anObject = { name: 'Mark', country: 'USA' }
// 위에서 Array를 자식스코프에 할당함..
childScope.anArray[1] === 22 // false
delete childScope.anArray
childScope.anArray[1] === 22 // true
위 그림과 같이
값을 접근하여 변경하게 되면 부모 scope가 아닌 자식 scope의 값의 변경이 일어나게 된다. 하지만 object 형태로 접근할 경우 부모 scope의 값의 조회 및 수정이 제대로 된다.
이는
not consulted. => 참조 되지 않는다.
2. AngularJS의 상속 이슈
AngularJS에서 상속 이슈가 발생하는 디렉티브들 먼저 소개하겠다. 이것보다 많겠지만 전부 소개 할 순 없고 내가 겪은 이슈 위주로 소개하겠습니다.
1. ng-show VS ng-if
2. ng-repeat
3. ng-controller
4. 사용자 정의 directive
1) ng-if
먼저 ng-if에 대해서 살펴보자.
ng-show 와 ng-if에 있어 성능상의 이슈 등으로 ng-if를 사용할것을 추천하는 글을 아마? 썻던걸로 기억한다. 이 뿐만 아니라 form의 valid 관련 문제에서도 ng-if를 사용하면 숨겨진 요소에 대해서는 valid를 체크를 하지 않게 할 수 있다.
다만 여기에 주의할점이 하나 있다. ng-if를 선언하게 되면 선언과 동시에 새로운 자식 scope를 만들어낸다. 코드를 한번 살펴보자.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<p>Select a car:</p>
<select ng-model="selectedCar" ng-options="x.model as x.color for x in cars ">
</select>
<div ng-show="true" ng-init="selectedCar='Ford Mustang'" ></div>
<div ng-if="true" ng-init="selectedCar='Fiat 500'" ></div>
<h1>You selected:</h1>
<p>Its color is: {{selectedCar}}</p>
<hr>
<p>Select a car2:</p>
<select ng-model="test.selectedCar" ng-options="x2.model as x2.color for x2 in cars2 ">
</select>
<div ng-show="true" ng-init="selectedCar='Ford Mustang'" ></div>
<div ng-if="true" ng-init="test.selectedCar='Fiat 500'" ></div>
<h1>You selected:</h1>
<p>Its color is: {{test.selectedCar}}</p>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.cars = [
{model : "Ford Mustang", color : "red"},
{model : "Fiat 500", color : "white"},
{model : "Volvo XC90", color : "black"}
];
$scope.cars2 = [
{model : "Ford Mustang", color : "red"},
{model : "Fiat 500", color : "white"},
{model : "Volvo XC90", color : "black"}
];
$scope.test={'selectedCar':''};
});
</script>
</body>
</html>
위 코드를 돌려보면 객체를 통해 접근하지 않는 방식과 객체를 통한 접근 방식 이 2가지 방식의 결과가 다르게 나오는것을 확인할 수있다.
2) ng-repeat
ng-repeat 역시 새로운 자식 scope가 생겨난다. 이것도 코드를 통해 살펴 보자!
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="app" >
<div ng-controller="Con">
<ul >
<li multi-file ng-repeat="x in file_array">
<input type='file' ng-model='x'/>
<a ng-click="plus($index)" ng-if="$last">plus</a>
<a ng-click="minus($index)" ng-if="!$first">minus</a>
{{x}}
</li>
</ul>
</div>
</body>
<script>
var app = angular.module("app",[]);
app.controller("Con",["$scope",function($scope){
$scope.file_array = ['input0'];
}]);
angular.module("app").directive("multiFile", function() {
return {
link: function (scope,elem,attrs) {
elem.bind("change", function(e) {
scope.$apply(function(){
})
})
scope.plus = function(index){
name="input"+(index+1);
scope.file_array.push(name);
console.log(scope.file_array);
}
scope.minus = function(index){
var name;
scope.file_array.splice(index,1);
for(;index<scope.file_array.length;index++){
name = "input"+index;
scope.file_array[index] = name
}
console.log(scope.file_array);
}
}
}
});
</script>
</html>
위 코드 역시 각자의 scope가 생겨 각각 파일을 따로 관리할수 있다!
3) ng-controller
컨트롤러간에도 상속 관계는 존재한다. dom 객체의 상속관계를 이용하여 상속관계를 정해줄 수 있고 controller에서 상속관계를 정해줄수도 있다.
먼저 dom 구조를 이용한 상속관계를 살펴보자
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MainCtrl">
{{greet}}!! {{whoami}}
<div ng-controller="SubCtrl">
{{greet}}!! {{whoami}}
</div>
<div ng-controller="SubCtrl2">
{{greet}}!! {{whoami}}
</div>
</div>
</body>
<script>
var app = angular.module("myApp", []);
app.controller("MainCtrl",["$scope",function($scope){
$scope.whoami= "Main Controller";
$scope.greet = "Hello";
}])
app.controller("SubCtrl",["$scope",function($scope){
$scope.whoami= "Sub Controller";
}])
app.controller("SubCtrl2",["$scope",function($scope){
$scope.whoami= "Sub Controller2";
}])
</script>
</html>
위 코드는 우리가 알고있듯이 dom 구조의 상속관계를 보여주며 이는 Controller 역시 그대로 따라감을 보여준다. 보시다시피 부모 -> 자식에 대한 값 greet가 전부 상속됨을 알수 있다.
다음으로는 dom구조에 구속받지 않고 상속관계를 맺는것을 보여준다.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MainCtrl">
{{greet}}!! {{whoami}}
</div>
<div ng-controller="SubCtrl">
{{greet}}!! {{whoami}}
</div>
<div ng-controller="SubCtrl2">
{{greet}}!! {{whoami}}
</div>
</body>
<script>
var app = angular.module("myApp", []);
function MainCtrl($scope){
$scope.whoami= "Main Controller";
$scope.greet = "Hello";
}
app.controller("MainCtrl",["$scope",MainCtrl])
app.controller("SubCtrl",["$scope",function($scope){
angular.extend(this,new MainCtrl($scope));
$scope.whoami= "Sub Controller";
}])
app.controller("SubCtrl2",["$scope",function($scope){
angular.extend(this,new MainCtrl($scope));
$scope.whoami= "Sub Controller2";
}])
</script>
</html>
위 코드는 이전 코드와 다르게 dom구조에 대한 상속 구조가 아님에도 불구하고 상속받는것을 보여준다.
컨트롤러의 상속구조는 이쯤에서 마무리하고 이번에는 컨트롤러간 '통신'을 애기해보자, 부모 - > 자식으로의 흐름은 따로 어떤 작업을 해주지 않아도 잘 진행된다. 하지만
자식 -> 부모는 특수한 작업을 해줘야만 한다. 그 '작업'에 대해서 한번 설명해보려 한다.
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MainCtrl">
--MainCtrl--<br>
sub :{{sub_input}}<br>
sub2 :{{sub_input2}}<br>
<input type="text" ng-model="main_input">
<div ng-controller="SubCtrl">
--SubCtrl--<br>
main : {{main_input}} <br>
<input type="text" ng-model="sub_input">
<button ng-click="subClick();">sub</button>
</div>
<div ng-controller="SubCtrl2">
--SubCtrl2--<br>
main : {{main_input}} <br>
<input type="text" ng-model="sub_input2">
<button ng-click="subClick2();">sub2</button>
</div>
</div>
</body>
<script>
var app = angular.module("myApp", []);
app.controller("MainCtrl",["$scope",function($scope){
$scope.$on('hi',function(event,args){
$scope.sub_input=args.message;
})
$scope.$on('hi2',function(event,args){
$scope.sub_input2=args.message;
})
}])
app.controller("SubCtrl",["$scope",function($scope){
$scope.subClick = function(){
$scope.$emit('hi',{message:$scope.sub_input})
}
}])
app.controller("SubCtrl2",["$scope",function($scope){
$scope.subClick2 = function(){
$scope.$emit('hi2',{message:$scope.sub_input2})
}
}])
</script>
</html>
위 굵은 글씨가 바로 '작업'에 해당 된다.
$on 은 통신에 대한 수신의 역할을 하며
$emit 은 자식 - > 부모로 송신의 역할을 하며
$broadcast는 부모 -> 자식으로 송신의 역할을 한다.
broadcast의 간단예를 소개하며 controller 상속은 마무리하겠습니다.
<!DOCTYPE html>
<html ng-app="myApp">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body>
<div ng-controller="ControllerZero">
<input ng-model="message">
<button ng-click="handleClick(message);">LOG</button>
<div ng-controller="ControllerOne">
<input ng-model="message2" >
<button ng-click="handleClick();">LOG 2</button>
</div>
</div>
</body>
<script>
var app = angular.module('myApp', []);
var myModule = angular.module('myModule', []);
function ControllerZero($scope) {
$scope.handleClick = function(msg) {
$scope.$broadcast('handleChild', {message : msg});
};
$scope.$on('updateParent', function(event, args) {
$scope.message = 'ZERO: ' + args.message;
});
}
function ControllerOne($scope) {
$scope.$on('handleChild', function(event, args) {
$scope.message2 = 'ONE: ' + args.message;
});
$scope.handleClick = function() {
$scope.$emit('updateParent', {message: $scope.message2});
};
}
app.controller("ControllerZero",["$scope",ControllerZero])
app.controller("ControllerOne",["$scope",ControllerOne])
</script>
</html>
마지막 사용자 정의 디렉티브는 앞선 공부에서 이미 공부했고 차후 '마이 디렉티브'에서 계속 볼것이므로 생략하겠습니다.
Anuglar JS 공부 <6> - $apply(), $watch() 변화 감지 그리고 $eval(),$parse() (0) | 2019.03.04 |
---|---|
Anuglar JS 공부<*> - my directive (0) | 2019.02.21 |
Anuglar JS 공부 <4> - module.directive (0) | 2019.02.19 |
Anuglar JS 공부 <3> - 양,단 방향 바인딩 (0) | 2019.02.18 |
Anuglar JS 공부 <2> Module,Controller,Directive, Service (0) | 2019.01.21 |