SlideShare a Scribd company logo
聞いてスッキリ!Lightningの理解ポイント
~ひと味足りない業務アプリに
「SuPICE」を効かせて欲しかったを実現~
2015年12月04日
株式会社テラスカイ 製品開発部
吉田 寛
Salesforce World Tour Tokyo 2015
Developer Theater Session
会社紹介:概要
Copyright © 2015 Terrasky Co., Ltd. All Rights Reserved. 2
社 名 : 株式会社テラスカイ
所在地 : 〒103-0027
東京都中央区日本橋1-3-13
東京建物日本橋ビル7階
事業所 : 大阪、名古屋
設 立 : 平成18(2006)年4月
資本金 : 4億5403.5万円
代表者 : 代表取締役社長 佐藤 秀哉
情報管理: ISO 27001/IS 561777
URL : http://www.terrasky.co.jp/
会社紹介:多くの専門技術者
Copyright © TerraSky Co., Ltd. All Rights Reserved. 3
2015年12月1日時点
AWS認定ソリューションアーキテクト
認定SalesCloud
コンサルタント
認定ServiceCloud
コンサルタント
認定上級
デベロッパー
認定上級
アドミニストレーター
Force.com MVP 認定テクニカルアーキテクト
みずほキャピタル 様 富士通ゼネラル 様
損保ジャパン日本興亜システムズ様 エフエーサービス様
昭和シェル石油 様 ダンアンドブラッドストリート 様
小田急電鉄 様 デジタルハリウッド 様
小田急バス 様 パソナグループ 様
アリスタ ライフサイエンス 様 リンクイベントプロデュース 様
日立アロカメディカル 様 富士通ラーニングメディア 様
楽天 様 世界自然保護基金ジャパン 様
KVH 様 その他 多数
会社紹介:卓越したクラウド導入実績
Copyright © TerraSky Co., Ltd. All Rights Reserved. 4
Salesforce導入実績
2,000案件超!
金融からコールセンター、
サービス業まで業種業態を問わず
豊富な導入実績
金融 ・ 流通
医療 ・ IT
製造 ・ サービス
不動産 ・ 教育
非営利
5
Lightning とは
Copyright © TerraSky Co., Ltd. All Rights Reserved. 6
Lightning とは
(出所:Youtube Dreamforce Keynoteより画面を引用)
7
Lightning とは、Force.com の新しいプラットフォームサービス群
Lightning コンポーネント
Lightning App Builder
Lightning Schema Builder
Lightning Connect
Salesforce Connect
Lightning Process Builder
Lightning Community Builder
(出所:salesforce.com社App Cloudサイトより画像を引用)
8
Lightning とは、Force.com の新しいプラットフォームサービス群
Lightning コンポーネント
Lightning App Builder
Lightning Schema Builder
Lightning Connect
Salesforce Connect
Lightning Process Builder
Lightning Community Builder
(出所:salesforce.com社App Cloudサイトより画像を引用)
今回のお話の範囲
Visualforce から Lightning へ
Copyright © TerraSky Co., Ltd. All Rights Reserved. 9
⁃ 新しいUIへ
⁃ 「ページ」から『コンポーネント』へ
Lightningページに
配置
Lightning の 画面開発
Copyright © TerraSky Co., Ltd. All Rights Reserved. 10
コンポーネントを
入手/開発
(出所:salesforce.com社資料「Building Lightning Components for ISVs」,Blog「Welcome to the Future of CRM. Welcome to Salesforce Lightning.」より図を引用)
デスクトップ、各デバイスで
利用
今までの利用者の作業範囲今までの開発者の作業範囲
これからの開発者の作業範囲 これからの利用者の作業範囲
Lightning の 画面開発
Copyright © TerraSky Co., Ltd. All Rights Reserved. 11
(出所:salesforce.com社資料「Building Lightning Components for ISVs」,Blog「Welcome to the Future of CRM. Welcome to Salesforce Lightning.」より図を引用)
⁃ 利用者の作業可能範囲が広がる
⁃ (イメージとして)開発者に求める作業期間が短くなる
より速い開発が必要
開発言語・使用技術 の 変化
Copyright © TerraSky Co., Ltd. All Rights Reserved. 12
⁃ 業務ロジックの実装はApexからLightning側に(と言われている)
⁃ JavaScript、CSSが多くなる
Copyright © TerraSky Co., Ltd. All Rights Reserved. 13
どんなコンポーネントが作れる?
Lightningで
Sample 1 : アコーディオンリスト
Copyright © TerraSky Co., Ltd. All Rights Reserved. 14
【AccordionList.cmp】
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" controller="AccountListController" >
<ltng:require styles="/resource/TS_AccordionList/TS_AccordionList/css/font-awesome.min.css"
scripts="/resource/TS_AccordionList/TS_AccordionList/js/jquery.min.js,
/resource/TS_AccordionList/TS_AccordionList/js/jquery-ui.min.js,
/resource/TS_AccordionList/TS_AccordionList/js/lodash.js" afterScriptsLoaded="{!c.afterScriptsLoaded}" />
<div class="sg-icon-art sg-icn--fnt center tc icon-utility-download" title="download" />
<!-- AccordionList -->
<div class="TS_component">
<h2 class="component_Title_header">
<div class="icon_Frame account_Color"><img src="/img/icon/t4v32/standard/account_120.png" class="icon " alt="Account"
title="Account" /
<span class="component_Title">Account Lists</span>
</h2>
<div class="component_Body">
<table id="AccordionListTable" ></table>
</div>
</div>
</aura:component>
【AccordionListController.js】
({
afterScriptsLoaded : function(component, event, helper) {
var expanded;
$(function () {
/* ここから */
var action = component.get("c.findAll");
var records;
action.setCallback(this, function(response){
var state = response.getState();
if (state === "SUCCESS") {
var res = response.getReturnValue();
records = Array.apply(null, new Array(res.length)).map(function (n, i) {
return {
Name: res[i].Name,
State:res[i].BillingState,
Phone: res[i].Phone,
Fax: res[i].Fax,
Address: res[i].BillingState + res[i].BillingCity + res[i].BillingStreet
};
});
}
var template = _.template('¥
<% records.forEach(function (r) { %>¥
<tr class="row">¥
<td>¥
<div class="form-control">¥
<span class="accName"><%= r.Name %></span><span class="accState"><%= r.State %></span>¥
</div>¥
<div class="content">¥
<div class="fields">¥
<span class="content_Icon"><i class="fa fa-phone"></i></span>¥
<span class="content_Value"><%= r.Phone %></span>¥
</div>¥
<div class="fields">¥
<span class="content_Icon"><i class="fa fa-fax"></i></span>¥
<span class="content_Value"><%= r.Fax %></span>¥
</div>¥
<div class="fields">¥
<span class="icon_Address"><i class="fa fa-building-o"></i></span>¥
<span class="content_Address"><%= r.Address %></span>¥
</div>¥
</div>¥
</td>¥
</tr>¥
<% }); %>¥
');
document.getElementById('AccordionListTable').innerHTML = template({records: records});
$('.row').click(function () {
if (expanded) {
$(expanded).find('.content').slideUp(300);
$(this).find('.accName').removeClass('textBold');
}
if (expanded === this) {
expanded = null;
return;
}
expanded = this;
var content = $(this).find('.content');
content.hide();
content.slideDown(300);
$('.accName').removeClass('textBold');
$(this).find('.accName').addClass('textBold');
});
});
$A.enqueueAction(action);
});
}
})
【AccordionListStyle.css】
/* Component Header */
.THIS .TS_component {
margin-left: 10px;
}
.THIS .component_Title_header{
margin: 5px;
}
.THIS .icon_Frame {
display: inline-block;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
.THIS .account_Color {
background: #7F8DE1;
}
.THIS .icon_Frame .icon {
width: 2rem;
height: 2rem;
vertical-align: middle;
}
.THIS .component_Title {
margin-left:10px;
}
/* AccordionList */
.THIS .component_Body{
width: 98%;
margin: auto;
border:solid 1px #ccc;
background: #fff;
padding: 10px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
.THIS #AccordionListTable {
margin:0 auto;
width: 100%;
}
.THIS #AccordionListTable td {
padding:10px;
border-top: solid 1px #ddd;
}
.THIS .form-control{
padding:5px;
color:#2A94D6;
}
.THIS .content {
display: none;
border: solid 1px #ccc;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
background:#F0F1F2;
background:#fff;
padding:5px;
}
.THIS .fields {
margin-bottom: 5px;
}
/* AccordionList Label */
.THIS .accName{
display: inline-block;
width:60%;
}
.THIS .accState{
display: inline-block;
text-align:right;
width:40%;
}
/* AccordionList content */
.THIS .content_Icon{
width: 30px;
text-align: center;
display: inline-block;
}
.THIS .content_Value{
display: inline-block;
width: auto;
}
/* content Addess only */
.THIS .icon_Address{
width: 30px;
text-align: center;
display: inline-block;
float: left;
}
.THIS .content_Address{
display: block;
width: auto;
margin-left: 30px;
}
.THIS .textBold {
font-weight:bold;
color:#344A5F;
}
【SuPICE.cls】
interface Action {
Object run(Object data);
}
virtual public class ActionResult {
@AuraEnabled
public final Boolean error { get; private set; }
@AuraEnabled
public final String[] messages { get; private set; }
@AuraEnabled
public final Object value { get; private set; }
public ActionResult(String[] messages, Object value) {
this.error = messages != null;
this.messages = messages;
this.value = value;
}
public ActionResult(Boolean success, String[] messages, Object value) {
this.error = success;
this.messages = messages;
this.value = value;
}
}
@AuraEnabled
global static Object action(String data) {
try {
Map<String, Object> dataMap = (Map<String, Object>) JSON.deserializeUntyped(data);
String type = (String) dataMap.get('action');
Object action = ACTION_MAP.get(type);
if (action == null) {
throw new SuPICEException('Unsupported action type: ' + type);
}
Object result = ((Action) action).run(dataMap.get('data'));
return result;
} catch (SuPICEException e) {
// SuPICE処理内で予測される例外
return new ActionResult(new String[] {e.getMessage()}, null);
} catch (Exception e) {
// 予期しない例外。とりあえずStackTraceを返す
return new ActionResult(new String[] {
ERROR.getStackTrace(e)
}, null);
}
}
private static final Map<String, Action> ACTION_MAP = new Map<String, Action> {
'query' => new QueryAction(),
'describe' => new DescribeAction(),
'save' => new SaveAction(),
'delete' => new DeleteAction()
};
class QueryActionArguments {
public String objectName { get; private set; }
public String[] fields { get; private set; }
public ConditionOperator condition { get; private set; }
public QueryActionArguments(Object root) {
Map<String, Object> rootMap = (Map<String, Object>) root;
objectName = getObject(rootMap);
fields = getFields(rootMap);
Set<String> fieldSet = new Set<String>();
fieldSet.add('Id'); //Idは必ず取得する
fieldSet.addAll(getFields(rootMap));
fields = new String[0];
fields.addAll(fieldSet);
Map<String, Object> untypedCondition = getCondition(rootMap);
if (untypedCondition != null) {
condition = parseOperator(untypedCondition);
}
}
String getObject(Map<String, Object> untyped) {
return (String) untyped.get('object');
}
String[] getFields(Map<String, Object> untyped) {
String[] typed = new String[0];
List<Object> fields = (List<Object>) untyped.get('fields');
for (Object o : fields) {
typed.add((String) o);
}
return typed;
}
Map<String, Object> getCondition(Map<String, Object> untyped) {
Object condition = untyped.get('condition');
if (condition == null) {
return null;
} else {
return (Map<String, Object>) condition;
}
}
}
class DescribeAction implements Action {
public ActionResult run(Object untyped) {
String objectName = (String)((Map<String, Object>) untyped).get('object');
SObjectType soType = Schema.getGlobalDescribe().get(objectName);
if (soType == null) {
throw ERROR.getSObjectNotFound(objectName);
}
DescribeSObjectResult describe = soType.getDescribe();
return new ActionResult(null, new DescribeResult(describe));
}
}
リストをクリック(or タップ)を
すると、詳細データが表示
JavaScript CSSApex
Sample 2 : ToDo の スワイプリスト
Copyright © TerraSky Co., Ltd. All Rights Reserved. 15
【AccordionList.cmp】
<aura:component controller="ToDoListController" implements="force:appHostable,flexipage:availableForAllPageTypes">
<aura:attribute name="toDos" type="Task[]" />
<ltng:require styles="
/resource/TS_ToDoList/Font-Awesome-master/css/font-awesome.min.css,
/resource/TS_ToDoList/Swiper-master/dist/css/swiper.min.css"
scripts="
/resource/TS_ToDoList/jquery-1.11.3.min.js,
/resource/TS_ToDoList/lodash.min.js,
/resource/TS_ToDoList/Swiper-master/dist/js/swiper.min.js,
/resource/TS_ToDoList/dateformat.js"
afterScriptsLoaded="{!c.afterScript}"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<!-- Todo List -->
<div class="TS_component">
<h2 class="component_Title_header">
<div class="icon_Frame todo_Color"><img src="/img/icon/t4v32/standard/task_120.png" class="icon"
alt="ToDo" title="ToDo" /></div>
<span class="component_Title">ToDo List</span>
</h2>
<div class="todo_List" id="todo_List">
</div>
</div>
</aura:component>
【ToDoListController.js】
({
doInit : function(component, event, helper) {
var action = component.get('c.getToDos');
action.setCallback(this,function(response){
component.set('v.toDos',response.getReturnValue());
});
$A.enqueueAction(action);
},
showSpinner : function (component, event, helper) {
var spinner = component.find('todo_spinner');
var evt = spinner.get("e.toggle");
evt.setParams({ isVisible : true });
evt.fire();
},
hideSpinner : function (component, event, helper) {
var spinner = component.find('todo_spinner');
var evt = spinner.get("e.toggle");
evt.setParams({ isVisible : false });
evt.fire();
},
afterScript : function(component, event, helper) {
var action = component.get('c.getToDos');
action.setCallback(this,function(response){
var str = '<% records.forEach(function (r) { %>¥
<div class="swiper-container">¥
<div class="done"><i class="fa fa-check-square-o"></i><br
/>Done </div>¥
<div class="delete"><i class="fa fa-trash-o"></i><br />Del
</div>¥
<div class="swiper-wrapper <%= r.style %>">¥
<div class="mark_l">¥
<i class="fa fa-caret-left"></i>¥
<i class="fa fa-hand-pointer-o"></i>Done¥
</div>¥
<div class="mark_r">¥
Delete <i class="fa fa-hand-pointer-o"></i>¥
<i class="fa fa-caret-right"></i>¥
</div>¥
<div class="swiper-slide" id="<%= r.Id %>">¥
<div class="subject"><%= r.Name %></div>¥
<span class="details"><%= r.Status %></span>¥
<span class="details"><%=
r.ActivityDate %></span>¥
</div>¥
</div>¥
</div>¥
<% }); %>';
helper.setToDoList(component,str,response.getReturnValue(),event);
helper.doSwiper(component);
});
$A.enqueueAction(action);
},
})
【ToDoListHelper.js】
({
setToDoList:function(component, str, record){
var expanded;
var self = this;
function toArray(fakeArray) {
return Array.prototype.slice.call(fakeArray);
}
$(function () {
var records = Array.apply(null, new Array(record.length)).map(function (n, i) {
/* 日付フォーマット M/d/yyyy */
var dateFormat = new DateFormat("M/d/yyyy");
var str = dateFormat.format(new Date(record[i].ActivityDate));
/** 期限を確認する **/
var today = new Date();
var date = new Date(record[i].ActivityDate);
var style = '';
if (today > date) {
style = 'expired';
}
return {
Name: record[i].Subject,
Status: record[i].Status,
IsClosed: record[i].IsClosed,
ActivityDate: str,
Id: record[i].Id,
style: style
};
});
var template = _.template(str);
document.getElementById('todo_List').innerHTML = template({records: records});
});
},
doSaveToDo: function(component, recordId){
// var upsertToDo = {'sobjectType':'Task','Id':recordId,'Status':'Completed'};
var upsertToDo = {'sobjectType':'Task','Id':recordId};
var action = component.get("c.saveToDo");
action.setParams({"tasks":upsertToDo});
action.setCallback(this,function(a){
console.log("FIN!!");
});
console.log("GO!!");
$A.enqueueAction(action);
},
doSwiper: function(component){
var self = this;
var mySwiper = new Swiper('.swiper-container',{
pagination: '.pagination',
loop:false,
paginationClickable:true,
calculateHeight:true,
touchRatio:0.6,
onTransitionStart: function (swiper){
var recordId = swiper.wrapper[0].id;
if(recordId){
self.doSaveToDo(component, recordId);
}
},
onTransitionEnd: function(swiper){
if(swiper.touches.diff <= -170){
$(swiper.container[0]).css('display','none');
} else if (swiper.touches.diff >= 170){
/* $(swiper.wrapper[0]).css({'background':'#D3D3D3','border-color':'#c7c7c7'}); */
$(swiper.wrapper[0]).addClass('todoDone');
/* $(swiper.wrapper[0]).find('.subject').addClass('todoDone'); */
}
}
});
}
})
【ToDoListStyle.css】
/* Component Header */
.THIS .component_Title_header{
margin: 5px;
}
.THIS .icon_Frame {
display: inline-block;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
}
.THIS .todo_Color {
background: #4BC076;
}
.THIS .icon_Frame .icon {
width: 2rem;
height: 2rem;
vertical-align: middle;
}
.THIS .component_Title {
margin-left:10px;
}
/* Records */
.THIS .swiper-wrapper {
height: 100%;
padding: 10px;
/* background: #8BC34A; */
background: #C8E6C9;
background: #8BC34A;
background: #fff;
border:1px solid #388E3C;
margin: 3px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
-webkit-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
-moz-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
width:auto;
}
.THIS .mark_l i{
margin-right: 2px;
}
.THIS .mark_l {
position: absolute;
top: 0;
left: 0;
margin-left: 5px;
margin-top: 5px;
color: #fff;
color: #616161;
}
.THIS .mark_r {
position: absolute;
top: 0;
right: 0;
margin-right: 5px;
margin-top: 5px;
color: #fff;
color: #616161;
}
.THIS .swiper-slide {
margin-top:20px;
width:100% !important;
border-left: solid 5px #388E3C;
padding-left: 5px;
}
.THIS .expired {
border:1px solid #D32F2F;
/* background: #eb4654; */
background: #FFCDD2;
background: #FF5252;
background: #eb4654;
background: #fff;
}
.THIS .expired .swiper-slide {
margin-top:20px;
width:100% !important;
border-left: solid 5px #D32F2F;
padding-left: 5px;
}
/* swipe時に表示される */
.THIS .done {
position: absolute;
top: 5px;
padding: 10px;
vertical-align: middle;
background: #4AB471;
color: #fff;
margin-top: 4px;
margin-left: 3px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
width:50%;
}
.THIS .delete {
position: absolute;
top: 5px;
right: 0;
padding: 10px;
text-align: right;
background: #D96383;
color: #fff;
margin-top: 4px;
margin-right: 3px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
width:48%
}
.THIS .subject {
font-size: 1.2em;
font-weight: bold;
margin-bottom: 5px;
}
.THIS .todoDone {
background: #D3D3D3;
border-color: #c7c7c7;
}
.THIS .todoDone .swiper-slide {
border-left: solid 5px #fff;
}
.THIS .details {
display: inline-block;
color: #fff;
width: 48%;
color: #616161;
}
JavaScript CSSApex
リストをスワイプをすると、
データ編集、削除等の
アクションを実行可能
Sample 3 : 取引先の検索画面
Copyright © TerraSky Co., Ltd. All Rights Reserved. 16
【AccountSearch.cmp】
<aura:component controller="AccountSearchController" implements="force:appHostable,flexipage:availableForAllPageTypes">
<ltng:require scripts="/resource/TS_AccountSearch/jquery-1.11.3.min.js,
/resource/TS_AccountSearch/lodash.min.js" />
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<div class="condition">
<!-- SearchBox Start -->
<div class="section">
<div class="sectionTitile">Search Box</div>
<div class="searchCondition">
<div class="searchItem">
<span class="searchItemLabel"></span>
<input type="text" id="selectInputItem" palaceholder="Account Name"/>
</div>
<div class="searchItem">
<span class="searchItemLabel"></span>
<select id="selectSearchItem">
<option value="">--None--</option>
</select>
</div>
<div class="searchButtonBox">
<input class="searchButton" type="button" value="Search" onclick="{!c.doSearch}"/>
</div>
</div>
</div>
<!-- Results Start -->
<div class="section">
<div class="sectionTitile">Search Results</div>
<div class="searchResult"></div>
</div>
</div>
<!-- detail Start -->
<div class="detail" style="display:none">
<div class="section">
<div id="TS_DetailSection" tabindex="0" class="sectionTitile">Detail</div>
<div class="detailList"></div>
<input type="button" value="Close" onclick="{!c.closeDetail}"/>
</div>
</div>
</aura:component>
【AccountSearchController.js】
({
doInit : function(component, event, helper){
var action = component.get('c.getOptions');
action.setCallback(this,function(response){
var sel = document.getElementById('selectSearchItem');
for(var i =0; i<response.getReturnValue().length; i++){
var val = response.getReturnValue()[i].BillingState;
if(val){
var op = document.createElement('option');
op.setAttribute('value',val);
op.innerHTML = val;
sel.appendChild(op);
}
}
});
$A.enqueueAction(action);
},
doSearch : function(component, event, helper) {
var accName = document.getElementById('selectInputItem').value;
var accState = document.getElementById('selectSearchItem').value;
var action;
if(accName !="" && accState == ""){
action = component.get('c.getAccountName');
}else if (accName == "" && accState != ""){
action = component.get('c.getAccountState');
}else{
action = component.get('c.getAccounts');
}
action.setParams({
'accName':accName,
'accState':accState
});
action.setCallback(this,function(response){
var str = '<% records.forEach(function (r) { %>¥
<div class="wrap">¥
<div class="mapframe">¥
<img class="map" src="/resource/mapicon/mapicon/icon_1r_64.png" address="<%= r.Street %>" />¥
</div>¥
<div id="<%= r.Id %>" class="recordList">¥
<div class="Name"><%= r.Name %></div>¥
<div>BillingAddress <br/>¥
Country : <%= r.Country %><br/>¥
PostalCode : <%= r.PostalCode %><br/>¥
State : <%= r.State %><br/>¥
City : <%= r.City %><br/>¥
Street : <%= r.Street %>¥
</div>¥
<div>Phone : <%= r.Phone %></div>¥
</div>¥
</div>¥
<% }); %>';
helper.setResult(component,str,response.getReturnValue(),event);
});
$A.enqueueAction(action);
},
closeDetail: function(){
// $('.detail').fadeOut('normal');
$('.detail').hide();
$('.condition').fadeIn('normal');
},
navigate : function() {
},
})
【AccountSearchHelper.js】
({
setResult: function(cmp,str,record,event) {
var self = this;
function toArray(fakeArray) {
return Array.prototype.slice.call(fakeArray);
}
$(function () {
var records = Array.apply(null, new Array(record.length)).map(function (n, i) {
var address = encodeURIComponent(record[i].BillingStreet);
return {
Id: record[i].Id,
Name: record[i].Name,
Country: record[i].BillingCountry,
PostalCode: record[i].BillingPostalCode,
State: record[i].BillingState,
City: record[i].BillingCity,
Street: record[i].BillingStreet,
Phone: record[i].Phone,
Address: address
};
});
var template = _.template(str);
document.getElementsByClassName('searchResult')[0].innerHTML = template({records: records});
});
/* 詳細表示用 */
$('.recordList').click(function (ev) {
self.setDetailList(cmp,record,ev);
$('.detail').fadeIn('normal');
$('.condition').hide();
});
/* 地図表示用 */
$('.map').click(function (e) {
var address = encodeURIComponent($(e.target).attr('address'));
var urlEvent = $A.get("e.force:navigateToURL");
urlEvent.setParams({
"url": 'https://www.google.com/maps/place/' + address
});
urlEvent.fire();
});
},
setDetailList: function(cmp,record,ev){
var self = this;
var recordId = ev.currentTarget.id;
var str = '<% records.forEach(function (r) { %>¥
<div class="recordDetail">¥
<div class="Name"><%= r.Name %></div>¥
<div>BillingAddress <br/>¥
Country : <%= r.Country %><br/>¥
PostalCode : <%= r.PostalCode %><br/>¥
State : <%= r.State %><br/>¥
City : <%= r.City %><br/>¥
Street : <%= r.Street %>¥
</div>¥
<div>Phone : <%= r.Phone %></div>¥
<div class="conRelatedList">¥
<table>¥
<thead>¥
<tr>¥
<th>No.</th><th>Contact Name</th>¥
</tr>¥
</thead>¥
<tbody class="conRelatedListBody">¥
</tbody>¥
</table>¥
</div>¥
<div class="oppRelatedList">¥
<table>¥
<thead>¥
<tr>¥
<th>No.</th><th>Opportunity Name</th>¥
</tr>¥
</thead>¥
<tbody class="oppRelatedListBody">¥
</tbody
>¥
</table>¥
</div>¥
</div><br/>¥
<% }); %>'
function toArray(fakeArray) {
return Array.prototype.slice.call(fakeArray);
}
$(function () {
var records = Array.apply(null, new Array(record.length)).map(function (n, i) {
return {
Id: record[i].Id,
Name: record[i].Name,
Country: record[i].BillingCountry,
PostalCode: record[i].BillingPostalCode,
State: record[i].BillingState,
City: record[i].BillingCity,
Street: record[i].BillingStreet,
Phone: record[i].Phone,
Contacts: record[i].Contacts,
Opportunities: record[i].Opportunities
};
});
var template = _.template(str);
for(var i=0; i<records.length; i++){
if(records[i].Id == recordId){
document.getElementsByClassName('detailList')[0].innerHTML = template({records: [records[i]]});
self.setRelatedList(records[i]);
}
}
});
},
setRelatedList: function(record){
var conStr = '<% records.forEach(function (r) { %>¥
<tr>¥
<td>No.<%= r.count %></td><td><%= r.cName %></td><td><i class="fa fa-phone"></i></td><td><i class="fa fa-envelope-o"></i></td>¥
</tr>¥
<% }); %>'
var oppStr = '<% records.forEach(function (r) { %>¥
<tr>¥
<td>No.<%= r.count %></td><td><%= r.oName %></td>¥
</tr>¥
<% }); %>'
function toArray(fakeArray) {
return Array.prototype.slice.call(fakeArray);
}
$(function () {
var template;
if(record.Contacts){
var cRecords = Array.apply(null, new Array(record.Contacts.length)).map(function (n, i) {
return {
count: i+1,
cName: record.Contacts[i].Name
};
});
template = _.template(conStr);
document.getElementsByClassName('conRelatedListBody')[0].innerHTML = template({records: cRecords});
}
if(record.Opportunities){
var oRecords = Array.apply(null, new Array(record.Opportunities.length)).map(function (n, i) {
return {
count: i+1,
oName: record.Opportunities[i].Name
};
});
template = _.template(oppStr);
document.getElementsByClassName('oppRelatedListBody')[0].innerHTML = template({records: oRecords});
}
});
}
})
【AccountSearchStyle.css】
.THIS .section {
padding: 3px;
border: solid 1px #ccc;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
margin: 5px;
}
.THIS .sectionTitile{
background: #717ECD;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
padding: 5px;
color: #fff;
}
.THIS .searchCondition {
padding: 5px;
}
.THIS #selectInputItem{
height: 30px;
min-width: 10em;
border:solid 1px #ccc;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
margin-left: 5px;
}
.THIS #selectSearchItem{
height: 30px;
min-width: 10em;
border:solid 1px #ccc;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
margin-left: 5px;
}
.THIS .searchItem {
display: inline-block;
margin: 5px 10px;
}
.THIS .searchItemLabel{
display: inline-block;
text-align: left;
}
.THIS .searchButtonBox{
display: inline-block;
}
.THIS .searchButton{
height: 30px;
}
.THIS .Name {
font-size: 1.2em;
font-weight: bold;
margin-bottom: 5px;
}
.THIS .wrap {
position: relative;
}
.THIS .recordList {
padding: 10px;
background: #fff;
border:1px solid rgb(199, 199, 199);
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
margin-top:10px;
box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
-webkit-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
-moz-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
}
.THIS .mapframe {
position: absolute;
right: 0;
top: 0;
}
.THIS .recordDetail {
padding: 10px;
background: #fff;
border:1px solid rgb(199, 199, 199);
margin-top: 3px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
-webkit-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
-moz-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px;
}
/* Contact Table */
.THIS .conRelatedList {
margin:10px 0;
}
.THIS .conRelatedList table {
border-collapse: separate;
border-spacing: 1px;
}
.THIS .conRelatedList table thead tr {
background: #56458c;
color: #fff;
}
.THIS .conRelatedList table thead tr th {
padding: 5px;
}
.THIS .conRelatedList table tbody tr td {
padding: 5px;
border-bottom: solid 1px #ddd;
}
/* Opportunity Table */
.THIS .oppRelatedList {
margin:10px 0;
}
.THIS .oppRelatedList table {
border-collapse: separate;
border-spacing: 1px;
}
.THIS .oppRelatedList table thead tr {
background: #F3AE4E;
}
.THIS .oppRelatedList table thead tr th {
padding: 5px;
}
.THIS .oppRelatedList table tbody tr td {
padding: 5px;
border-bottom: solid 1px #ddd;
}
JavaScript CSSApex
検索ボタンを実行すると、
検索結果リストを表示
新サービス「SuPICE」
Copyright © TerraSky Co., Ltd. All Rights Reserved. 17
Lightning Component をノンコーディングで作成できる
世界初のクラウドサービス
Unit
Property
Deploy
来年2月
発売
予定
1. マウス操作で簡単に作成
Copyright © TerraSky Co., Ltd. All Rights Reserved. 18
「SuPICE」で何ができる?
Copyright © TerraSky Co., Ltd. All Rights Reserved. 19
2. プレビューでデザイン、動作を確認
Copyright © TerraSky Co., Ltd. All Rights Reserved. 20
3. Lightningのデザインに準拠
3つのSampleコンポーネントもSuPICEで作れます
Copyright © TerraSky Co., Ltd. All Rights Reserved. 21
アコーディオン リスト スワイプ リスト 検索画面
現在地
Camp Quick Start
I-1
T-3
Kids Coding
カウンター
IoTPartner
• ブースでデモをご覧になれますので、お立ち寄りください
ノンプログラミングで
Lightning Component
を作れる

