Lightning Component の継承関係をまとめてみました

Lightning Component の継承の説明がいまいち分かりづらかったので、ちょっとしたサンプルを書いてみました。

---

## 基本的なコンポーネントの動きの確認
###基本的なコンポーネントの動き
```html:BasicComponent.cmp
<aura:component implements="forceCommunity:availableForAllPageTypes" access="public" >
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
</aura:component>
```

```js:BasicComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
})
```

```js:BasicComponentHelper.js
({
    helperMethod : function() {
        console.log('helperMethod!');
    }
})
```

```bash:検証結果
controllerMethod!
helperMethod!
```

###ポイント
- コミュニティビルダーで動作確認をするため、`forceCommunity:availableForAllPageTypes`をインプリメントしています
- `init`イベントをトリガに、コントローラメソッド、ヘルパーメソッドを呼び出します。


---
## 継承関係を構築する
###スーパーコンポーネント
```html:SuperComponent.cmp
<aura:component access="global" extensible="true"> 
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
    
    <!-- User Interface -->
    {!v.body}
</aura:component>
```

```js:SuperComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('SuperComponent.controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
})
```

```js:SuperComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SuperComponent.helperMethod');
    }
})
```

###サブコンポーネント
```html:SubComponent.cmp
<aura:component access="global" extends="c:SuperComponent" implements="forceCommunity:availableForAllPageTypes"> 
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
    
    <!-- User Interface -->
    {!v.body}  
</aura:component>
```

```js:SubComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('SubComponent.controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
})
```

```js:SubComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SubComponent.helperMethod');
    }
})
```

###検証結果
```bash:検証結果
SuperComponent.controllerMethod!
SuperComponent.helperMethod
SubComponent.controllerMethod!
SubComponent.helperMethod
```

###ポイント
- スーパーコンポーネントには`extensible="true"`を記述し、サブコンポーネントには`extend="c:SuperComponent"`を記述する事で継承関係を宣言しています。
- スーパーコンポーネントには`{!v.body}`を記述し、サブコンポーネントが描画される領域を指定する必要があります。
- 継承関係があっても、それぞれのイベントハンドラがスーパーコンポーネントから順に実行されていきます。

---

##サブコンポーネントからスーパーコンポーネントのヘルパーを継承する
###スーパーコンポーネント
```html:SuperComponent.cmp
<aura:component access="global" extensible="true"> 
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
    
    <!-- User Interface -->
    {!v.body}
</aura:component>
```

```js:SuperComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('SuperComponent.controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
})
```

```js:SuperComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SuperComponent.helperMethod');
    }
})
```

###サブコンポーネント
```html:SubComponent.cmp
<aura:component access="global" extends="c:SuperComponent" implements="forceCommunity:availableForAllPageTypes"> 
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
    
    <!-- User Interface -->
    {!v.body}  
</aura:component>
```

```js:SubComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('SubComponent.controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
})
```
###検証結果
```bash:検証結果
SuperComponent.controllerMethod!
SuperComponent.helperMethod
SubComponent.controllerMethod!
SuperComponent.helperMethod
```

###ポイント
- サブコンポーネントはスーパーコンポーネントのヘルパーメソッドにアクセス出来ます。自身がそのヘルパーメソッドを持たない場合、スーパーコンポーネントを辿って継承関係において上に最も近いヘルパーメソッドを呼び出します。

---
##サブコンポーネントからスーパーコンポーネントのコントローラーを継承する
###スーパーコンポーネント
```html:SuperComponent.cmp
<aura:component access="global" extensible="true"> 
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
    
    <!-- User Interface -->
    {!v.body}
</aura:component>
```

```js:SuperComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('SuperComponent.controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
})
```

```js:SuperComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SuperComponent.helperMethod');
    }
})
```

###サブコンポーネント
```html:SubComponent.cmp
<aura:component access="global" extends="c:SuperComponent" implements="forceCommunity:availableForAllPageTypes"> 
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
    
    <!-- User Interface -->
    {!v.body}  
</aura:component>
```

```js:SubComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SubComponent.helperMethod');
    }
})
```

###検証結果
```bash:検証結果
SuperComponent.controllerMethod!
SuperComponent.helperMethod
SuperComponent.controllerMethod!
SubComponent.helperMethod
```

