モーダル画面を使った掲示板(Bulletin board with modal screen)


+ [Lightning Experienceのホーム画面に社内のお知らせを入れたいです。](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007T3cvg)


自社では日の目を見ることなく、今は使ってない掲示板です。似たような質問があったのでちょっとまとめてみました。
[英語版](https://qiita.com/Keiji_otsubo/items/8f2a2f469e64d71e13da)

[エクスペリエンスビルダーで配置したレコードリスト（カスタムオブジェクト）の内容を画面遷移しないで表示したい](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000FY6QJSA1)

こんな感じです。
表示するにはカスタムコンポーネントを使っていますが、掲示板自体の登録は標準画面で行います。単なる手抜きですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/51643fc8-2b6b-4e4d-9649-797a5670626c.png)

# カスタムオブジェクト

カスタムフィールド(custom field)とName

|項目の表示ラベル(field label)|項目名(field API name)|データ型(Type)|
|:-------------|:-----|:------|
|タイトル|title__c|テキスト(80)|
|公開|isrelease__c|チェックボックス|
|内容区分|body_type__c|選択リスト|
|掲示No|Name|自動採番|
|掲示内容|Body__c|リッチテキストエリア(32768)|
|終了日|end_date__c|日付|
|開始日|start_date__c|日付|

# コンポーネントの一覧

+ BulletinBoardsearch  : 掲示板コンポーネント本体(Bulletin board component body)
+ BulletinBoardList    : データテーブルのコンポーネント(Data table component)
+ BulletinBoardModal   : 詳細を表示するモーダルコンポーネント(modal component)

+ BulletinBoardsearch.apex : BulletinBoardsearcで使うApexクラス (apex class)
+ BulletinBoardsearch_test : テストクラス (test class)


# BulletinBoardsearch

```
<aura:component controller="BulletinBoardsearch" implements="flexipage:availableForAllPageTypes" access="global">
	<!-- ************************************************* -->
    <!--  K.Otsubo 2019/06/27                              -->
    <!--  BulletinBoardsearch                              -->
    <!-- ************************************************* -->
    
        
    <!-- 画面ロード時のイベントがあればここに書く          -->  
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <aura:attribute name="myUser" type="User" default="{ 'sobjectType': 'User' }"/>
    <aura:attribute name="myheight" type="String" default="height:230px; overflow-y: auto !important"/>
    <aura:attribute name="myData" type="List" />     
    <aura:attribute name="myColumns" type="List" />　
	<aura:attribute name="message" type="String" />
    <aura:attribute name="dispMap" type="Map" />
    
    
    <div class="slds-col scrollable" style="{!v.myheight}">    
        <lightning:layout horizontalAlign="spread" multipleRows="true">  
                                      
            <lightning:layoutItem padding="around-xxsmall" flexibility="grow" size="12" mediumDeviceSize="12" largeDeviceSize="12">
                
                
                <c:BulletinBoardList myColumns="{!v.myColumns}" myData="{!v.myData}" aura:id="BulletinBoardList"/>
                
            </lightning:layoutItem>
        </lightning:layout>	 
    </div>
</aura:component>
```

```
({
    
	doInit : function(component, event, helper) {    
        //初期値セット 
		var action = component.get("c.getInitSearch");
        //action.setParams({"whereString":whereString});
        action.setCallback(this, function(response){
            if (action.getState() == "SUCCESS") {            	
                var myVal = response.getReturnValue();
                helper.setAll(component, event,myVal);
                component.set("v.myUser",myVal.myUser);
                component.set("v.dispMap",myVal); 
                //component.set("v.myheight",myVal.myheight);       
                var childCmp2 = component.find("BulletinBoardList");
                childCmp2.search_sheet(myVal.myData); 
                
            }  else if (action.getState() == "ERROR" ) {
                var errors = response.getError();
                if (errors[0] && errors[0].message) {
                    // サーバーサイドでcatchできなかったパターン
                	component.set("v.message", errors[0].message);
                } 
            }
        });
        $A.enqueueAction(action);
    },
})
```

```
({
	setAll : function(component, event,myVal) {   
        component.set("v.myColumns",myVal.myColumns);
        component.set("v.myData",myVal.myData);
	},
})
```

```
.THIS {
}

.THIS .fkd-button {
    padding-top: 1.5rem;
}

.THIS .customIcon div{
    vertical-align: top !important;
}
```

# BulletinBoardList

