Trailhead 用自転車セレクタアプリケーションのメモ（１）：画面描画

ようやくTrailHeadのサンプル「自転車」を少々理解できた段階で、
自分なりにソースをアレンジしてみました。
勉強か、備忘として、画面表示までのプロセスを記録したします。

<h2>1、作成準備：</h2>
1-1. 以下のアプリケーションサンプルをダウンロード（条件が許容範囲内であれば）
<a>https://trailhead.salesforce.com/ja/content/learn/modules/lightning-web-components-basics/handle-events-in-lightning-web-components</a>
キーワード： Trailhead 用自転車セレクタアプリケーション

1-2. ツール：Lightning Web Components Playground 
https://developer.salesforce.com/docs/component-library/tools/playground

<h2>2、説明：</h2>
playgroundを使用して、Bikesサンプルを再現してみました。
こちらの構造は以下のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/578884/d2a7f75e-f429-357d-bd26-706aa6125b53.png)

図は大体こんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/578884/af4eba34-bc8b-12be-1926-673a649f68aa.png)

１、まずは静的画面を読み込む。
	Selector.html

	<template>
	    <div class="wrapper">
	    <header class="header">Select a Bike</header>
	    <section class="content">
	        <div class="columns">
	        <main class="main" >
	            <c-list onproductselecteds={handleProductSelected}></c-list>
	        </main>
	        <aside class="sidebar-second">
	            <c-detail product-id={selectedProductId}></c-detail>
	        </aside>
	        </div>
	    </section>
	    </div>
	</template>

ブラウザーが上から順次に描画、wrapper,header,section基本的に定番のパターンだから、省略。
重要な部分は「c-list」と「c-detail」、「c-list」は命名空間として、同じパスの中に「list」ディレクトリを探って、list.htmlをここに反映する。
同じく、「detail」ディレクトリを探って、detail.htmlを反映する。なぜdetailが右側に行くかはここで追究しません。Cssの仕様をＦｌｅｘに設定されてるからと思うんだけど。

	list.html
	<template>
	    <div class="container">
	        there should be tiles list below
	        <template for:each={bikes} for:item="bike">
	            <c-tile 
	                key={bike.fields.Id.value} 
	                product={bike} 
	                ontileclick={handleTileClick}>
	            </c-tile>
	        </template>
	    </div>
	</template>

