빡코

[개념] Aura Components Basic 본문

카테고리 없음

[개념] Aura Components Basic

chris.djang 2020. 2. 15. 14:24

What Is the Lightning Component Framework?

It’s a modern framework for building single-page applications with dynamic, responsive user interfaces for Lightning Platform apps. It uses JavaScript on the client side and Apex on the server side.

- single-page applications

 

  • <lightning:card> creates a container around a group of information.
  • <lightning:formattedDateTime> displays formatted date and time.
  • <lightning:relativeDateTime> displays the relative time difference between the current time and the provided time.
<aura:component>
    <aura:attribute name="expense" type="Expense__c"/>
    <aura:registerEvent name="updateExpense" type="c:expensesItemUpdate"/>
    <!-- Color the item green if the expense is reimbursed -->
    <lightning:card title="{!v.expense.Name}" iconName="standard:scan_card"
                    class="{!v.expense.Reimbursed__c ?
                           'slds-theme--success' : ''}">
        <aura:set attribute="footer">
            <p>Date: <lightning:formattedDateTime value="{!v.formatdate}"/></p>
            <p class="slds-text-title"><lightning:relativeDateTime value="{!v.formatdate}"/></p>
        </aura:set>
        <p class="slds-text-heading--medium slds-p-horizontal--small">
            Amount: <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
        </p>
        <p class="slds-p-horizontal--small">
           Client: {!v.expense.Client__c}
        </p>
        <p>
            <lightning:input type="toggle" 
                             label="Reimbursed?"
                             name="reimbursed"
                             class="slds-p-around--small"
                             checked="{!v.expense.Reimbursed__c}"
                             messageToggleActive="Yes"
                             messageToggleInactive="No"
                             onchange="{!c.clickReimbursed}"/>
        </p>
    </lightning:card>
</aura:component>

onchange = "{! c.clickReimbursed}" 클릭시 아래 Controller가 실행된다. 

({
    clickReimbursed: function(component, event, helper) {
        var expense = component.get("v.expense");
        var updateEvent = component.getEvent("updateExpense");
        updateEvent.setParams({ "expense": expense });
        updateEvent.fire();
    }
})

-a component is a bundle of code

-Visualforce를 통해서 container page 생성후 기존 JS 프레임워크를 사용할 수 있다.  (container page) 

 

component attribute

  1. A component attribute is the place where you can store a value. In the preceding example, the helloMessage component has a component attribute named message. Most of the time we’re talking about component attributes.
  2. You define a component attribute using the <aura:attribute> tag. We’ll see an example of that momentarily. Let’s call these attribute definitions.
  3. The <aura:attribute> tag itself takes attributes when you use it! 😖 That is, when you define a component attribute using the <aura:attribute> tag, you set attributes on <aura:attribute> that specify the “shape” of the component attribute you’re defining. 😡 Wait, let’s try again: add a component attribute definition by setting attributes on the attribute’s definition. 😭 A component attribute’s attribute definition takes attributes? 😴
//helloMessage 컴포넌트
<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>Hello! [ message goes here, soon ]</p>
</aura:component>

 

expression 을 표현하는 방법

{!<expression>}

<aura:component>
    <aura:attribute name="message" type="String"/>
    <p>{!'Hello! ' + v.message}</p>
</aura:component>
{!$Label.c.Greeting + v.message} //커스텀라벨의 name이 Greeting인 값을 불러온다. 
<aura:component>
    <aura:attribute name="customMessage" type="String"/> //attribute name을 customMessage로 하고, 타입은 Stringd이다.
    <p> <c:helloMessage message="{!v.customMessage}"/> </p> //message에다가 customMessage를 대입한다. 
</aura:component>

-v is something called a value provider. Value providers are a way to group, encapsulate, and access related data. 

-To use a value, separate the value provider and the property name with a dot (period). 

 

attribute types

  • Primitives data types, such as Boolean, Date, DateTime, Decimal, Double, Integer, Long, or String. The usual suspects in any programming language.
  • Standard and custom Salesforce objects, such as Account or MyCustomObject__c.
  • Collections, such as List, Map, and Set.
  • Custom Apex classes.
  • Framework-specific types, such as Aura.Component, or Aura.Component[]. These are more advanced than we’ll get to in this module, but you should know they exist.