More Related Content

PDF
Aplicacion turbogenerador java
PDF
Proyecto Final Android-SQLite
PDF
1- Sourcecode Array
PDF
Here's the Downtown Sound lineup for 2015
PDF
Jak zabít několik much jednou ranou přechodem na fragmenty
PDF
Aller plus loin avec Doctrine2
PDF
Palestra PythonBrasil[8]
DOCX
Hacer una calculadora en Java y en Visual Basic
Aplicacion turbogenerador java
Proyecto Final Android-SQLite
1- Sourcecode Array
Here's the Downtown Sound lineup for 2015
Jak zabít několik much jednou ranou přechodem na fragmenty
Aller plus loin avec Doctrine2
Palestra PythonBrasil[8]
Hacer una calculadora en Java y en Visual Basic

What's hot (20)

PDF
JavaScript Avanzado
PDF
jQuery sans jQuery
PDF
Get more votes!
PPTX
Testgetriebene Entwicklung mit JavaScript – Jax 2012
PDF
Android Fast Track CRUD Android PHP MySql
PDF
Get more votes!
PDF
2015 Key Ingredient Cook-Off
PDF
Skaters and BMXers from all over the U.S. descend on Grant Park
PDF
Share file to dropbox in android example
PDF
A slew of AACM 50th anniversary celebrations this weekend
PDF
FP is coming... le 19/05/2016
PDF
Estandarizacion de macros
PDF
Assalamualaykum warahmatullahi wabarakatuu
PDF
Migrating Legacy Web Applications to AngularJS
PDF
Chief Keef's hologram can't catch a break, and it's a win for Keef
PDF
Peek inside the fantastical Ukrainian Village home and studio of artists Jare...
DOCX
Propuesta..sujei
PDF
Developing an Android REST client to determine POI using asynctask and integr...
PDF
SSP - The Simple Singleton Pattern
PDF
JavaScript Avanzado
jQuery sans jQuery
Get more votes!
Testgetriebene Entwicklung mit JavaScript – Jax 2012
Android Fast Track CRUD Android PHP MySql
Get more votes!
2015 Key Ingredient Cook-Off
Skaters and BMXers from all over the U.S. descend on Grant Park
Share file to dropbox in android example
A slew of AACM 50th anniversary celebrations this weekend
FP is coming... le 19/05/2016
Estandarizacion de macros
Assalamualaykum warahmatullahi wabarakatuu
Migrating Legacy Web Applications to AngularJS
Chief Keef's hologram can't catch a break, and it's a win for Keef
Peek inside the fantastical Ukrainian Village home and studio of artists Jare...
Propuesta..sujei
Developing an Android REST client to determine POI using asynctask and integr...
SSP - The Simple Singleton Pattern
Ad