###ポイント
- サブコンポーネントはスーパーコンポーネントのコントローラーメソッドにアクセス出来ます。自身がそのコントローラーメソッドを持たない場合、スーパーコンポーネントを辿って継承関係において上に最も近いコントローラーメソッドを呼び出します。

---
##スーパーコンポーネントからサブコンポーネントでオーバーライドされたコントローラーを呼び出す
###スーパーコンポーネント
```html:SuperComponent.cmp
<aura:component access="global" extensible="true"> 
    <!-- Component Methods -->
    <aura:method access="public" name="controllerMethod" action="{!c.controllerMethod}" 
                 description="オーバーライドさせたいメソッドをコンポーネントメソッド化しておく" />
    
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethodIfOverridden}" />
    
    <!-- User Interface -->
    {!v.body}
</aura:component>
```

```js:SuperComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('SuperComponent.controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
    controllerMethodIfOverridden : function(cmp, event, helper) {
        cmp.getConcreteComponent().controllerMethod();
    },
})
```

```js:SuperComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SuperComponent.helperMethod');
    }
})
```


###サブコンポーネント
```html:SubComponent.cmp
<aura:component access="global" extends="c:SuperComponent" implements="forceCommunity:availableForAllPageTypes"> 
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}" action="{!c.controllerMethod}" />
    
    <!-- User Interface -->
    {!v.body}  
</aura:component>
```

```js:SubComponentController.js
({
    controllerMethod : function(cmp, event, helper) {
        console.log('SubComponent.controllerMethod!');
        helper.helperMethod(cmp, helper);
    },
})
```

```js:SubComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SubComponent.helperMethod');
    }
})
```
###検証結果
```bash:検証結果
SubComponent.controllerMethod!
SubComponent.helperMethod
SubComponent.controllerMethod!
SubComponent.helperMethod
```

###ポイント
- オーバーライドさせたいコントローラーメソッドをコンポーネントメソッド化しておく。コンポーネントメソッドは継承される。
- `cmp.getConcreteComponent()`は、インスタンス化されたコンポーネントを返すので、スーパーコンポーネントからでもサブコンポーネントのコンポーネントメソッドにアクセスが出来るようになる。


---
## (おまけ)サブコンポーネントのヘルパーを直接呼び出す

以前までは、`cmp.getConcreteComponent().getDef().getHelper().someHelperMethod()`で直接サブのヘルパーメソッドを呼びさせていたようなのですが、いつの間にか使えなくなってしまったみたいですね。よくあるよくある。

---
###スーパーコンポーネント
```html:SuperComponent.cmp
<aura:component access="global" extensible="true">     
    <!-- Private Attributes -->
    <aura:attribute access="private" name="concreteHelper" type="Object" />
  
    <!-- Component Methods -->
    <aura:method access="private" name="initConcreteHelper" action="{!c.initConcreteHelper}" />
    
    <!-- Application Event Handler -->
    <aura:handler name="init" value="{!this}"                          action="{!c.onValueInit}"             />
    <aura:handler                             event="aura:doneWaiting" action="{!c.callHelperMethodFromSub}" /> 
    
    <!-- User Interface -->
    {!v.body}
</aura:component>
```

```js:SuperComponentController.js
({
    onValueInit : function(cmp, event, helper) {
        cmp.getConcreteComponent().initConcreteHelper();
    },
    initConcreteHelper : function(cmp, event, helper) {
        cmp.set('v.concreteHelper', helper);
    },
    callHelperMethodFromSub : function(cmp, event, helper) {
        cmp.get('v.concreteHelper').helperMethod(cmp, helper);
    },
})
```

###サブコンポーネント
```html:SubComponent.cmp
<aura:component access="global" extends="c:SuperComponent" implements="forceCommunity:availableForAllPageTypes"> 
    <!-- User Interface -->
    {!v.body}  
</aura:component>
```

```js:SubComponentHelper.js
({
    helperMethod : function(cmp, helper) {
        console.log('SubComponent.helperMethod');
    }
})
```
###ポイント
- `cmp.getConcreteComponent()`とコンポーネントメソッドの応用のようなもので、サブコンポーネントのヘルパーへの参照を変数に格納しておくことで、自由に呼び出すことが出来ます。
- `init`イベントをなどでヘルパーへの参照を保存しないいけないので、`init`イベントに紐付けるコントローラーメソッドはオーバーライドさせてはいけません。