a:component>
    <aura:attribute name="expense" type="Expense__c"/> //expense는 Custom Object
    <p>Amount:
        <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
    </p>
    <p>
        Client: {!v.expense.Client__c}
    </p>
    <p>
        <lightning:input type="toggle"                            
                         label="Reimbursed?"                           
                         name="reimbursed"                         
                         checked="{!v.expense.Reimbursed__c}" />
     </p> 
    <!-- Other markup here -->
</aura:component>

-component’s purpose is to display the details of an expense by referencing the field on the Expense__c record, using the {!v.expense.fieldName} expression.

 

  • The default attribute defines the default attribute value. It’s used when the attribute is referenced and you haven’t yet set the attribute’s value.
  • The required attribute defines whether the attribute is required. The default is false.
  • The description attribute defines a brief summary of the attribute and its usage

helloPlayground component (original link)

<aura:component>
    <aura:attribute name="messages" type="List" //하나의 messages라는 attribute를 가지고 있음 
        default="['You look nice today.', //3개의 데이터를 가진 리스트를 default로 가지고 있음
            'Great weather we\'re having.',
            'How are you?']"/>

    <h1>Hello Playground</h1>
    <p>Silly fun with attributes and expressions.</p>

    <h2>List Items</h2>
    <p><c:helloMessage message="{!v.messages[0]}"/></p> //그럼 helloMessage 메시지는 어디서 들어온거지? 
    <p><c:helloMessage message="{!v.messages[1]}"/></p> //밑에 helloMessage.cmp를 주입받는다. 
    <p><c:helloMessage message="{!v.messages[2]}"/></p>

    <h2>List Iteration</h2>
    <aura:iteration items="{!v.messages}" var="msg">
        <p><c:helloMessage message="{!msg}"/></p>
    </aura:iteration>

    <h2>Conditional Expressions and Global Value Providers</h2>
    <aura:if isTrue="{!$Browser.isIPhone}">
        <p><c:helloMessage message="{!v.messages[0]}"/></p>
    <aura:set attribute="else">
        <p><c:helloMessage message="{!v.messages[1]}"/></p>
        </aura:set>
    </aura:if>
</aura:component>

helloMessage.cmp

<aura:component >
    <aura:attribute name="message" type="String"/>
    <p>{!'Hello!'+ v.message}</p>
</aura:component>

harnessApp.app  //실제 실행되는 부분 

<aura:application >
    <c:helloWorld/>
    <c:helloPlayground/>
</aura:application>

출력값 

<Challenge>

Create a Packing List Item Component

Create a Lightning Component to display a single item for your packing list.

  • Create a component called campingListItem.
  • Add an attribute named item of type Camping_Item__c that is required.
  • Display Name, Price, Quantity, Packed using an expression.
  • Display Price and Quantity using the appropriate formatted number fields.
  • Display Packed status using a toggle.
<aura:component >
    <aura:attribute name="item" type="Camping_Item__c" required="true"/> 
    <p>Name:
        {!v.item.Name}
    </p>
    <p>Price:
        <lightning:formattedNumber value="{!v.item.Price__c}" style="currency" />
    </p>
    <p>Quantity:
        <lightning:formattedNumber value = "{!v.item.Quantity__c}" />
    </p>
    <p>
        <lightning:input type="toggle"                            
                         label="Packed"                           
                         name="packed"                         
                         checked="{!v.item.Packed__c}" />
    </p>
	
</aura:component>

Handle Actions with Controllers

-Lightning Components, a controller is a resource in a component bundle that holds the action handlers for that component. And action handlers are just JavaScript functions with a particular function signature.

-Lightning Components is View-Controller-Controller-Model, or perhaps View-Controller-Controller-Database.

-interacting with Salesforce, your components will have a server-side controller in addition to the client-side controller we’ve worked with in this unit. This dual controller design is the key difference between Lightning Components and MVC.

-in Lightning Components, there’s no Apex class that directly stands in between @AuraEnabled controller methods and DML operations. 

- sObjects are already an abstraction between your Apex code and the underlying storage layer.

 

QQ)sObjects는 도대체 무엇?

 

//helloMessageInteractive.cmp
<aura:component>
 
    <aura:attribute name="message" type="String"/>
 
    <p>Message of the day: {!v.message}</p>
 
    <div>
        <lightning:button label="You look nice today."
            onclick="{!c.handleClick}"/>
 
        <lightning:button label="Today is going to be a great day!"
            onclick="{!c.handleClick}"/>
    </div>
 