```
<aura:component implements="force:appHostable" access="global">
	<!-- ************************************************* -->
    <!--                                                   -->
    <!-- K.Otsubo 2019/06/27                               -->
    <!-- BulletinBoardList                                 -->
    <!-- ************************************************* -->
    
    <aura:attribute name="sortedBy" type="String" />
    <aura:attribute name="sortedDirection" type="String" />
    
    <aura:attribute name="myIdM" type="String" />
    <aura:attribute name="myData" type="List" />     
    <aura:attribute name="myDataDisplay" type="List" /> 
    <aura:attribute name="myColumns" type="List" />　
    <aura:attribute name="draftValues" type="List" />
    <aura:attribute name="myUser" type="User" default="{ 'sobjectType': 'User' }"/>
    
	<aura:attribute name="listOfAll" type="list"/> 
    <aura:attribute name="PaginationList" type="list"/> 
    <aura:attribute name="selectedCount" type="integer" default="0"
                    description="selected Records Count"/>
    <aura:attribute name="startPage" type="Integer" />
    <aura:attribute name="endPage" type="Integer"/>
    <aura:attribute name="totalRecordsCount" type="Integer"/>
    <aura:attribute name="pageSize" type="Integer" default="5"
                    description="number of records to be display on per page"/>
    <aura:attribute name="currentPage" type="integer" default="1"/>
    <aura:attribute name="totalPagesCount" type="integer" default="0"/>
    <aura:attribute name="bNoRecordsFound" type="boolean" default="true"/>
    
    
    <aura:method name="search_sheet">  
        <aura:attribute name="myData" type="List" />     
    </aura:method>
        
    <!-- <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> -->
    
    <div class="slds-box slds-box_xx-small slds-text-align_left slds-m-around_xx-small slds-theme_default">
        <lightning:layout verticalAlign="left" multipleRows="true">
            
            <div class="slds-size_12-of-12">
                <aura:if isTrue="{!v.totalPagesCount > 1}">
                    <div class="slds-align_absolute-center">                       
                        <div class="slds-m-around_xx-small">                            
                            <div class="paginator">
                                <aura:if isTrue="{!v.startPage == 0}">
                                    <span class="prevNextLinks"><img src="/s.gif" class="prevoff" alt="前へ"></img>前へ</span>
                                    <aura:set attribute="else">
                                        <span class="prevNextLinks"><a href="javaScript:void(0)" onclick="{!c.navigation}" data-name="previous"><img src="/s.gif" class="prev" alt="前へ"></img>前へ</a></span>                                    
                                    </aura:set>
                                </aura:if>
                            </div>
                        </div>
                        
                        <div class="slds-m-around_xx-small">                            
                            <span class="slds-badge slds-badge_inverse ">                                            
                                Page {!v.currentPage} out of {!v.totalPagesCount}
                            </span> 
                        </div>
                        
                        <div class="slds-m-around_xx-small">
                            <div class="paginator">
                                <aura:if isTrue="{!(v.endPage + 1) >= v.totalRecordsCount}">
                                    <span class="prevNextLinks">次へ<img src="/s.gif" class="nextoff" alt="次へ"></img></span>
                                    <aura:set attribute="else">
                                        <span class="prevNextLinks"><a href="javaScript:void(0)" onclick="{!c.navigation}" data-name="next">次へ<img src="/s.gif" class="next" alt="次へ"></img></a></span>                                    
                                    </aura:set>
                                </aura:if>
                            </div> 
                        </div>                    
                    </div>
                </aura:if>
            </div>            
            
            <div class="slds-scrollable" style="height:100%;">
                <lightning:datatable data="{!v.myDataDisplay}" columns="{!v.myColumns}" 
                                     aura:id="myDataTable"
                                     onsort="{!c.updateColumnSorting}"
                                     sortedDirection="{!v.sortedDirection}" 
                                     onrowselection="{! c.getSelectedName }"
                                     onrowaction="{!c.handleRowAction}"
                                     hideCheckboxColumn ="true"
                                     minColumnWidth="40"
                                     onsave="{!c.saveTable}"
                                     sortedBy="{!v.sortedBy}" keyField="Id" />
            </div>
        </lightning:layout>
    </div>
    
    <c:BulletinBoardModal myId ="{!v.myIdM}" aura:id="BulletinBoardModal" 
                          detailsList="{!v.myDataDisplay}"/>                          
</aura:component>
```

```
({
    
    doInit : function(component, event, helper) {     
        //helper.doInit2(component, event);
    },
    
    search_sheet : function(component, event, helper) {
        component.set("v.currentPage",1);
        fkdutil.doInit2(component, event);
    },
    
    /* javaScript function for pagination */
    navigation: function(component, event, helper) {        
        var sObjectList = component.get("v.myData"); 
        var end = component.get("v.endPage");
        var start = component.get("v.startPage");
        var pageSize = component.get("v.pageSize");
        //var whichBtn = event.getSource().get("v.name");
        var whichBtn = event.currentTarget.dataset.name;
        // check if whichBtn value is 'next' then call 'next' helper method
        if (whichBtn == 'next') {
            component.set("v.currentPage", component.get("v.currentPage") + 1);
            var next_data = fkdutil.next(component, event, sObjectList, end, start, pageSize);
            component.set('v.myDataDisplay', next_data);
        }
        // check if whichBtn value is 'previous' then call 'previous' helper method
        else if (whichBtn == 'previous') {
            component.set("v.currentPage", component.get("v.currentPage") - 1);
            var previous_data = fkdutil.previous(component, event, sObjectList, end, start, pageSize);
            component.set('v.myDataDisplay', previous_data);
        }
        
        //component.set("v.showSaveCancelBtn",false);
        //component.set("v.message",'');
    },
    
    // Client-side controller called by the onsort event handler
    // 
    updateColumnSorting : function (component, event, helper) {        
        var fieldName = event.getParam('fieldName');
        var sortDirection = event.getParam('sortDirection');
        //// assign the latest attribute with the sorted column fieldName and sorted direction
        component.set("v.sortedBy", fieldName);
        component.set("v.sortedDirection", sortDirection);
        helper.sortData(component, fieldName, sortDirection);
    },

    handleRowAction: function (component, event, helper) {
        var action = event.getParam('action');
        var row = event.getParam('row');
        component.set("v.myIdM", row.Id);
        //alert(row.Id);
        var detailsList = component.get('v.myDataDisplay');
        //alert(JSON.stringify(detailsList));
        var childCmp = component.find("BulletinBoardModal");    
        childCmp.handleApplicationEvent(row.Id,detailsList);  
        //childCmp.handleApplicationEvent(); 
    },
})
```

```
({
	sortData: function (component, fieldName, sortDirection) {
        var data = component.get("v.myData");
        var reverse = sortDirection !== 'asc';
        data.sort(fkdutil.sortBy(fieldName, reverse))
        component.set("v.myData", data);
        fkdutil.doInit2(component,event);
    },
})
```