Viewers also liked (20)

PDF
Salesforce Lightning をやってみてあれこれ
PDF
Lightningコンポーネント事始め
PDF
Lightning Component公開への道 ~「Multi-View Calendar」開発で分かったこと~
PDF
Lightning開発のポイント
PDF
Lightning Process Builder で ビジネス・プロセスを自動化
PDF
Go Faster with Lightning : Salesforce Lightning 概要Webinar
PPTX
Lightingコンポーネントベーシック開発
PPTX
[TerraSky Day 2016] エンジニアのためになる!実用的なSalesforceページデザインについて考えてみよう
PPTX
Salesforce Lightning workshop
PDF
JSON Schema in Web Frontend #insideFE
PPT
La conducta assertiva
PPTX
Employee of the month
PDF
Professional Pilot November 2016
PPT
Projeto Integrado de Aprendizagem
PDF
Sisältömarkkinointi on verkkokauppiaan salainen ase
PPTX
Why a yorkie is right for me
PPT
Gempa bumi ok
PPT
Miguel De Cervantes Y D. Quijote
PDF
Unidad 0 3º de ESO
PDF
Ch6project analysis
Salesforce Lightning をやってみてあれこれ
Lightningコンポーネント事始め
Lightning Component公開への道 ~「Multi-View Calendar」開発で分かったこと~
Lightning開発のポイント
Lightning Process Builder で ビジネス・プロセスを自動化
Go Faster with Lightning : Salesforce Lightning 概要Webinar
Lightingコンポーネントベーシック開発
[TerraSky Day 2016] エンジニアのためになる!実用的なSalesforceページデザインについて考えてみよう
Salesforce Lightning workshop
JSON Schema in Web Frontend #insideFE
La conducta assertiva
Employee of the month
Professional Pilot November 2016
Projeto Integrado de Aprendizagem
Sisältömarkkinointi on verkkokauppiaan salainen ase
Why a yorkie is right for me
Gempa bumi ok
Miguel De Cervantes Y D. Quijote
Unidad 0 3º de ESO
Ch6project analysis
Ad