</aura:component>
//helloMessageInteractiveController.js
({
//액션핸들러이름:익명함수(helloMessageInteractive,액션 핸들러가 호출 된 이벤트,재사용함수)
    handleClick: function(component, event, helper) {
        var btnClicked = event.getSource();         // the button
        var btnMessage = btnClicked.get("v.label"); // the button's label(label도 하나의 attribute이다)
        component.set("v.message", btnMessage);     // update our message
    },
    handleClick2: function(component, event, helper) {
        var newMessage = event.getSource().get("v.label");
        console.log("handleClick2: Message: " + newMessage);
        component.set("v.message", newMessage);
    },
    handleClick3: function(component, event, helper) {
        component.set("v.message", event.getSource().get("v.label"));
    }
})

When the button is clicked, its action handler gets called (1). In the action handler, the controller gets the button that was clicked, pulls the label text out of it, and then sets the component’s message attribute to that text (2). And the message of the day is updated (3). You look nice today!

화면1
화면2
화면3

YOUR CHALLENGE

Mark Item as Packed

Add a button to the campingListItem component that when clicked, marks the item as packed.

  • Add a button labeled Packed! that calls the packItem controller function when clicked.
  • The controller function should do the following:
    • Mark the item attribute as packed using a value of true
    • Disable the button by marking the disabled attribute using a value of true

CampingListItem.cmp 

<aura:component >
    <div>
        <lightning:button label="Packed!" onclick="{!c.packItem}"/>
    </div>
     <aura:attribute name="item" type="Camping_Item__c" required="true"/> 
    <p>Name:
        {!v.item.Name}
    </p>
    <p>Price:
        <lightning:formattedNumber value="{!v.item.Price__c}" style="currency" />
    </p>
    <p>Quantity:
        <lightning:formattedNumber value = "{!v.item.Quantity__c}" />
    </p>
    <p>
        <lightning:input type="toggle"                            
                         label="Packed"                           
                         name="packed"                         
                         checked="{!v.item.Packed__c}" />
    </p>
</aura:component>

campingListItemController.js

({
	packItem : function(component, event, helper) {
       	component.set("v.item.Packed__c",true);
        let button = event.getSource(); 
        button.set('v.disabled',true);	
       
	}
})

Input Data Using Forms

expenses.cmp

<aura:component>
    
    <aura:attribute name="expenses" type="Expense__c[]"/>
    
     <aura:attribute name="newExpense" type="Expense__c"
         default="{ 'sobjectType': 'Expense__c',
                        'Name': '',
                        'Amount__c': 0,
                        'Client__c': '',
                        'Date__c': '',
                        'Reimbursed__c': false }"/>
 
    <!-- PAGE HEADER -->
    <lightning:layout class="slds-page-header slds-page-header--object-home">
        <lightning:layoutItem>
            <lightning:icon iconName="standard:scan_card" alternativeText="My Expenses"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading--label">Expenses</h1>
                <h2 class="slds-text-heading--medium">My Expenses</h2>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / PAGE HEADER -->
    <!-- NEW EXPENSE FORM -->
    <lightning:layout>
        <lightning:layoutItem padding="around-small" size="6">
        
            <!-- [[ expense form goes here ]] -->
            
             <!-- CREATE NEW EXPENSE -->
    <div aria-labelledby="newexpenseform">
        <!-- BOXED AREA -->
        <fieldset class="slds-box slds-theme--default slds-container--small">
        <legend id="newexpenseform" class="slds-text-heading--small 
          slds-p-vertical--medium">
          Add Expense
        </legend>
  
        <!-- CREATE NEW EXPENSE FORM -->
            <!-- aura:id -->
        <form class="slds-form--stacked">          
            <lightning:input aura:id="expenseform" label="Expense Name"
                             name="expensename"
                             value="{!v.newExpense.Name}"
                             required="true"/> 
            <lightning:input type="number" aura:id="expenseform" label="Amount"
                             name="expenseamount"
                             min="0.1"
                             formatter="currency"
                             step="0.01"
                             value="{!v.newExpense.Amount__c}"
                             messageWhenRangeUnderflow="Enter an amount that's at least $0.10."/>
            <lightning:input aura:id="expenseform" label="Client"
                             name="expenseclient"
                             value="{!v.newExpense.Client__c}"
                             placeholder="ABC Co."/>
            <lightning:input type="date" aura:id="expenseform" label="Expense Date"
                             name="expensedate"
                             value="{!v.newExpense.Date__c}"/>
            <lightning:input type="checkbox" aura:id="expenseform" label="Reimbursed?"  
                             name="expreimbursed"
                             checked="{!v.newExpense.Reimbursed__c}"/>
            <lightning:button label="Create Expense" 
                              class="slds-m-top--medium"
                              variant="brand"
                              onclick="{!c.clickCreate}"/>
        </form>
        <!-- / CREATE NEW EXPENSE FORM -->
  
      </fieldset>
      <!-- / BOXED AREA -->
    </div>
    <!-- / CREATE NEW EXPENSE -->
            
             
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / NEW EXPENSE FORM -->
    