```
.THIS {
}

.THIS .scroll-table {
    overflow: auto;
    white-space: nowrap;
    border: solid 1px #ddd;
    
}

.THIS .new-badge {
    margin: var(--lwc-spacingXSmall,0.5rem);/* 2019/10/23 add*/
    color: #333;
    display: inline-block;
    border: 1px solid #ccc;
    /*vertical-align: top;
    margin: 1px 5px 0 0;
    padding: 0 5px;*/
    border-radius: 2px;
    font-size: 60%;
    line-height: 80%;/* 140-->80*/
	/*以下は消されている */
    background: #f6f6aa;
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJod…EiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
    background: -moz-linear-gradient(top,#f6f6aa 0,#ffdb5b 100%);
    background: -webkit-gradient(linear,left top,left bottom,color-stop(0%,#f6f6aa),color-stop(100%,#ffdb5b));
    background: -webkit-linear-gradient(top,#f6f6aa 0,#ffdb5b 100%);
    background: -o-linear-gradient(top,#f6f6aa 0,#ffdb5b 100%);
    background: -ms-linear-gradient(top,#f6f6aa 0,#ffdb5b 100%);
    background: linear-gradient(to bottom,#f6f6aa 0,#ffdb5b 100%);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6aa',endColorstr='#ffdb5b',GradientType=0);    
}

.THIS .Active {
   background-color: #30afd6;   
   /*background-color: #d8edff;*/
}

.THIS .Inactive {
   /*background-color: red;  */  
}


.THIS .slds-th__action .slds-th__action-button { display: none; }

.THIS .tabStyle thead th span {
    background-color: red;
    color: white;
    line-height: 2px;
}

.THIS .Active {
   background-color: #30afd6;    
}

.THIS .Inactive {
   /*background-color: red;  */  
}


/* pager用のcss */
.THIS img body html {
    border: 0;
}

.THIS .paginator {
    margin: 0;
    white-space: nowrap;
    text-align: center;
    position: relative;
}

.THIS .paginator .prevNextLinks {
    color: #a8a8a8;
    font-size: 80%;
}

.THIS .paginator .prevNextLinks a {
    color: #000;
    text-decoration: none;
    display: inline-block;
    font-size: 80%;
}

.THIS .paginator .first {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: 0 1px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .next {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -17px 1px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .prev {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -10px 1px;
    margin: 0;
    padding: 0;
    width: 9px;
    height: 10px;
}

.THIS .paginator .last {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -27px 1px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .lastoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -27px -10px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .nextoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -17px -10px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .firstoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: 0 -10px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .prevoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -10px -10px;
    margin: 0;
    padding: 0;
    width: 9px;
    height: 10px;
}
/* pager用のcss end */
```

# BulletinBoardModal