listの中にtileが1個1個forループで設定されてるから、「bikes」の分だけ設定されてしまう。
ここで｛bikes｝はlistのプロパティとして、ｊｓの中にデータを振り込まれるので、jsを見てみましょう。

	import { LightningElement, track } from 'lwc';
	// import { bikes } from 'c/data';
	export const input_bikes = [
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1arAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-12T02:57:48.000Z"},"Level__c":{"displayValue":"Racer","value":"Racer"},"MSRP__c":{"displayValue":"$7,800","value":7800},"Material__c":{"displayValue":"Carbon","value":"Carbon"},"Name":{"displayValue":null,"value":"DYNAMO X1"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox1.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-12T02:57:48.000Z"}},"id":"a0256000001F1arAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-12T02:57:48.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-12T02:57:48.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1atAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-10T17:26:47.000Z"},"Level__c":{"displayValue":"Racer","value":"Racer"},"MSRP__c":{"displayValue":"$6,802","value":6802},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"DYNAMO X2"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox2.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-10T17:26:47.000Z"}},"id":"a0256000001F1atAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-10T17:26:47.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-10T17:26:47.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1auAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T04:37:56.000Z"},"Level__c":{"displayValue":"Enthusiast","value":"Enthusiast"},"MSRP__c":{"displayValue":"$5,601","value":5601},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"DYNAMO X3"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox3.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T04:37:56.000Z"}},"id":"a0256000001F1auAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T04:37:56.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T04:37:56.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1avAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Enthusiast","value":"Enthusiast"},"MSRP__c":{"displayValue":"$5,500","value":5500},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"DYNAMO X4"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/dynamox4.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1avAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Mountain","value":"Mountain"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1azAAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Enthusiast","value":"Enthusiast"},"MSRP__c":{"displayValue":"$4,600","value":4600},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"FUSE X1"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/fusex1.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1azAAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b2AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T04:41:56.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$3,200","value":3200},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X1"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax1.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T04:41:56.000Z"}},"id":"a0256000001F1b2AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T04:41:56.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T04:41:56.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b3AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$3,200","value":3200},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X2"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax2.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1b3AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b6AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$2,700","value":2700},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X3"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax3.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1b6AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"},
	    {"apiName":"Product__c","childRelationships":{},"fields":{"Category__c":{"displayValue":"Commuter","value":"Commuter"},"CreatedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Description__c":{"displayValue":null,"value":"A durable e-bike with great looks."},"Id":{"displayValue":null,"value":"a0256000001F1b7AAC"},"LastModifiedDate":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"},"Level__c":{"displayValue":"Beginner","value":"Beginner"},"MSRP__c":{"displayValue":"$2,700","value":2700},"Material__c":{"displayValue":"Aluminum","value":"Aluminum"},"Name":{"displayValue":null,"value":"ELECTRA X4"},"Picture_URL__c":{"displayValue":null,"value":"https://s3-us-west-1.amazonaws.com/sfdc-demo/ebikes/electrax4.jpg"},"SystemModstamp":{"displayValue":null,"value":"2018-10-09T03:29:52.000Z"}},"id":"a0256000001F1b7AAC","lastModifiedById":null,"lastModifiedDate":"2018-10-09T03:29:52.000Z","recordTypeInfo":null,"systemModstamp":"2018-10-09T03:29:52.000Z"}
	];
	
	export default class List extends LightningElement {
	// ------------------------------------------------------------------------    
	    bikes = input_bikes;
	// ------------------------------------------------------------------------
	    handleTileClick(evt) {
	        console.log("listClick1")
	        // This component wants to emit a productselected event to its parent
	        const event = new CustomEvent('productselecteds', {
	            detail: evt.detail
	        });
	        console.log("listClick2")
	        // Fire the event from c-list
	        this.dispatchEvent(event);
	        console.log("listClick3")
	    }
	// ------------------------------------------------------------------------
	
	}
この画面表示の段階ではclassの配下のコードが上から順次実行するので、「bikes = input_bikes」だけが実行される。
つまり、bikesがinput_bikesのデータで充填してしまう。
それで、list.htmlの中のforループが実行し、この「input_bikes」分のbikeがtileに与えて、tileを表示。
「input_bikes」少々長いけど、適当に改行して見れば、そんなに難しいデータではありません。
（ Trailhead 用自転車セレクタアプリケーションの中のdataというディレクトリのdata.jsの中の内容をそのまま貼り付けただけ、そこを見れは分かる）
「input_bikes」中のjson書式：”[”と”]”の中に配列として、１行ずつ分かれてるため、ループすると、1行ずつ「bike」としてtileに入れ込む、全部で９個のtileが生成される。

もう一回list.htmlを見てみましょう。

	list.html
	<template>
	    <div class="container">
	        there should be tiles list below
	        <template for:each={bikes} for:item="bike">
	            <c-tile 
	                key={bike.fields.Id.value} 
	                product={bike} 
	                ontileclick={handleTileClick}>
	            </c-tile>
	        </template>
	    </div>
	</template>
[key={bike.fields.Id.value} ]、bikeはこのkey（一意）でデータを割当するらしい。
つまり、bikesはidでbikeを「product」に与える。Tileを表示する。

	tile.html
	<template>
	    <div class="container">
	        <a onclick={tileClick}>
	            <div class="title">{product.fields.Name.value}</div>
	            <img class="product-img" src={product.fields.Picture_URL__c.value}></img>
	        </a>
	    </div>
	</template>
tileは非常にシンプルなソースですが、”｛”、”｝”の中の部分は「input_bikes」の内容に順を追って探せば出で来る。

	tile.js
	import { LightningElement, api } from 'lwc';
	export default class Tile extends LightningElement {
	    
	    @api product;
	    tileClick() {
	        console.log("product:" + this.product)
	        console.log("tileClick1")
	        const event = new CustomEvent('tileclick', {
	            // detail contains only primitives
	            detail: this.product.fields.Id.value
	        });
	        console.log("tileClick2")
	        // Fire the event from c-tile
	        this.dispatchEvent(event);
	        console.log("tileClick3")
	    }
	}
tile.jsには関数しか書かれていないため、現段階では実行されません。

	detail.html
	<template>
	    <template if:true={product}>
	        <div class="container">
	            <div>{product.fields.Name.value}</div>
	            <div class="price">{product.fields.MSRP__c.displayValue}</div>
	            <div class="description">{product.fields.Description__c.value}</div>
	            <img class="product-img" src={product.fields.Picture_URL__c.value}></img>
	            <p>
	                <lightning-badge label={product.fields.Material__c.value}></lightning-badge>
	                <lightning-badge label={product.fields.Level__c.value}></lightning-badge>
	            </p>
	            <p>
	                <lightning-badge label={product.fields.Category__c.value}></lightning-badge>
	            </p>
	        </div>
	    </template>
	    <template if:false={product}>
	        <div>Select a bike</div>
	    </template>
	</template>
右側のdetailについては、やや複雑ですが、htmlから見てみましょう。

	detail.html
	<template>
	    <template if:true={product}>
	        <div class="container">
	            <div>{product.fields.Name.value}</div>
	            <div class="price">{product.fields.MSRP__c.displayValue}</div>
	            <div class="description">{product.fields.Description__c.value}</div>
	            <img class="product-img" src={product.fields.Picture_URL__c.value}></img>
	            <p>
	                <lightning-badge label={product.fields.Material__c.value}></lightning-badge>
	                <lightning-badge label={product.fields.Level__c.value}></lightning-badge>
	            </p>
	            <p>
	                <lightning-badge label={product.fields.Category__c.value}></lightning-badge>
	            </p>
	        </div>
	    </template>
	    <template if:false={product}>
	        <div>Select a bike</div>
	    </template>
	</template>
プロパティとして「product」の値次第で、配下を決める。
[product]は@trackマークアップされるため、値が変換されるたびにcomponentが再描画する、だけど、detail.jsは描画の段階ではproductは充填してないため、
下のfalseのDomが反映される。よって、「select a bike」しか書かれてないわけ（下の画面参照）。
要注意して欲しいのは、selector.htmlの中に「<c-detail product-id={selectedProductId}></c-detail>」という文は「product-id」はdetailの公開パラメータです（＠apiのマークアップ）。
setter と getterメソッドは「product-id」がアクセスたびに実行してしまうため、最初に読込み場合に既にアクセスしたため、setterとgetterは実行した。
公式サイト：
Both @track and @api mark a property as reactive. 
If the property’s value changes, the component rerenders. 
Tracked properties are private, 
where properties decorated with @api are public and can be set by another component.

	detail.js（一部）
	export default class Detail extends LightningElement {
	
	    // Ensure changes are reactive when product is updated
	    @track product;
	    // Private var to track @api productId
	    // _productId = undefined; // 書いても良い
	    
	    // Use set and get to process the value every time it's
	    // requested while switching between products
	    set productId(value) {
	        console.log("setDetail:"+"start")
	        this._productId = value;
	        console.log("this._productId:"+this._productId)
	        this.product = bikes.find(bike => bike.fields.Id.value === value);
	        console.log("setDetail:"+"end")
	    }
	    
	    // getter for productId
	    @api get productId(){
	        console.log("getDetail")
	        // return this._productId;
	    }
	}
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/578884/ceb40cb0-6ac9-cd8e-afa4-8d8f726f3264.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/578884/94a01a55-6681-e537-6ec0-c6105a70a030.png)
（なぜか、getterは実行してないが、知ってたらご教示ください。）
setterが実行したが、value=undefinedのため、this.productが存在しない。

はい、ここまでは画面がなぜこうな風になったのか、説明は以上です。
css、イベントの通知は今回は一旦触れないようにしてます。
何か間違って処があれば、連絡していただければ、感謝です。

リファレンス：
<a>https://trailhead.salesforce.com/ja/content/learn/modules/lightning-web-components-basics</a>
<a>https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.js_props_public</a>
<a>https://hub.appirio.jp/tech-blog/lightningwebcomponents2</a>
<a>https://developer.salesforce.com/docs/component-library/documentation/lwc/create_lists</a>