<c:expensesList expenses="{!v.expenses}"/>
</aura:component>

expensesController.js

({
	 
    clickCreate: function(component, event, helper) {
     //component.find('expenseform' 배열형태의 expenseformd에 대한 참조를 가지고 온다.
     //ID가 고유한 경우 component리턴, 고유하지 않은 경우 component배열을 리턴한다. 
     var validExpense = component.find('expenseform')
     			.reduce(function (validSoFar, inputCmp) {//reduce()는 배열을 단일값으로 줄인다. 
                //validSoFar는 유효하지 않은 값을 찾을때까지 true로 유지
                //유효하지 않은 필드는 비어있는 필수필드, 지정된 최소값보다 숫자가 낮은 필드일수 있다. 
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid(); //유효하지 않은 필드에대한 오류메시지 표시 
            return validSoFar && inputCmp.get('v.validity').valid; //현재 입력필들의 유효성을 반환
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            var newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    }
})

*컨트롤러가 자식 구성 요소에 접근하는 방법이 필요할 때, aura:id를 컴포넌트 마크업에 추가한 후, component.find(theId)로 런타임에 대한 구성요소를 가지고 온다. 

-component.find() only lets you access direct child components

 

expensesHelper.js

({
    createExpense: function(component, expense) {
        var theExpenses = component.get("v.expenses");//컴포넌트 attribute에 저장된 배열에 대한 참조를 가지고 온다. 
 
        // Copy the expense to a new object
        // THIS IS A DISGUSTING, TEMPORARY HACK
        
        
        var newExpense = JSON.parse(JSON.stringify(expense));
 		console.log("Expenses before 'create': " + JSON.stringify(theExpenses));
        theExpenses.push(newExpense);
        component.set("v.expenses", theExpenses);//동일한 컴포넌트 어트리뷰터 참조에 set
        console.log("Expenses after 'create': " + JSON.stringify(theExpenses));
    }
})

- anywhere in your app that you’ve referenced the expenses attribute in an expression, the value of that expression is updated, and that update cascades everywhere the expenses attribute was used.

 

expensesItem.cmp

<aura:component>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="formatdate" type="Date"/>
    <aura:attribute name="expense" type="Expense__c"/>

    <lightning:card title="{!v.expense.Name}" iconName="standard:scan_card"
                    class="{!v.expense.Reimbursed__c ?
                           'slds-theme--success' : ''}">
        <aura:set attribute="footer">
            <p>Date: <lightning:formattedDateTime value="{!v.formatdate}"/></p>
            <p class="slds-text-title"><lightning:relativeDateTime value="{!v.formatdate}"/></p>
        </aura:set>
        <p class="slds-text-heading--medium slds-p-horizontal--small">
           Amount: <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
        </p>
        <p class="slds-p-horizontal--small">
            Client: {!v.expense.Client__c}
        </p>
        <p>
            <lightning:input type="toggle" 
                             label="Reimbursed?"
                             name="reimbursed"
                             class="slds-p-around--small"
                             checked="{!v.expense.Reimbursed__c}"
                             messageToggleActive="Yes"
                             messageToggleInactive="No"
                             onchange="{!c.clickReimbursed}"/>
        </p>
    </lightning:card>
</aura:component>

expensesItem.css

.THIS.slds-card.slds-theme--success {
    background-color: rgb(75, 202, 129);
}

expneseItemController.js

({
    doInit : function(component, event, helper) {
        var mydate = component.get("v.expense.Date__c");
        if(mydate){
            component.set("v.formatdate", new Date(mydate));
        }
    },
})

espensesList.cmp 

<aura:component>
    <aura:attribute name="expenses" type="Expense__c[]"/>
    <lightning:card title="Expenses">
        <p class="slds-p-horizontal--small">
            <aura:iteration items="{!v.expenses}" var="expense">
                <c:expenseItem expense="{!expense}"/>
            </aura:iteration>
        </p>
    </lightning:card>
</aura:component>

 

 

Next, modify the campingList component to contain a new item input form and an iteration of campingListItem components for displaying the items entered. Here are additional details for the modifications to the campingList component.

  • Add an attribute named items with the type of an array of camping item custom objects.
  • Add an attribute named newItem of type Camping_Item__c with default quantity and price values of 0.
  • The component displays the Name, Quantity, Price, and Packed form fields with the appropriate input component types and values from the newItem attribute. The Quantity field accepts a number that's at least 1.
  • Submitting the form executes the action clickCreateItem in the JavaScript controller.
  • If the form is valid, the JavaScript controller pushes the newItem onto the array of existing items, triggers the notification that the items value provider has changed, and resets the newItem value provider with a blank sObjectType of Camping_Item__c. For this challenge, place the code in your component's controller, not the helper.

 

camping.app

<aura:application extends="force:slds">
   <c:campingList/> 
</aura:application>

campingHeader.cmp

<aura:component >
    <lightning:layout class="slds-page-header slds-page-header--object-home">
    <lightning:layoutItem >
    <lightning:icon iconName="action:goal" alternativeText="My Camping"/>
</lightning:layoutItem>
    <lightning:layoutItem padding="horizontal-small">
<div class="page-section page-header">
<h1 class="slds-text-heading--label">Camping</h1>
<h2 class="slds-text-heading--medium">My Camping</h2>
</div>
</lightning:layoutItem>
</lightning:layout>
</aura:component>

campingList.cmp

<aura:component >
<aura:attribute name="items" type="Camping_Item__c[]"/>
<aura:attribute name="newItem" type="Camping_Item__c" default="{'Name':'',
	'Quantity__c':0,
	'Price__c':0,
	'Packed__c':false,
	'sobjectType':'Camping_Item__c'}"/>
 