```
<aura:component controller="BulletinBoardsearch" implements="force:appHostable" access="global">
	<!-- ************************************************* -->
    <!--  K.Otsubo 2019/06/28                              -->
    <!--  BulletinBoardModal                               -->
    <!-- ************************************************* -->
    
    

    <!-- <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> -->
    

    <aura:method name="handleApplicationEvent">
        <aura:attribute name="myId" type="String"/> 
        <aura:attribute name="detailsList" type="fkd_bulletin_board__c[]" />
    </aura:method>
    
    <aura:attribute name="dispMap" type="Map" />
    <aura:attribute name="myMap" type="Map" />
    <aura:attribute name="messageM" type="String" />
    <aura:attribute name="myUser" type="User" default="{ 'sobjectType': 'User' }"/>
    <aura:attribute name="myMode2" type="Boolean" default="false"/>
    <aura:attribute name="singleRec" type="fkd_bulletin_board__c" default="{'sobjectType' : 'fkd_bulletin_board__c'}"/>
    <aura:attribute name="singleRecD" type="fkd_bulletin_board__c" default="{'sobjectType' : 'fkd_bulletin_board__c'}"/>
    
    <aura:attribute name="parentForm" type="String"/>
    <aura:attribute name="myId" type="String"/> 
    
    <aura:attribute name="isOpen" type="boolean" default="false"/>
    
    <aura:attribute name="detailsList" type="fkd_bulletin_board__c[]" />
    <!-- aura attributes to store data/values --> 
	<aura:attribute name="listOfAllAccounts" type="list"/> 
    <aura:attribute name="PaginationList" type="list"/> 
    <aura:attribute name="selectedCount" type="integer" default="0"
                    description="selected Records Count"/>
    <aura:attribute name="startPage" type="Integer" />
    <aura:attribute name="endPage" type="Integer"/>
    <aura:attribute name="totalRecordsCount" type="Integer"/>
    <aura:attribute name="pageSize" type="Integer" default="4"
                    description="number of records to be display on per page"/>
    <aura:attribute name="currentPage" type="integer" default="1"/> 
    <aura:attribute name="totalPagesCount" type="integer"/> 
    <aura:attribute name="bNoRecordsFound" type="boolean"/>
    
    <aura:if isTrue="{!v.isOpen}">
        <div class="slds-modal slds-fade-in-open" >
            <div class="slds-modal__container fkd-slds-modal__containerM">
                <div class="slds-modal__header" role="banner">
                    <!-- ×ボタン -->
                    <lightning:buttonIcon title="Cloase"
                                          alternativeText="Close window."
                                          iconName="utility:close"
                                          variant="bare"
                                          size="large"
                                          onclick="{!c.showModalBox}"
                                          class="slds-modal__close slds-button_icon-inverse"/>

                    <h2 class="title slds-text-heading_medium" >{!v.singleRec.title__c}</h2>
                </div>
                
                <div class="slds-modal__content slds-p-around_xx-small">                    
                    <!-- <div class="fkd-scrollableM"> -->
                        {!v.messageM}
                        <lightning:layout multipleRows="true">    
                            <lightning:layoutItem padding="around-xx-small" flexibility="grow" size="12" mediumDeviceSize="12" largeDeviceSize="12"> 
                                <div class="slds-box slds-box_xx-small slds-text-align_left slds-m-around_xx-small slds-theme_shade">
                                    <!--<lightning:layout verticalAlign="center" multipleRows="true">-->
                                    
                                    <div class="slds-size_12-of-12">
                                        <div class="slds-m-around_xx-small">
                                            <div id="target">
                                                <div class="fkd-scrollableM">
                                            		<lightning:formattedRichText value="{!v.singleRec.Body__c}" aura:id="target"/>
                                                </div>
                                            </div>
                                        </div>
                                    </div>                                 
                                </div>
                            </lightning:layoutItem>
                            
                                <div class="slds-size_12-of-12">
                                </div>
                                
                                <c:fkdUIlightningOutput mySize="slds-size_3-of-12"                                                                
                                                        myLabel="作成者"
                                                        aura:id="CreatedBy.Name"
                                                        myValue="{!v.singleRec.CreatedBy.Name}" />
                                
                                <c:fkdUIlightningOutput mySize="slds-size_3-of-12"
                                                        myType="datetime"
                                                        myLabel="作成日時"
                                                        myValue="{!v.singleRec.CreatedDate}" />
                                
                                <c:fkdUIlightningOutput mySize="slds-size_3-of-12"                                                                
                                                        myLabel="最終更新者"
                                                        myValue="{!v.singleRec.LastModifiedBy.Name}" />
                                
                                <c:fkdUIlightningOutput mySize="slds-size_3-of-12"
                                                        myType="datetime"
                                                        myLabel="最終更新日時"
                                                        myValue="{!v.singleRec.LastModifiedDate}" />
                            
                            <img src="" id="result" />
                            
                            <!--</lightning:layout>-->
                        </lightning:layout>
                    <!--</div>--><!-- xx -->
                </div>
                
                <div class="slds-modal__footer">
                    <lightning:layout verticalAlign="center" multipleRows="true">
                        <div class="slds-size_1-of-12">
                            BulletinBoardModal
                        </div>
                        <div class="slds-size_8-of-12">
                            <div class="slds-align_absolute-center">
                                <div class="slds-m-around_xx-small">                            
                                    <div class="paginator">
                                        <aura:if isTrue="{!v.startPage == 0}">
                                            <span class="prevNextLinks"><img src="/s.gif" class="prevoff" alt="前へ"></img>前へ</span>
                                            <aura:set attribute="else">
                                                <span class="prevNextLinks"><a href="javaScript:void(0)" onclick="{!c.navigation}" data-name="previous"><img src="/s.gif" class="prev" alt="前へ"></img>前へ</a></span>                                    
                                            </aura:set>
                                        </aura:if>
                                    </div>
                                </div>
                                         
                                <div class="slds-m-around_xx-small">                            
                                    <span class="slds-badge slds-badge_inverse ">                                            
                                        Page {!v.currentPage} out of {!v.totalPagesCount}
                                    </span> 
                                </div>
                                
                                <div class="slds-m-around_xx-small">
                                    <div class="paginator">
                                        <aura:if isTrue="{!(v.endPage + 1) >= v.totalRecordsCount}">
                                            <span class="prevNextLinks">次へ<img src="/s.gif" class="nextoff" alt="次へ"></img></span>
                                            <aura:set attribute="else">
                                                <span class="prevNextLinks"><a href="javaScript:void(0)" onclick="{!c.navigation}" data-name="next">次へ<img src="/s.gif" class="next" alt="次へ"></img></a></span>                                    
                                            </aura:set>
                                        </aura:if>
                                    </div> 
                                </div>                                                    
                                
                            </div>
                        </div>
                        <div class="slds-size_3-of-12">
                            <div class="slds-xx-small-buttons_horizontal">                                
                                <lightning:button variant="neutral" label="閉じる" onclick="{!c.showModalBox}"  />
                                <!-- <lightning:button variant="brand" label="更新" onclick="{!c.saveModal}" aura:id="myUpdate" />  -->   
                                <!-- <lightning:button variant="neutral" label="印刷" onclick="{!c.print_out}"  /> -->
                            </div>
                        </div>
                    </lightning:layout>
                </div>
                
            </div>
        </div>
        <div class="slds-backdrop slds-backdrop_open" ></div>
    </aura:if>
    
</aura:component>
```

```
({
	    
    handleApplicationEvent : function(component, event, helper) {
        //method化       
        //画面がちらつくので、ここで一旦クリアー
        component.set("v.singleRec","");
        component.set("v.singleRecD","");   
        //var xx = event.getParam('myId');//うまくいかないな
        //alert(xx);
        var myId = component.get("v.myId");
        //var myId = event.getParam("v.myId");
        
        if ($A.util.isUndefined(myId) || $A.util.isEmpty(myId)){
            //新規追加              
            component.set("v.myMode2",true);
        } else {
            //修正
            component.set("v.myMode2",false);
        }
        helper.getSingleRec(component, event);  
        //ページャーのための準備
        helper.setPager(component, event,myId);
        
		component.set("v.isOpen","true");
	},
    
    showModalBox : function(component, event, helper) {
        //Cancelボタン
        component.set("v.isOpen","false");
    },
    
    navigation: function(component, event, helper) {
        //ページャーでの移動
        //var whichBtn = event.getSource().get("v.name");
        var whichBtn = event.currentTarget.dataset.name;
        var detailsList = component.get("v.detailsList");
        var currentPage = component.get("v.currentPage");
        if (whichBtn == 'next') {
            var myId = detailsList[(currentPage -1) +1 ].Id;            
        }
        else if (whichBtn == 'previous') {
            var myId = detailsList[(currentPage -1) -1].Id;
        }
        component.set("v.myId",myId);
        //画面がちらつくので、ここで一旦クリアー
        component.set("v.singleRec","");
        component.set("v.singleRecD","");  
        helper.getSingleRec(component, event);  
        //ページャーのための準備
        helper.setPager(component, event, myId); 
    },
    
    print_out : function(component, event, helper) {
        //Chrome うごかない
        //windows.print();
        //if (window.stop) {
        //    location.reload(); //triggering unload (e.g. reloading the page) makes the print dialog appear
        //    window.stop(); //immediately stop reloading
        //}
        
        //HTML内に画像を表示
        var childCmp2 = component.find("CreatedBy.Name");
        html2canvas(childCmp2,{
        //html2canvas(document.getElementById("target"),{
            onrendered: function(canvas){
                //imgタグのsrcの中に、html2canvasがレンダリングした画像を指定する。
                var imgData = canvas.toDataURL("image/png");
                document.getElementById("result").src = imgData;
            }
        });        
       
    },
    
})
```