More from 寛 吉田 (9)

PDF
VisualforceへのSalesforce Lightning Design System (SLDS) 適用はどうします?
PPTX
SWTT2017 - Lightningアプリケーション開発現場で 実践しているノウハウ
PDF
Do you known issues ? - Practical use of known issue -
PPTX
Clickjack protection for salesforce
PPTX
The mash-up choice
PPTX
Performance of visualforce lt version-20121031
PPTX
Performance of Visualforce
PPTX
SkyVisualEditorで使ってるコードちょっとだけ教えます-PDFでの文字折返し-
PPTX
AWS to Salesforce 活用例 ~SSL Termination試してみました~
VisualforceへのSalesforce Lightning Design System (SLDS) 適用はどうします?
SWTT2017 - Lightningアプリケーション開発現場で 実践しているノウハウ
Do you known issues ? - Practical use of known issue -
Clickjack protection for salesforce
The mash-up choice
Performance of visualforce lt version-20121031
Performance of Visualforce
SkyVisualEditorで使ってるコードちょっとだけ教えます-PDFでの文字折返し-
AWS to Salesforce 活用例 ~SSL Termination試してみました~

聞いてスッキリ!Lightningの理解ポイント

  • 2. 会社紹介:概要 Copyright © 2015 Terrasky Co., Ltd. All Rights Reserved. 2 社 名 : 株式会社テラスカイ 所在地 : 〒103-0027 東京都中央区日本橋1-3-13 東京建物日本橋ビル7階 事業所 : 大阪、名古屋 設 立 : 平成18(2006)年4月 資本金 : 4億5403.5万円 代表者 : 代表取締役社長 佐藤 秀哉 情報管理: ISO 27001/IS 561777 URL : http://www.terrasky.co.jp/
  • 3. 会社紹介:多くの専門技術者 Copyright © TerraSky Co., Ltd. All Rights Reserved. 3 2015年12月1日時点 AWS認定ソリューションアーキテクト 認定SalesCloud コンサルタント 認定ServiceCloud コンサルタント 認定上級 デベロッパー 認定上級 アドミニストレーター Force.com MVP 認定テクニカルアーキテクト
  • 4. みずほキャピタル 様 富士通ゼネラル 様 損保ジャパン日本興亜システムズ様 エフエーサービス様 昭和シェル石油 様 ダンアンドブラッドストリート 様 小田急電鉄 様 デジタルハリウッド 様 小田急バス 様 パソナグループ 様 アリスタ ライフサイエンス 様 リンクイベントプロデュース 様 日立アロカメディカル 様 富士通ラーニングメディア 様 楽天 様 世界自然保護基金ジャパン 様 KVH 様 その他 多数 会社紹介:卓越したクラウド導入実績 Copyright © TerraSky Co., Ltd. All Rights Reserved. 4 Salesforce導入実績 2,000案件超! 金融からコールセンター、 サービス業まで業種業態を問わず 豊富な導入実績 金融 ・ 流通 医療 ・ IT 製造 ・ サービス 不動産 ・ 教育 非営利
  • 6. Copyright © TerraSky Co., Ltd. All Rights Reserved. 6 Lightning とは (出所:Youtube Dreamforce Keynoteより画面を引用)
  • 7. 7 Lightning とは、Force.com の新しいプラットフォームサービス群 Lightning コンポーネント Lightning App Builder Lightning Schema Builder Lightning Connect Salesforce Connect Lightning Process Builder Lightning Community Builder (出所:salesforce.com社App Cloudサイトより画像を引用)
  • 8. 8 Lightning とは、Force.com の新しいプラットフォームサービス群 Lightning コンポーネント Lightning App Builder Lightning Schema Builder Lightning Connect Salesforce Connect Lightning Process Builder Lightning Community Builder (出所:salesforce.com社App Cloudサイトより画像を引用) 今回のお話の範囲
  • 9. Visualforce から Lightning へ Copyright © TerraSky Co., Ltd. All Rights Reserved. 9 ⁃ 新しいUIへ ⁃ 「ページ」から『コンポーネント』へ
  • 10. Lightningページに 配置 Lightning の 画面開発 Copyright © TerraSky Co., Ltd. All Rights Reserved. 10 コンポーネントを 入手/開発 (出所:salesforce.com社資料「Building Lightning Components for ISVs」,Blog「Welcome to the Future of CRM. Welcome to Salesforce Lightning.」より図を引用) デスクトップ、各デバイスで 利用
  • 11. 今までの利用者の作業範囲今までの開発者の作業範囲 これからの開発者の作業範囲 これからの利用者の作業範囲 Lightning の 画面開発 Copyright © TerraSky Co., Ltd. All Rights Reserved. 11 (出所:salesforce.com社資料「Building Lightning Components for ISVs」,Blog「Welcome to the Future of CRM. Welcome to Salesforce Lightning.」より図を引用) ⁃ 利用者の作業可能範囲が広がる ⁃ (イメージとして)開発者に求める作業期間が短くなる より速い開発が必要
  • 12. 開発言語・使用技術 の 変化 Copyright © TerraSky Co., Ltd. All Rights Reserved. 12 ⁃ 業務ロジックの実装はApexからLightning側に(と言われている) ⁃ JavaScript、CSSが多くなる
  • 13. Copyright © TerraSky Co., Ltd. All Rights Reserved. 13 どんなコンポーネントが作れる? Lightningで
  • 14. Sample 1 : アコーディオンリスト Copyright © TerraSky Co., Ltd. All Rights Reserved. 14 【AccordionList.cmp】 <aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" controller="AccountListController" > <ltng:require styles="/resource/TS_AccordionList/TS_AccordionList/css/font-awesome.min.css" scripts="/resource/TS_AccordionList/TS_AccordionList/js/jquery.min.js, /resource/TS_AccordionList/TS_AccordionList/js/jquery-ui.min.js, /resource/TS_AccordionList/TS_AccordionList/js/lodash.js" afterScriptsLoaded="{!c.afterScriptsLoaded}" /> <div class="sg-icon-art sg-icn--fnt center tc icon-utility-download" title="download" /> <!-- AccordionList --> <div class="TS_component"> <h2 class="component_Title_header"> <div class="icon_Frame account_Color"><img src="/img/icon/t4v32/standard/account_120.png" class="icon " alt="Account" title="Account" / <span class="component_Title">Account Lists</span> </h2> <div class="component_Body"> <table id="AccordionListTable" ></table> </div> </div> </aura:component> 【AccordionListController.js】 ({ afterScriptsLoaded : function(component, event, helper) { var expanded; $(function () { /* ここから */ var action = component.get("c.findAll"); var records; action.setCallback(this, function(response){ var state = response.getState(); if (state === "SUCCESS") { var res = response.getReturnValue(); records = Array.apply(null, new Array(res.length)).map(function (n, i) { return { Name: res[i].Name, State:res[i].BillingState, Phone: res[i].Phone, Fax: res[i].Fax, Address: res[i].BillingState + res[i].BillingCity + res[i].BillingStreet }; }); } var template = _.template('¥ <% records.forEach(function (r) { %>¥ <tr class="row">¥ <td>¥ <div class="form-control">¥ <span class="accName"><%= r.Name %></span><span class="accState"><%= r.State %></span>¥ </div>¥ <div class="content">¥ <div class="fields">¥ <span class="content_Icon"><i class="fa fa-phone"></i></span>¥ <span class="content_Value"><%= r.Phone %></span>¥ </div>¥ <div class="fields">¥ <span class="content_Icon"><i class="fa fa-fax"></i></span>¥ <span class="content_Value"><%= r.Fax %></span>¥ </div>¥ <div class="fields">¥ <span class="icon_Address"><i class="fa fa-building-o"></i></span>¥ <span class="content_Address"><%= r.Address %></span>¥ </div>¥ </div>¥ </td>¥ </tr>¥ <% }); %>¥ '); document.getElementById('AccordionListTable').innerHTML = template({records: records}); $('.row').click(function () { if (expanded) { $(expanded).find('.content').slideUp(300); $(this).find('.accName').removeClass('textBold'); } if (expanded === this) { expanded = null; return; } expanded = this; var content = $(this).find('.content'); content.hide(); content.slideDown(300); $('.accName').removeClass('textBold'); $(this).find('.accName').addClass('textBold'); }); }); $A.enqueueAction(action); }); } }) 【AccordionListStyle.css】 /* Component Header */ .THIS .TS_component { margin-left: 10px; } .THIS .component_Title_header{ margin: 5px; } .THIS .icon_Frame { display: inline-block; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; } .THIS .account_Color { background: #7F8DE1; } .THIS .icon_Frame .icon { width: 2rem; height: 2rem; vertical-align: middle; } .THIS .component_Title { margin-left:10px; } /* AccordionList */ .THIS .component_Body{ width: 98%; margin: auto; border:solid 1px #ccc; background: #fff; padding: 10px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; } .THIS #AccordionListTable { margin:0 auto; width: 100%; } .THIS #AccordionListTable td { padding:10px; border-top: solid 1px #ddd; } .THIS .form-control{ padding:5px; color:#2A94D6; } .THIS .content { display: none; border: solid 1px #ccc; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; background:#F0F1F2; background:#fff; padding:5px; } .THIS .fields { margin-bottom: 5px; } /* AccordionList Label */ .THIS .accName{ display: inline-block; width:60%; } .THIS .accState{ display: inline-block; text-align:right; width:40%; } /* AccordionList content */ .THIS .content_Icon{ width: 30px; text-align: center; display: inline-block; } .THIS .content_Value{ display: inline-block; width: auto; } /* content Addess only */ .THIS .icon_Address{ width: 30px; text-align: center; display: inline-block; float: left; } .THIS .content_Address{ display: block; width: auto; margin-left: 30px; } .THIS .textBold { font-weight:bold; color:#344A5F; } 【SuPICE.cls】 interface Action { Object run(Object data); } virtual public class ActionResult { @AuraEnabled public final Boolean error { get; private set; } @AuraEnabled public final String[] messages { get; private set; } @AuraEnabled public final Object value { get; private set; } public ActionResult(String[] messages, Object value) { this.error = messages != null; this.messages = messages; this.value = value; } public ActionResult(Boolean success, String[] messages, Object value) { this.error = success; this.messages = messages; this.value = value; } } @AuraEnabled global static Object action(String data) { try { Map<String, Object> dataMap = (Map<String, Object>) JSON.deserializeUntyped(data); String type = (String) dataMap.get('action'); Object action = ACTION_MAP.get(type); if (action == null) { throw new SuPICEException('Unsupported action type: ' + type); } Object result = ((Action) action).run(dataMap.get('data')); return result; } catch (SuPICEException e) { // SuPICE処理内で予測される例外 return new ActionResult(new String[] {e.getMessage()}, null); } catch (Exception e) { // 予期しない例外。とりあえずStackTraceを返す return new ActionResult(new String[] { ERROR.getStackTrace(e) }, null); } } private static final Map<String, Action> ACTION_MAP = new Map<String, Action> { 'query' => new QueryAction(), 'describe' => new DescribeAction(), 'save' => new SaveAction(), 'delete' => new DeleteAction() }; class QueryActionArguments { public String objectName { get; private set; } public String[] fields { get; private set; } public ConditionOperator condition { get; private set; } public QueryActionArguments(Object root) { Map<String, Object> rootMap = (Map<String, Object>) root; objectName = getObject(rootMap); fields = getFields(rootMap); Set<String> fieldSet = new Set<String>(); fieldSet.add('Id'); //Idは必ず取得する fieldSet.addAll(getFields(rootMap)); fields = new String[0]; fields.addAll(fieldSet); Map<String, Object> untypedCondition = getCondition(rootMap); if (untypedCondition != null) { condition = parseOperator(untypedCondition); } } String getObject(Map<String, Object> untyped) { return (String) untyped.get('object'); } String[] getFields(Map<String, Object> untyped) { String[] typed = new String[0]; List<Object> fields = (List<Object>) untyped.get('fields'); for (Object o : fields) { typed.add((String) o); } return typed; } Map<String, Object> getCondition(Map<String, Object> untyped) { Object condition = untyped.get('condition'); if (condition == null) { return null; } else { return (Map<String, Object>) condition; } } } class DescribeAction implements Action { public ActionResult run(Object untyped) { String objectName = (String)((Map<String, Object>) untyped).get('object'); SObjectType soType = Schema.getGlobalDescribe().get(objectName); if (soType == null) { throw ERROR.getSObjectNotFound(objectName); } DescribeSObjectResult describe = soType.getDescribe(); return new ActionResult(null, new DescribeResult(describe)); } } リストをクリック(or タップ)を すると、詳細データが表示 JavaScript CSSApex
  • 15. Sample 2 : ToDo の スワイプリスト Copyright © TerraSky Co., Ltd. All Rights Reserved. 15 【AccordionList.cmp】 <aura:component controller="ToDoListController" implements="force:appHostable,flexipage:availableForAllPageTypes"> <aura:attribute name="toDos" type="Task[]" /> <ltng:require styles=" /resource/TS_ToDoList/Font-Awesome-master/css/font-awesome.min.css, /resource/TS_ToDoList/Swiper-master/dist/css/swiper.min.css" scripts=" /resource/TS_ToDoList/jquery-1.11.3.min.js, /resource/TS_ToDoList/lodash.min.js, /resource/TS_ToDoList/Swiper-master/dist/js/swiper.min.js, /resource/TS_ToDoList/dateformat.js" afterScriptsLoaded="{!c.afterScript}"/> <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> <!-- Todo List --> <div class="TS_component"> <h2 class="component_Title_header"> <div class="icon_Frame todo_Color"><img src="/img/icon/t4v32/standard/task_120.png" class="icon" alt="ToDo" title="ToDo" /></div> <span class="component_Title">ToDo List</span> </h2> <div class="todo_List" id="todo_List"> </div> </div> </aura:component> 【ToDoListController.js】 ({ doInit : function(component, event, helper) { var action = component.get('c.getToDos'); action.setCallback(this,function(response){ component.set('v.toDos',response.getReturnValue()); }); $A.enqueueAction(action); }, showSpinner : function (component, event, helper) { var spinner = component.find('todo_spinner'); var evt = spinner.get("e.toggle"); evt.setParams({ isVisible : true }); evt.fire(); }, hideSpinner : function (component, event, helper) { var spinner = component.find('todo_spinner'); var evt = spinner.get("e.toggle"); evt.setParams({ isVisible : false }); evt.fire(); }, afterScript : function(component, event, helper) { var action = component.get('c.getToDos'); action.setCallback(this,function(response){ var str = '<% records.forEach(function (r) { %>¥ <div class="swiper-container">¥ <div class="done"><i class="fa fa-check-square-o"></i><br />Done </div>¥ <div class="delete"><i class="fa fa-trash-o"></i><br />Del </div>¥ <div class="swiper-wrapper <%= r.style %>">¥ <div class="mark_l">¥ <i class="fa fa-caret-left"></i>¥ <i class="fa fa-hand-pointer-o"></i>Done¥ </div>¥ <div class="mark_r">¥ Delete <i class="fa fa-hand-pointer-o"></i>¥ <i class="fa fa-caret-right"></i>¥ </div>¥ <div class="swiper-slide" id="<%= r.Id %>">¥ <div class="subject"><%= r.Name %></div>¥ <span class="details"><%= r.Status %></span>¥ <span class="details"><%= r.ActivityDate %></span>¥ </div>¥ </div>¥ </div>¥ <% }); %>'; helper.setToDoList(component,str,response.getReturnValue(),event); helper.doSwiper(component); }); $A.enqueueAction(action); }, }) 【ToDoListHelper.js】 ({ setToDoList:function(component, str, record){ var expanded; var self = this; function toArray(fakeArray) { return Array.prototype.slice.call(fakeArray); } $(function () { var records = Array.apply(null, new Array(record.length)).map(function (n, i) { /* 日付フォーマット M/d/yyyy */ var dateFormat = new DateFormat("M/d/yyyy"); var str = dateFormat.format(new Date(record[i].ActivityDate)); /** 期限を確認する **/ var today = new Date(); var date = new Date(record[i].ActivityDate); var style = ''; if (today > date) { style = 'expired'; } return { Name: record[i].Subject, Status: record[i].Status, IsClosed: record[i].IsClosed, ActivityDate: str, Id: record[i].Id, style: style }; }); var template = _.template(str); document.getElementById('todo_List').innerHTML = template({records: records}); }); }, doSaveToDo: function(component, recordId){ // var upsertToDo = {'sobjectType':'Task','Id':recordId,'Status':'Completed'}; var upsertToDo = {'sobjectType':'Task','Id':recordId}; var action = component.get("c.saveToDo"); action.setParams({"tasks":upsertToDo}); action.setCallback(this,function(a){ console.log("FIN!!"); }); console.log("GO!!"); $A.enqueueAction(action); }, doSwiper: function(component){ var self = this; var mySwiper = new Swiper('.swiper-container',{ pagination: '.pagination', loop:false, paginationClickable:true, calculateHeight:true, touchRatio:0.6, onTransitionStart: function (swiper){ var recordId = swiper.wrapper[0].id; if(recordId){ self.doSaveToDo(component, recordId); } }, onTransitionEnd: function(swiper){ if(swiper.touches.diff <= -170){ $(swiper.container[0]).css('display','none'); } else if (swiper.touches.diff >= 170){ /* $(swiper.wrapper[0]).css({'background':'#D3D3D3','border-color':'#c7c7c7'}); */ $(swiper.wrapper[0]).addClass('todoDone'); /* $(swiper.wrapper[0]).find('.subject').addClass('todoDone'); */ } } }); } }) 【ToDoListStyle.css】 /* Component Header */ .THIS .component_Title_header{ margin: 5px; } .THIS .icon_Frame { display: inline-block; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; } .THIS .todo_Color { background: #4BC076; } .THIS .icon_Frame .icon { width: 2rem; height: 2rem; vertical-align: middle; } .THIS .component_Title { margin-left:10px; } /* Records */ .THIS .swiper-wrapper { height: 100%; padding: 10px; /* background: #8BC34A; */ background: #C8E6C9; background: #8BC34A; background: #fff; border:1px solid #388E3C; margin: 3px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; -webkit-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; -moz-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; width:auto; } .THIS .mark_l i{ margin-right: 2px; } .THIS .mark_l { position: absolute; top: 0; left: 0; margin-left: 5px; margin-top: 5px; color: #fff; color: #616161; } .THIS .mark_r { position: absolute; top: 0; right: 0; margin-right: 5px; margin-top: 5px; color: #fff; color: #616161; } .THIS .swiper-slide { margin-top:20px; width:100% !important; border-left: solid 5px #388E3C; padding-left: 5px; } .THIS .expired { border:1px solid #D32F2F; /* background: #eb4654; */ background: #FFCDD2; background: #FF5252; background: #eb4654; background: #fff; } .THIS .expired .swiper-slide { margin-top:20px; width:100% !important; border-left: solid 5px #D32F2F; padding-left: 5px; } /* swipe時に表示される */ .THIS .done { position: absolute; top: 5px; padding: 10px; vertical-align: middle; background: #4AB471; color: #fff; margin-top: 4px; margin-left: 3px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; width:50%; } .THIS .delete { position: absolute; top: 5px; right: 0; padding: 10px; text-align: right; background: #D96383; color: #fff; margin-top: 4px; margin-right: 3px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; width:48% } .THIS .subject { font-size: 1.2em; font-weight: bold; margin-bottom: 5px; } .THIS .todoDone { background: #D3D3D3; border-color: #c7c7c7; } .THIS .todoDone .swiper-slide { border-left: solid 5px #fff; } .THIS .details { display: inline-block; color: #fff; width: 48%; color: #616161; } JavaScript CSSApex リストをスワイプをすると、 データ編集、削除等の アクションを実行可能
  • 16. Sample 3 : 取引先の検索画面 Copyright © TerraSky Co., Ltd. All Rights Reserved. 16 【AccountSearch.cmp】 <aura:component controller="AccountSearchController" implements="force:appHostable,flexipage:availableForAllPageTypes"> <ltng:require scripts="/resource/TS_AccountSearch/jquery-1.11.3.min.js, /resource/TS_AccountSearch/lodash.min.js" /> <aura:handler name="init" value="{!this}" action="{!c.doInit}" /> <div class="condition"> <!-- SearchBox Start --> <div class="section"> <div class="sectionTitile">Search Box</div> <div class="searchCondition"> <div class="searchItem"> <span class="searchItemLabel"></span> <input type="text" id="selectInputItem" palaceholder="Account Name"/> </div> <div class="searchItem"> <span class="searchItemLabel"></span> <select id="selectSearchItem"> <option value="">--None--</option> </select> </div> <div class="searchButtonBox"> <input class="searchButton" type="button" value="Search" onclick="{!c.doSearch}"/> </div> </div> </div> <!-- Results Start --> <div class="section"> <div class="sectionTitile">Search Results</div> <div class="searchResult"></div> </div> </div> <!-- detail Start --> <div class="detail" style="display:none"> <div class="section"> <div id="TS_DetailSection" tabindex="0" class="sectionTitile">Detail</div> <div class="detailList"></div> <input type="button" value="Close" onclick="{!c.closeDetail}"/> </div> </div> </aura:component> 【AccountSearchController.js】 ({ doInit : function(component, event, helper){ var action = component.get('c.getOptions'); action.setCallback(this,function(response){ var sel = document.getElementById('selectSearchItem'); for(var i =0; i<response.getReturnValue().length; i++){ var val = response.getReturnValue()[i].BillingState; if(val){ var op = document.createElement('option'); op.setAttribute('value',val); op.innerHTML = val; sel.appendChild(op); } } }); $A.enqueueAction(action); }, doSearch : function(component, event, helper) { var accName = document.getElementById('selectInputItem').value; var accState = document.getElementById('selectSearchItem').value; var action; if(accName !="" && accState == ""){ action = component.get('c.getAccountName'); }else if (accName == "" && accState != ""){ action = component.get('c.getAccountState'); }else{ action = component.get('c.getAccounts'); } action.setParams({ 'accName':accName, 'accState':accState }); action.setCallback(this,function(response){ var str = '<% records.forEach(function (r) { %>¥ <div class="wrap">¥ <div class="mapframe">¥ <img class="map" src="/resource/mapicon/mapicon/icon_1r_64.png" address="<%= r.Street %>" />¥ </div>¥ <div id="<%= r.Id %>" class="recordList">¥ <div class="Name"><%= r.Name %></div>¥ <div>BillingAddress <br/>¥ Country : <%= r.Country %><br/>¥ PostalCode : <%= r.PostalCode %><br/>¥ State : <%= r.State %><br/>¥ City : <%= r.City %><br/>¥ Street : <%= r.Street %>¥ </div>¥ <div>Phone : <%= r.Phone %></div>¥ </div>¥ </div>¥ <% }); %>'; helper.setResult(component,str,response.getReturnValue(),event); }); $A.enqueueAction(action); }, closeDetail: function(){ // $('.detail').fadeOut('normal'); $('.detail').hide(); $('.condition').fadeIn('normal'); }, navigate : function() { }, }) 【AccountSearchHelper.js】 ({ setResult: function(cmp,str,record,event) { var self = this; function toArray(fakeArray) { return Array.prototype.slice.call(fakeArray); } $(function () { var records = Array.apply(null, new Array(record.length)).map(function (n, i) { var address = encodeURIComponent(record[i].BillingStreet); return { Id: record[i].Id, Name: record[i].Name, Country: record[i].BillingCountry, PostalCode: record[i].BillingPostalCode, State: record[i].BillingState, City: record[i].BillingCity, Street: record[i].BillingStreet, Phone: record[i].Phone, Address: address }; }); var template = _.template(str); document.getElementsByClassName('searchResult')[0].innerHTML = template({records: records}); }); /* 詳細表示用 */ $('.recordList').click(function (ev) { self.setDetailList(cmp,record,ev); $('.detail').fadeIn('normal'); $('.condition').hide(); }); /* 地図表示用 */ $('.map').click(function (e) { var address = encodeURIComponent($(e.target).attr('address')); var urlEvent = $A.get("e.force:navigateToURL"); urlEvent.setParams({ "url": 'https://www.google.com/maps/place/' + address }); urlEvent.fire(); }); }, setDetailList: function(cmp,record,ev){ var self = this; var recordId = ev.currentTarget.id; var str = '<% records.forEach(function (r) { %>¥ <div class="recordDetail">¥ <div class="Name"><%= r.Name %></div>¥ <div>BillingAddress <br/>¥ Country : <%= r.Country %><br/>¥ PostalCode : <%= r.PostalCode %><br/>¥ State : <%= r.State %><br/>¥ City : <%= r.City %><br/>¥ Street : <%= r.Street %>¥ </div>¥ <div>Phone : <%= r.Phone %></div>¥ <div class="conRelatedList">¥ <table>¥ <thead>¥ <tr>¥ <th>No.</th><th>Contact Name</th>¥ </tr>¥ </thead>¥ <tbody class="conRelatedListBody">¥ </tbody>¥ </table>¥ </div>¥ <div class="oppRelatedList">¥ <table>¥ <thead>¥ <tr>¥ <th>No.</th><th>Opportunity Name</th>¥ </tr>¥ </thead>¥ <tbody class="oppRelatedListBody">¥ </tbody >¥ </table>¥ </div>¥ </div><br/>¥ <% }); %>' function toArray(fakeArray) { return Array.prototype.slice.call(fakeArray); } $(function () { var records = Array.apply(null, new Array(record.length)).map(function (n, i) { return { Id: record[i].Id, Name: record[i].Name, Country: record[i].BillingCountry, PostalCode: record[i].BillingPostalCode, State: record[i].BillingState, City: record[i].BillingCity, Street: record[i].BillingStreet, Phone: record[i].Phone, Contacts: record[i].Contacts, Opportunities: record[i].Opportunities }; }); var template = _.template(str); for(var i=0; i<records.length; i++){ if(records[i].Id == recordId){ document.getElementsByClassName('detailList')[0].innerHTML = template({records: [records[i]]}); self.setRelatedList(records[i]); } } }); }, setRelatedList: function(record){ var conStr = '<% records.forEach(function (r) { %>¥ <tr>¥ <td>No.<%= r.count %></td><td><%= r.cName %></td><td><i class="fa fa-phone"></i></td><td><i class="fa fa-envelope-o"></i></td>¥ </tr>¥ <% }); %>' var oppStr = '<% records.forEach(function (r) { %>¥ <tr>¥ <td>No.<%= r.count %></td><td><%= r.oName %></td>¥ </tr>¥ <% }); %>' function toArray(fakeArray) { return Array.prototype.slice.call(fakeArray); } $(function () { var template; if(record.Contacts){ var cRecords = Array.apply(null, new Array(record.Contacts.length)).map(function (n, i) { return { count: i+1, cName: record.Contacts[i].Name }; }); template = _.template(conStr); document.getElementsByClassName('conRelatedListBody')[0].innerHTML = template({records: cRecords}); } if(record.Opportunities){ var oRecords = Array.apply(null, new Array(record.Opportunities.length)).map(function (n, i) { return { count: i+1, oName: record.Opportunities[i].Name }; }); template = _.template(oppStr); document.getElementsByClassName('oppRelatedListBody')[0].innerHTML = template({records: oRecords}); } }); } }) 【AccountSearchStyle.css】 .THIS .section { padding: 3px; border: solid 1px #ccc; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; margin: 5px; } .THIS .sectionTitile{ background: #717ECD; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; padding: 5px; color: #fff; } .THIS .searchCondition { padding: 5px; } .THIS #selectInputItem{ height: 30px; min-width: 10em; border:solid 1px #ccc; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; margin-left: 5px; } .THIS #selectSearchItem{ height: 30px; min-width: 10em; border:solid 1px #ccc; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; margin-left: 5px; } .THIS .searchItem { display: inline-block; margin: 5px 10px; } .THIS .searchItemLabel{ display: inline-block; text-align: left; } .THIS .searchButtonBox{ display: inline-block; } .THIS .searchButton{ height: 30px; } .THIS .Name { font-size: 1.2em; font-weight: bold; margin-bottom: 5px; } .THIS .wrap { position: relative; } .THIS .recordList { padding: 10px; background: #fff; border:1px solid rgb(199, 199, 199); border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; margin-top:10px; box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; -webkit-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; -moz-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; } .THIS .mapframe { position: absolute; right: 0; top: 0; } .THIS .recordDetail { padding: 10px; background: #fff; border:1px solid rgb(199, 199, 199); margin-top: 3px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; -webkit-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; -moz-box-shadow:rgba(113, 135, 164, 0.65098) 4px 3px 6px -3px; } /* Contact Table */ .THIS .conRelatedList { margin:10px 0; } .THIS .conRelatedList table { border-collapse: separate; border-spacing: 1px; } .THIS .conRelatedList table thead tr { background: #56458c; color: #fff; } .THIS .conRelatedList table thead tr th { padding: 5px; } .THIS .conRelatedList table tbody tr td { padding: 5px; border-bottom: solid 1px #ddd; } /* Opportunity Table */ .THIS .oppRelatedList { margin:10px 0; } .THIS .oppRelatedList table { border-collapse: separate; border-spacing: 1px; } .THIS .oppRelatedList table thead tr { background: #F3AE4E; } .THIS .oppRelatedList table thead tr th { padding: 5px; } .THIS .oppRelatedList table tbody tr td { padding: 5px; border-bottom: solid 1px #ddd; } JavaScript CSSApex 検索ボタンを実行すると、 検索結果リストを表示
  • 17. 新サービス「SuPICE」 Copyright © TerraSky Co., Ltd. All Rights Reserved. 17 Lightning Component をノンコーディングで作成できる 世界初のクラウドサービス Unit Property Deploy 来年2月 発売 予定
  • 18. 1. マウス操作で簡単に作成 Copyright © TerraSky Co., Ltd. All Rights Reserved. 18 「SuPICE」で何ができる?
  • 19. Copyright © TerraSky Co., Ltd. All Rights Reserved. 19 2. プレビューでデザイン、動作を確認
  • 20. Copyright © TerraSky Co., Ltd. All Rights Reserved. 20 3. Lightningのデザインに準拠
  • 21. 3つのSampleコンポーネントもSuPICEで作れます Copyright © TerraSky Co., Ltd. All Rights Reserved. 21 アコーディオン リスト スワイプ リスト 検索画面
  • 22. 現在地 Camp Quick Start I-1 T-3 Kids Coding カウンター IoTPartner • ブースでデモをご覧になれますので、お立ち寄りください ノンプログラミングで Lightning Component を作れる