<!-- NEW Campaing FORM -->
<div class="slds-col slds-col--padded slds-p-top--large">
    
	<c:campingHeader/>
	<div aria-labelledby="newCampaingForm">
	<!-- BOXED AREA -->
		<fieldset class="slds-box slds-theme--default slds-container--small">
 			<legend id="newCampaingForm" class="slds-text-heading--small slds-p-vertical--medium">
               	Add Expense
			</legend>
 			<!-- CREATE NEW Campaing FORM -->
				<form class="slds-form--stacked">
 
				<!-- For Name Field -->
    			<lightning:input aura:id="expenseform" label="Camping Name"
					name="expensename"
					value="{!v.newItem.Name}"
					required="true"/>
                    
				<!-- For Quantity Field -->
    			<lightning:input type="number" aura:id="campingform" label="Quantity"
					name="expenseamount"
					min="1"
					value="{!v.newItem.Quantity__c}"
					messageWhenRangeUnderflow="Enter minimum 1 Quantity"/>
				<!-- For Price Field -->
                    
    			<lightning:input aura:id="campingform" label="Price"
					formatter="currency"
					name="expenseclient"
					value="{!v.newItem.Price__c}"/>
                    
				<!-- For Check Box -->
    			<lightning:input type="checkbox" aura:id="campingform" label="Packed"
					name="expreimbursed"
					checked="{!v.newItem.Packed__c}"/>
 
    			<lightning:button label="Create Camping"
					class="slds-m-top--medium"
					variant="brand"
					onclick="{!c.clickCreateItem}"/>
			</form>
            
<!-- / CREATE NEW EXPENSE FORM --></fieldset>
<!-- / BOXED AREA -->
</div>
<!-- / CREATE NEW EXPENSE -->
</div>
<!-- ITERATIING ITEM LISTS -->
<div class="slds-card slds-p-top--medium">

<c:campingHeader/>
 
<section class="slds-card__body">
	<div id="list" class="row">
		<aura:iteration items="{!v.items}" var="item">
			<c:campingListItem item="{!item}"/>
		</aura:iteration>
	</div>
</section>
</div>
<!-- / ITERATIING ITEM LISTS -->
</aura:component>

campingListItem.cmp

<aura:component >
<aura:attribute name="item" type="Camping_Item__c"/>
 
Name:
<ui:outputText value="{!v.item.Name}"/>
 
Price:
<ui:outputCurrency value="{!v.item.Price__c}"/>
 
Quantity:
<ui:outputNumber value="{!v.item.Quantity__c}"/>
 