```
({
	getSingleRec : function(component, event) {
        var myMode2 =  component.get("v.myMode2");
        if (myMode2 == true) {
            //新規登録
            //var parentId = component.get("v.parentId");//受注のId    
            //var action = component.get("c.mySingleRec_create");
            //action.setParams({"parentId":parentId});            
        } else{
            //編集 
            var myId = component.get("v.myId");//自身のId   
            var action = component.get("c.mySingleRec_edit");//Apexの関数名とは一致させないこと            
            action.setParams({"myId":myId});    
        }
        action.setCallback(this, function(response){   
            if (action.getState() == "SUCCESS") {
                var myVal = response.getReturnValue();
                component.set("v.dispMap",myVal);   
                component.set("v.myUser",myVal.myUser);
                component.set("v.singleRec",myVal.singleRec);
                component.set("v.singleRecD",myVal.singleRecD);                
            }  else if (action.getState() == "ERROR" ) {
                var errors = response.getError();
                if (errors[0] && errors[0].message) {
                    // サーバーサイドでcatchできなかったパターン
                    component.set("v.messageM", errors[0].message);
                } 
            }
        });
        $A.enqueueAction(action);
    },
    
    setPager : function(component, event,myId) {
        //ページャーのための準備
        var detailsList = component.get("v.detailsList");
        var totalPagesCount = detailsList.length;
        component.set("v.totalPagesCount",totalPagesCount);//トータル数
        component.set("v.totalRecordsCount",totalPagesCount);
        var currentPage = 1;
        for (var step = 0; step < totalPagesCount ; step++) {
            if (detailsList[step].Id == myId) currentPage = step + 1;
        }//end of for
        component.set("v.currentPage",currentPage);//現在のレコード位置
        if (currentPage == 1) component.set("v.startPage",0);
        if (currentPage != 1) component.set("v.startPage",currentPage -1);
        if (currentPage == totalPagesCount) component.set("v.endPage",totalPagesCount);
        if (currentPage != totalPagesCount) component.set("v.endPage",currentPage - 1);
    },
})
```

```
.THIS {
}

.THIS .slds-modal__header{
    height: 70px;
}

.THIS .fkd-scrollableM {
    height: 340px;
    overflow-y:auto;
}
        
.THIS .fkd-slds-modal__containerM {
    max-width: 70rem !important;
    width:70% !important;
}

.THIS .fkd-button2 {
    padding-top: 1.8rem;
}

.THIS .fkd-Checkbox {
    padding-top: 0.2rem;
}

.THIS some-indicator > lightning-primitive-icon > svg > use{
	fill: rgb(0,0,0);  /* fill: none; の代わり */;
}


.THIS .slds-th__action .slds-th__action-button { display: none; }

.THIS .tabStyle thead th span {
    background-color: red;
    color: white;
    line-height: 2px;
}

.THIS .Active {
   background-color: #30afd6;    
}

.THIS .Inactive {
   /*background-color: red;  */  
}


/* pager用のcss */
.THIS img body html {
    border: 0;
}

.THIS .paginator {
    margin: 0;
    white-space: nowrap;
    text-align: center;
    position: relative;
}

.THIS .paginator .prevNextLinks {
    color: #a8a8a8;
    font-size: 80%;
}

.THIS .paginator .prevNextLinks a {
    color: #000;
    text-decoration: none;
    display: inline-block;
    font-size: 80%;
}

.THIS .paginator .first {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: 0 1px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .next {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -17px 1px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .prev {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -10px 1px;
    margin: 0;
    padding: 0;
    width: 9px;
    height: 10px;
}

.THIS .paginator .last {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -27px 1px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .lastoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -27px -10px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .nextoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -17px -10px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .firstoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: 0 -10px;
    width: 9px;
    height: 10px;
}

.THIS .paginator .prevoff {
    background-image: url(/img/paginationArrows.gif);
    background-repeat: no-repeat;
    background-position: -10px -10px;
    margin: 0;
    padding: 0;
    width: 9px;
    height: 10px;
}
/* pager用のcss end */
```

# Apex クラス


```
public with sharing class BulletinBoardsearch {
	// *************************************************
    // 掲示板索用画面 Apex                        
    // K.Otsubo 2019/06/27
    //                               
    // *************************************************
    
    //************
    //*** 定数 ***
    //************
    private static final Integer myLimit = 100;      //検索レコード数 
    private static final Integer myLimit2 = 50;     //検索レコード数 (半数)
    
        /**
     * 画面の高さ
     */
    @AuraEnabled
  	public static String getmyheight() {
        String RetStr = fkd_User.calcMyheight(340);
        return RetStr;
    }
    
    /**
     * doInitの呼び出しをまとめる
     */    
    @AuraEnabled
    public static Map<String,Object> getInitSearch() {
        Map<String,Object>  RetMap = new Map<String,Object>();
        
        User myUser = fkd_User.getCurrentUser0();
        RetMap.put('myUser',myUser);
        
        RetMap.put('myheight',getmyheight());//画面の高さ        
        
        RetMap.put('myColumns',getColumns ());/* tebleの属性 */
        
        String whereString ='';
        //RetMap.put('sales_staff_name',myUser.Name);
        //String whereString =' sales_office__c =\'' + myUser.sales_office__c + '\' ';        
        //String whereString =' department__c =\'' + myUser.department__c + '\' ';
        //whereString = whereString + ' AND estimate_create_date__c != null ';
        //whereString = whereString + ' AND sales_order_memo_create_date__c = null  ';//作成中ものだけ
        //whereString = whereString + ' AND sales_staff_id__r.Name = \'' + myUser.Name + '\' ';//自分の分だけ
        RetMap.put('myData',getData (whereString));/* Data */
        
        RetMap.put('department_op',fkd_common.getselectOptions_A07c(myUser.department__c,'1',true));
        
        return RetMap;
        
    }
    
    @AuraEnabled
    public static Map<String,Object>  getformSearch(String whereString) {
        //system.debug(Logginglevel.INFO,'### logging ##### whereString ----> '+ where_string0 );
		Map<String,Object>  RetMap = new Map<String,Object>();
                
        RetMap.put('myColumns',getColumns ());/* tebleの属性 */        
 		if (whereString !='') whereString = whereString ;
        RetMap.put('myData',getData (whereString));/* Data */        
        
        return RetMap;
    }
    
    public static List<Map<String,Object>> getData (String whereString) {    
        List<Map<String,Object>> myDataList = new List<Map<String,Object>>();
        if (whereString !='') whereString = ' AND ' + whereString;
                
        String mySOQL = 'SELECT Id,Name, ' + 
                        ' title__c,body_type__c, ' +             			
            			' CreatedBy.Name,CreatedDate,LastModifiedBy.Name,LastModifiedDate ' +
                        ' FROM fkd_bulletin_board__c ' +
                        ' WHERE isrelease__c = true ' +
                        whereString +
                        ' ORDER BY start_date__c desc,LastModifiedDate desc LIMIT ' +  String.valueOf(myLimit);//LastModifiedDate
        
        system.debug(Logginglevel.INFO,'### whereString ############ ----> '+ whereString );   
        //system.debug(Logginglevel.INFO,'### logging ############ ----> '+ mySOQL );     
        List<fkd_bulletin_board__c> myBulletinBoardList = Database.query(mySOQL);
        //system.debug(Logginglevel.INFO,'### logging ############ ----> '+ myConsList ); 
        
        Date toDay = Date.today();
        String Name_st ='';
        Integer I = 0;
        for (fkd_bulletin_board__c myList : myBulletinBoardList) {
            Map<String, Object> myMap = new Map<String, Object>();        	
        	myMap.put('Id',myList.Id);            
            myMap.put('Name',myList.Name);
            Name_st = '/lightning/n/fkd0002form?c__id=' + myList.Id  +'&c__name=edit'; //デフォルトネームスペース　c__ をつける対応           
            myMap.put('Name_st',Name_st);
            myMap.put('urlLabel',myList.Name);
            myMap.put('title__c',myList.title__c);
            myMap.put('body_type__c',myList.body_type__c);
            myMap.put('CreatedDate',myList.CreatedDate);
            myMap.put('CreatedBy.Name',myList.CreatedBy.Name);
            myMap.put('LastModifiedDate',myList.LastModifiedDate);
            myMap.put('LastModifiedBy.Name',myList.LastModifiedBy.Name);   
            Date startDate = Date.valueOf(myList.CreatedDate);
            Integer numberDaysDue = startDate.daysBetween(toDay);
            if (numberDaysDue > 3) myMap.put('new','');   
            if (numberDaysDue > 3) myMap.put('_new-badge_','');
			if (numberDaysDue <= 3) myMap.put('new','new!'); 
            if (numberDaysDue <= 3) myMap.put('_new-badge_','new-badge');
            myDataList.add(myMap);
        }
        
        return myDataList;
    }
    
    /**
     * tableの定義体
     */
    public static List<Map<String,Object>> getColumns () {    
        List<Map<String,Object>> myTabList = new List<Map<String,Object>>();
        
        //一番最後はインライン編集 1360x768 の場合1290pxまで
        myTabList.add(fkd_common.mapColumn('new-badge','','new',true,50,false));    
        myTabList.add(fkd_common.mapColumn('Detail2','詳細','closeDate',false,60,false));  
        //myTabList.add(fkd_common.mapColumn('url','物件名','Name_st',true,200,false));        
        myTabList.add(fkd_common.mapColumn('text','区分','body_type__c',true,200,false));     
        myTabList.add(fkd_common.mapColumn('text','タイトル','title__c',true,380,false));        
		//myTabList.add(fkd_common.mapColumn('text','作成者','CreatedBy.Name',true,90,false));
		myTabList.add(fkd_common.mapColumn('text','更新者','LastModifiedBy.Name',true,90,false));
		myTabList.add(fkd_common.mapColumn('date2','更新日','LastModifiedDate',true,80,false));
        return myTabList;
    }
    
      
    /**
     * モーダルデータ取得用(参照用)
     */    
    public static fkd_bulletin_board__c getBulletinBoardD (String myId) {
         fkd_bulletin_board__c myObj = new fkd_bulletin_board__c();
        
        String whereString =' AND Id =\'' + myId + '\'';
        
        String mySOQL = 'SELECT Id,Name, ' +
            ' CreatedBy.Name,CreatedDate,LastModifiedBy.Name,LastModifiedDate ' +
            ' FROM fkd_bulletin_board__c ' +
            ' WHERE LastModifiedDate != null ' +
            whereString +
            ' ORDER BY CreatedDate ';//LastModifiedDate
        
        List<fkd_bulletin_board__c> myList = Database.query(mySOQL);
        
        if (myList.size() > 0) {
            myObj = myList[0];//1件だけ
            //System.debug(Logginglevel.INFO,'============================logging '+ myObj.item_type__c);
        }
        
        return myObj;
    }
    
    
}
```