Packed:
<ui:outputCheckbox value="{!v.item.Packed__c}"/>
 
</aura:component>

campingListController.js

({
clickCreateItem : function(component, event, helper) {
var validCamping = component.find('campingform').reduce(function (validSoFar, inputCmp) {
// Displays error messages for invalid fields
inputCmp.showHelpMessageIfInvalid();
return validSoFar && inputCmp.get('v.validity').valid;
}, true);
 
if(validCamping){
var newCampingItem = component.get("v.newItem");
//helper.createCamping(component,newCampingItem);
var campings = component.get("v.items");
var item = JSON.parse(JSON.stringify(newCampingItem));
 
campings.push(item);
 
component.set("v.items",campings);
component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c','Name': '','Quantity__c': 0,
'Price__c': 0,'Packed__c': false });
}
}
})

 

출력값


Connect to Salesforce with Server-Side Controllers

The circuit starts with the Create button, which is wired to the clickCreate action handler (1). When the action handler runs, it gets values out of the form fields (2) and then adds a new expense to the expenses array (3). When the array is updated via set, it triggers the automatic rerendering of the list of expenses (4).

 

ExpensesController.apex

public with sharing class ExpensesController {
    
    @AuraEnabled 
    public static List<Expense__c> getExpenses(){
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
               			Reimbursed__c, CreatedDate
               FROM Expense__c];
    }
}

-"Aura"는 Lightning Components 핵심의 프레임 워크 이름

-All @AuraEnabled controller methods must be static methods, and either public or global scope.

-It just returns the SOQL query results directly(e method doesn’t do anything special to package the data for Lightning Components)

-Lightning Components framework handles all the marshalling/unmarshalling work involved in most situations.

 

-expenses component에 <aura:component controller="ExpensesController"> 추가 

-데이터를 로드하지 않고, 단순히 remote method를 호출한다.  

-1.expenses component가 로드될 때, expense레코드를 읽고, 읽어낸 레코들을 expenses component attribute에추가한다.

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

-expense component’s controller에 아래 내용 추가 

({
	 
    clickCreate: function(component, event, helper) {
      var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            var newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    },
    //Load expenses from Salesforce
    doInit: function(component, event, helper){
        //Create the action(1.리모트메서드 콜을 만들고)
        //c.getExpenses: it returns a remote method call to our Apex controller. 
        var action = component.get("c.getExpenses"); //c.? Server-side controller
        //Add callback behavior for when response is received (callback 함수는 서버에서 응답이 올때 실행됨)
        //(2.응답이 왔을때의 콜백함수 만들고)
        action.setCallback(this, function(response){ //this: 콜백이 실행될 범위(여기선 action handler자체)
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.expenses", response.getReturnValue());
            }
            else{
                console.log("Failed with state: "+ state);
            }
        });
        //send action off to be executed 
        //(3.리모트메서드콜을 큐업)
        $A.enqueueAction(action); // $A:프레임워크 전역변수 
        //1.서버요청을 대기열에 넣는다.
        
    }
})

 

-var action = component.get("c.getExpenses"); // 리모트 메서드 콜 또는 액션을 만듦

-c.Apex controller를 의미! 

-component.get("c.whatever") returns a reference to an action available in the controller, it returns a remote method call to our Apex controller. 

-$A.enqueueAction(action)서버요청을 큐업해주는 역할, 서버단에서 고려되지 시작하면 이 역할은 종료됨

  function(response) {
        var state = response.getState();
        if (state === "SUCCESS") {
            component.set("v.expenses", response.getReturnValue());
        }
    }

콜백함수는 단일 매개변수만 사용한다. 

1. 응답상태를 가져온다

2. 만약에 SUCCESS이면 요청은 계획대로 이루어진것이다

3. expenses attribute 에 응답받은 데이터를 set 한다. 

 

그렇다면 

1. SUCCESS가 아닐경우? 

2. 응답이 없을 경우?

3. 그리고 어떻게 응답된 데이터를 단순히 component attribute에 할당할 수 있는가? 서버컨드롤러와 컴포넌트 어트리뷰트의 데이터 타입이 일치할 경우 바로 리턴값을 할당할 수 있다. 

 

<aura:attribute name="expenses" type="Expense__c[]"/> : expenses의 데이터 타입! 

server-side controller action은 Expense__c로 정의된 return 데이터 타입을 가지고 있다. 

public static List<Expense__c> getExpenses() { ... } 이렇게!! 

 

업데이트된 컨트롤러

public with sharing class ExpensesController {
    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        // Perform isAccessible() checking first, then
        return [SELECT Id, Name, Amount__c, Client__c, Date__c, 
                       Reimbursed__c, CreatedDate 
                FROM Expense__c];
    }
    
    @AuraEnabled
    public static Expense__c saveExpense(Expense__c expense) {
        // Perform isUpdateable() checking first, then
        upsert expense;
        return expense;
    }
}

 

expensesHelper.js

    createExpense: function(component, expense) {
        var action = component.get("c.saveExpense");
        action.setParams({ //apex의 saveExpense 메서드에 정의된 파라미터의 이름과 동일해야한다.  
            "expense": expense
        });
        
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                var expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue()); //서버의 response를 push함
                component.set("v.expenses", expenses);
            }
        });
        $A.enqueueAction(action);
    },

 

action.setParams({:your parameter name must match the parameter name used in your Apex method declaration.

**Always use the exact API name of every object, field, type, class, method, entity, element! 

**required: 

 

YOUR CHALLENGE

Save and Load Records with a Server-Side Controller

Persist your records to the database using a server-side controller. The campingList component loads existing records when it starts up and saves records to the database when the form is submitted.

  • Create a CampingListController Apex class with a getItems method and saveItem method.
  • Add a doInit initialization handler that loads existing records from the database when the component starts up.
  • Modify the JavaScript controller to use a createItem method in the helper to save records to the database from a valid form submission. The new items are added to the controller's items value provider.
public class CampingListController {
    @auraenabled
    public static List<Camping_Item__c> getItems (){
        List<Camping_Item__c> CI = [select id, name,price__c,Quantity__c,Packed__c from Camping_Item__c ];
        return CI;
    }
    @auraenabled
    public static Camping_Item__c saveItem (Camping_Item__c item){
        insert item;
        return item;
    }
}
<aura:component controller="CampingListController">
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
	<aura:attribute name="items" type="Camping_Item__c[]"/>
    <aura:attribute name="er" type="boolean" default="false"/>
    <aura:attribute name="newItem" type="Camping_Item__c"    default="{ 'sobjectType': 'Camping_Item__c',
                         'Name': '',
                         'Price__c': 0,
                         'Quantity__c': 0,                         
                         'Packed__c': false
                       }"/>
    <ui:inputText value="{!v.newItem.Name}" aura:id="name" label="name"/>
    <ui:inputCheckbox value="{!v.newItem.Packed__c}" aura:id="Packed" label="Packed"/>
    <ui:inputCurrency value="{!v.newItem.Price__c}"  aura:id="Price" label="Price"/>
    <ui:inputNumber value="{!v.newItem.Quantity__c}" aura:id="Quantity" label="Quantity"/>
    <ui:button label="Create Camping" press="{!c.createItem}" aura:id="button"/>
    <br/>
	<aura:iteration items="{!v.items}" var="PerItem">
        
        <c:campingListItem item="{!PerItem}" />
    </aura:iteration>
</aura:component>

controller

({
	
    doInit  : function(component, event, helper) {
		var action = component.get("c.getItems");
        action.setCallback(this, function(response){
            var state = response.getState();
           
            if (component.isValid() && state === "SUCCESS") {
           
               
                component.set("v.items", response.getReturnValue());
                 
            }
        });
        
        $A.enqueueAction(action);
	},
    
    createItem : function(component, event, helper){
        
        helper.validateFields (component,component.find("name"));
        helper.validateFields (component,component.find("Price"));
        helper.validateFields (component,component.find("Quantity"));
        if(component.get("v.er") === false)
        {     
            var Item = component.get("v.newItem");            
            helper.createItem (component,Item);             
                       
        }
	}    
})

helper

({
	
    validateFields : function (component,field) {
        
        var nameField = field;
        console.log('yes:'+nameField);
        var expname = nameField.get("v.value"); 
        if ($A.util.isEmpty(expname)){
           component.set("v.er",true);
           nameField.set("v.errors", [{message:"this field can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
    },
    
    createItem : function (component,Item){         
        var action = component.get("c.saveItem");
        action.setParams({"item":Item});
        action.setCallback(this,function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                var campings = component.get("v.items");
                campings.push(response.getReturnValue());
                component.set("v.items", campings);
            }
        });
       $A.enqueueAction(action);        
    }
})