```
@isTest
public class BulletinBoardsearch_test {
	// *************************************************
    // 掲示板索用画面 Test Apex                        
    // K.Otsubo 2019/06/28
    //                               
    // *************************************************
    
    static testMethod void test_main(){
        User u = fkd_User.createTestUser();
         
        Test.startTest();
         
        // 作成したユーザで処理を実行
    	System.runAs(u){    
            fkd_bulletin_board__c myObj = new fkd_bulletin_board__c();
            myObj.title__c = 'test';
            myObj.isrelease__c = True;
            insert myObj;
            
            fkd_bulletin_board__c myObj2 = new fkd_bulletin_board__c();
            myObj2.title__c = 'test2';
            myObj2.isrelease__c = false;
            insert myObj2;
            
            String whereString = '';
            Map<String,Object> myVal = BulletinBoardsearch.getInitSearch();
            List<Map<String,Object>> myData = (List<Map<String,Object>>)myVal.get('myData');
            System.assertEquals(myData.size(), 1); 
            
            whereString = ' title__c like \'%test%\'';
            Map<String,Object> myVal2 = BulletinBoardsearch.getformSearch(whereString);
            List<Map<String,Object>> myData2 = (List<Map<String,Object>>)myVal2.get('myData');
            System.assertEquals(myData2.size(), 1); 
            
            Map<String,Object> myVal3 = BulletinBoardsearch.mySingleRec_edit(myObj.Id);
            fkd_bulletin_board__c myBulletinBoard = (fkd_bulletin_board__c)myVal3.get('singleRec');
            System.assertEquals(myBulletinBoard.title__c, 'test'); 
            
        }
        Test.stopTest();
        
    }
    
}
```

DataTable用のカラム作成の別クラス

```
    /**
     * DataTable用のカラム作成
     * myFieldNameをTrimしておく 2019/02/26     
     **/
    public static Map<String,Object> mapColumn (String myType,
                                                String myLabel,
                                                String myFieldName,
                                                Boolean mySortable,
                                                Integer myInitialWidth,
                                                Boolean myEditable) {
       Map<String,Object> myMap = new Map<String,Object>();
       //選択されたセルに強制的に色をつける
       Map<String,String> cellAttributes2 = new  Map<String,String>();
       cellAttributes2.put('fieldName','_status_');                
       Map<String,Object> cellAttributes = new  Map<String,Object>();
       cellAttributes.put('class',cellAttributes2);                                              
                                                    
        switch on myType {
            when 'Detail' {
                Map<String,Object> rowActions = new  Map<String,Object>();
                rowActions.put('iconName', 'utility:record_create');
                rowActions.put('label', myLabel);
                rowActions.put('name', 'show_details');
                rowActions.put('title', myFieldName.trim());
                myMap.put('typeAttributes',rowActions);
                //myMap.put('label',myLabel);        
                //myMap.put('fieldName',myFieldName.trim());        
                myMap.put('type','button');
                myMap.put('initialWidth',myInitialWidth);
            }
            
            when 'Detail2' {
                Map<String,Object> rowActions = new  Map<String,Object>();
                rowActions.put('iconName', 'utility:edit_form');
                rowActions.put('variant', 'bare');
                //rowActions.put('label', myLabel);
                rowActions.put('name', 'show_details2');
                rowActions.put('title', myFieldName.trim());
                myMap.put('typeAttributes',rowActions);
                //myMap.put('label',myLabel);        
                //myMap.put('fieldName',myFieldName.trim());        
                myMap.put('type','button-icon');
                myMap.put('initialWidth',myInitialWidth);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
            
            when 'Detail3' {
                Map<String,Object> rowActions = new  Map<String,Object>();
                rowActions.put('iconName', 'utility:edit');
                rowActions.put('variant', 'bare');
                //rowActions.put('label', myLabel);
                rowActions.put('name', 'show_details3');
                rowActions.put('title', myFieldName.trim());
                myMap.put('typeAttributes',rowActions);
                //myMap.put('label',myLabel);        
                //myMap.put('fieldName',myFieldName.trim());        
                myMap.put('type','button-icon');
                myMap.put('initialWidth',myInitialWidth);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
        	when 'url' {
				myMap.put('label',myLabel);        
                myMap.put('fieldName',myFieldName.trim());        
                myMap.put('type',myType);
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('editable',myEditable); 
                myMap.put('initialWidth',myInitialWidth); //数値は''で囲まない
                Map<String,String> actions2 = new  Map<String,String>();
                actions2.put('fieldName','urlLabel');                
                Map<String,Object> actions = new  Map<String,Object>();
                actions.put('label',actions2);               
                actions.put('tooltip',actions2);
                actions.put('target','_self'); //2018/10/30 これがないと別画面になる仕様変更か？_blank _self
                myMap.put('typeAttributes',actions);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
            when 'fkd0042List' {
                myMap.put('label',myLabel);        
                myMap.put('fieldName',myFieldName.trim());        
                myMap.put('type','action');
                myMap.put('initialWidth',myInitialWidth); //数値は''で囲まない
                List<Map<String,Object>> actionList = new  List<Map<String,Object>>();
                Map<String,String> actions = new  Map<String,String>();
                actions.put('label','選択解除');  
                actions.put('name','Release'); //handleRowActionに送られる
                actionList.add(actions);
                Map<String,List<Map<String,Object>>> actions2 = new  Map<String,List<Map<String,Object>>>();
                actions2.put('rowActions',actionList); 
                myMap.put('typeAttributes',actions2);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
                //system.debug(Logginglevel.INFO,'############### ----> '+ myMap); 
            }
            when 'url2' {
				myMap.put('label',myLabel);        
                myMap.put('fieldName',myFieldName.trim());        
                myMap.put('type','url');//注意2020/11/25
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('editable',myEditable); 
                myMap.put('initialWidth',myInitialWidth); //数値は''で囲まない
                Map<String,String> actions2 = new  Map<String,String>();
                actions2.put('fieldName','urlLabel');                
                Map<String,Object> actions = new  Map<String,Object>();
                actions.put('label',actions2);               
                actions.put('tooltip',actions2);
                actions.put('target','_blank'); //2018/10/30 これがないと別画面になる仕様変更か？_blank _self
                myMap.put('typeAttributes',actions);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
            when 'url22' {
				myMap.put('label',myLabel);        
                myMap.put('fieldName',myFieldName.trim());        
                myMap.put('type','url');//注意2020/11/25
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('editable',myEditable); 
                myMap.put('initialWidth',myInitialWidth); //数値は''で囲まない
                Map<String,String> actions2 = new  Map<String,String>();
                actions2.put('fieldName','urlLabel2');                
                Map<String,Object> actions = new  Map<String,Object>();
                actions.put('label',actions2);               
                actions.put('tooltip',actions2);
                actions.put('target','_blank'); //2018/10/30 これがないと別画面になる仕様変更か？_blank _self
                myMap.put('typeAttributes',actions);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
            when 'boolean' {
                myMap.put('type',myType);
                myMap.put('label',myLabel);
                myMap.put('fieldName',myFieldName.trim());                
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('initialWidth',myInitialWidth);
                
                //選択されたセルに強制的に色をつける
                Map<String,String> cellAttributes_b2 = new  Map<String,String>();
                cellAttributes_b2.put('fieldName','_status_');                
                Map<String,Object> cellAttributes_b = new  Map<String,Object>();
                cellAttributes_b.put('class',cellAttributes_b2);    
                cellAttributes_b.put('fieldName','utility:chec'); 
                //myMap.put('cellAttributes','{"fieldName": "utility:chec"}');//utility:sentiment_negative
                myMap.put('cellAttributes',cellAttributes_b);//強制的に色をつける
            }
            when 'text' {
                myMap.put('type',myType);
                myMap.put('label',myLabel);
                myMap.put('fieldName',myFieldName.trim());        
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('wrapText','true');//2020/11/26add
                myMap.put('initialWidth',myInitialWidth);                
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
                //titleが利かない
                //Map<String,String> actions2 = new  Map<String,String>();
                //actions2.put('fieldName','urlLabel'); 
                //Map<String,Object> actions = new  Map<String,Object>();                             
                //actions.put('title',actions2);
                //myMap.put('typeAttributes',actions);  
                
                //Map<String,Object> myAttributes = new  Map<String,Object>();                
                //myAttributes.put('title','xxx'); 
                //myMap.put('typeAttributes',myAttributes);
            }
            when 'date2' {
                myMap.put('type','date');
                myMap.put('label',myLabel);
                myMap.put('fieldName',myFieldName.trim());        
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('initialWidth',myInitialWidth);
                Map<String,Object> myAttributes = new  Map<String,Object>();                
                myAttributes.put('year','2-digit');
                myAttributes.put('month','2-digit');
                myAttributes.put('day','2-digit');
                myMap.put('typeAttributes',myAttributes);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
            when 'number3.3' {
                myMap.put('type','number');
                myMap.put('label',myLabel);
                myMap.put('fieldName',myFieldName.trim());        
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('initialWidth',myInitialWidth);
                Map<String,Object> myAttributes = new  Map<String,Object>();                
                myAttributes.put('minimumFractionDigits',3);
				//myAttributes.put('miniimumFractionDigits',0);                  
                myMap.put('typeAttributes',myAttributes);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
            when 'number3.2' {
                myMap.put('type','number');
                myMap.put('label',myLabel);
                myMap.put('fieldName',myFieldName.trim());        
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('initialWidth',myInitialWidth);
                Map<String,Object> myAttributes = new  Map<String,Object>();                
                myAttributes.put('minimumFractionDigits',2);
				//myAttributes.put('miniimumFractionDigits',0);                  
                myMap.put('typeAttributes',myAttributes);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
            when 'new-badge' {
                //指定するcssはBulletinBoardListを参照のこと 2019/10/23
                Map<String,String> cellAttributes_new2 = new  Map<String,String>();
                cellAttributes_new2.put('fieldName','_new-badge_');                
                Map<String,Object> cellAttributes_new = new  Map<String,Object>();
                cellAttributes_new.put('class',cellAttributes_new2);          
                myMap.put('type',myType);
                myMap.put('label',myLabel);
                myMap.put('fieldName',myFieldName.trim());        
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('initialWidth',myInitialWidth);
                myMap.put('cellAttributes',cellAttributes_new);//new-badge
            }
            when else {
                myMap.put('type',myType);
                myMap.put('label',myLabel);
                myMap.put('fieldName',myFieldName.trim());        
                if (mySortable == true ) myMap.put('sortable','true');
                if (myEditable == true ) myMap.put('editable','true'); 
                myMap.put('initialWidth',myInitialWidth);
                myMap.put('cellAttributes',cellAttributes);//強制的に色をつける
            }
        }
        return myMap;
    }
```