Saltar al contenido

¿Cómo paso un registro SObject de LWC a un método Apex personalizado para conservar los datos?

Solución:

En LWC, ahora es preferible intentar usar Lightning Data Service primero, que incluye cualquiera de los adaptadores ui * Api como getRecord, createRecord y updateRecord:

La forma más sencilla de trabajar con datos es utilizar los componentes Lightning base construidos en LDS: lightning-record-form, lightning-record-edit-form o lightning-record-view-form componentes.

Si necesita más personalización de la que permiten esos componentes, use @wire para especificar un adaptador de cable Lightning Data Service. Cada adaptador de cable tiene una forma de datos diferente.

Supongamos que ha agotado sus opciones y está buscando una forma escalable y repetible de pasar datos de manera consistente entre el cliente y el servidor. Un tipo de datos extremadamente flexible es entonces Map<String, Object>:

lwcTest.html

<lightning-button label="Mutate Data" onclick={mutateData}></lightning-button>
<lightning-button label="Update Complex" onclick={updateComplexData}></lightning-button>
<lightning-button label="Update Account" onclick={updateAccountData}></lightning-button>

lwcTest.js

import { LightningElement, wire, api } from 'lwc';
import wireSimpleOrComplexData  from '@salesforce/apex/DataServiceCtrl.wireSimpleOrComplexData';
import updateComplex  from '@salesforce/apex/DataServiceCtrl.updateComplex';
import updateAccount  from '@salesforce/apex/DataServiceCtrl.updateAccount';

export default class LwcTest extends LightningElement {
  @api
  get recordId() {
    return this._accountId;
  }
  set recordId(value) {
    this._accountId = value;
  }

  // private
  _accountId;
  _wiredData;
  _account;
  _contacts;
  _opportunities;

  @wire(wireSimpleOrComplexData, { accountId: '$_accountId' })
  wiredData(result) {
    this._wiredData = result; // to preserve refreshApex if needed
    if (result.data) {
      // for single sobject object spread works since this is a shallow clone
      this._account = { ...result.data.accountFromServer };

      // for collections, since every element in array is proxied, we need a deep clone
      this._contacts = JSON.parse(JSON.stringify(result.data.contactsFromServer));

      // if complex objects are wanted, it might be better to do this at the result.data level
      this._opportunities = JSON.parse(JSON.stringify(result.data.opportunitiesFromServer));

      console.log(result.data.user.firstName); // UserInfo.getFirstName()
      console.log(result.data.system.now);     // System.now()
    } else {
      console.log(result.error);
    }
  }

  // Before reaching this, all the following data had their read only proxies removed
  mutateData() {
    this._account.Name += ' Updated';

    for (let contact of this._contacts) {
      contact.Email = contact.LastName + '@test.com';
    }

    for (let opp of this._opportunities) {
      opp.Name += ' Updated';
    }
  }

  updateComplexData() {
    const dataToUpdate = {
      accountAfterMutate: this._account,
      contactsAfterMutate: this._contacts,
      opportunitiesAfterMutate: this._opportunities
    }
    updateComplex({data: dataToUpdate})
      .then(result => {
        // on success, you can bind to a tracked vars to re-render them
        console.log(result);
      })
      .catch(error => {
        console.log(error);
      });
  }

  updateAccountData() {
    updateAccount({accountToUpdate: this._account})
      .then(result => {
        // on success, you can bind to a tracked account variable for template view to re-render the template
        console.log(result);
      })
      .catch(error => {
        console.log(error);
      });
  }
}

DataServiceCtrl.cls

  @AuraEnabled (cacheable=true)
  public static Map<String, Object> wireSimpleOrComplexData(String accountId) {
    Map<String, Object> result = new Map<String, Object>();
    // Data 1 single sobject
    Account acc = [SELECT Id, Name FROM Account WHERE Id =: accountId LIMIT 1];
    result.put('accountFromServer', acc); // otherwise will be an array of 1 if directly SOQL-ed

    // Data 2 collections
    result.put('contactsFromServer', [SELECT Id, LastName FROM Contact WHERE AccountId =: accountId]);
    result.put('opportunitiesFromServer', [SELECT Id, Name FROM Opportunity WHERE AccountId =: accountId]);

    // Data 3 nested properties like a POJO
    Map<String, String> userInfoMap = new Map<String, String>();
    userInfoMap.put('firstName', UserInfo.getFirstName());

    Map<String, Object> systemInfoMap = new Map<String, Object>();
    systemInfoMap.put('now', System.now());
    
    result.put('user', userInfoMap);
    result.put('system', systemInfoMap);

    return result;
  }

  @AuraEnabled
  public static Map<String, Object> updateComplex(Map<String, Object> data) {
    // Because sobjects were directly used, we can use this serialize/deserialize trick to get it back into a useable state
    Account account = (Account) JSON.deserialize(
      JSON.serialize(data.get('accountAfterMutate')),
      Account.class
    );
    List<Contact> contacts = (List<Contact>) JSON.deserialize(
      JSON.serialize(data.get('contactsAfterMutate')),
      List<Contact>.class
    );
    List<Opportunity> opportunities = (List<Opportunity>) JSON.deserialize(
      JSON.serialize(data.get('opportunitiesAfterMutate')),
      List<Opportunity>.class
    );
    // you could put database.saveResult in here if you want
    Map<String, Object> updateResults = new Map<String, Object>();
    update account;
    update contacts;
    update opportunities;
    updateResults.put('account', account);
    updateResults.put('contacts', contacts);
    updateResults.put('opportunities', opportunities);

    return updateResults;
  }

  @AuraEnabled
  public static Account updateAccount(Account accountToUpdate) {
    // no need to serialize/deserialize or transport as JSON here
    update accountToUpdate;
    return accountToUpdate;
  }

Comencé a mirar a mi alrededor y seguí el ejemplo del tema Migrar Apex, que dice:

Los componentes Aura y los componentes web Lightning utilizan un controlador Apex para leer o persistir Datos de Salesforce. No hay diferencias de sintaxis para los dos modelos de programación.

Entonces, según cómo funcionó en Lightning Aura Components, intenté ver si también funcionaba en LWC, y , funciona.

En resumen, necesitaba asegurarme de representar al SObject record / data como JSON y luego pásalo como parámetro.

A continuación se muestra el código de muestra que funcionó para mí, donde pude construir un JSON / manipular un JSON existente y luego pasarlo como parámetro a un método Apex personalizado para crear / actualizar un registro.

HTML

<lightning-card title="My Hello World" icon-name="standard:contact">
    {recordId}
</lightning-card>
<lightning-button label="Create Record" onclick={createRecord}></lightning-button>
<lightning-button label="Update Record" onclick={udpateRecord}></lightning-button>

JavaScript

import createContactRecord from '@salesforce/apex/ContactController.createContactRecord';
import updateContactRecord from '@salesforce/apex/ContactController.updateContactRecord';
import myContact from "@salesforce/apex/ContactController.fetchContact";
....

@track recordId;
contactRecord;


// fetches a contact record from Apex
@wire (myContact)
    fetchedContact({error, data}){
        if(data){

            // this is where I save the fetched contact which will be updated later
            this.contactRecord = JSON.stringify(data);
            ...
            ...
        }
        ...
    }


// my create record JS function, where I construct a SObject and am able to 
// successfully create a record
createRecord() {

    // created a JSON representation of the Contact record, 
    // same as we would do in Lightning Aura Components

    let cont = { 'sobjectType': 'Contact' };
    cont.FirstName="Jayant";
    cont.LastName="From LWC";

    createContactRecord({newRecord: cont})
        .then(result => {
            this.recordId = result;
            console.log(result);
        })
        .catch(error => {
            console.log(error);
            this.error = error;
        });
}


// my update record JS function, where I manipulate the JSON 
// and set some values to be able to successfully update the record
updateRecord() {
    let cont = JSON.parse(this.contactRecord);

    // update the fields those are required to be updated
    cont.LastName="-LWC1";

    updateContactRecord({recordForUpdate: cont})
        .then(result => {
            this.wiredContact = result;
            console.log(result);
        })
        .catch(error => {
            console.log(error);
            this.error = error;
        });
}   

Apéndice

@AuraEnabled(cacheable=true)
public static Contact fetchContact(){
    return [SELECT Id,Name, LastName FROM Contact where Id='xxxx' LIMIT  1];
}


@AuraEnabled
public static String createContactRecord(Contact newRecord){
    insert newRecord;
    return newRecord.Id;
}


@AuraEnabled
public static String updateContactRecord(Contact recordForUpdate){
    update recordForUpdate;
    return recordForUpdate.Name;
}

Aquí está mi código que utilizo para los registros CRU en LWC. Ejemplo bastante básico. No estoy usando la manipulación de String o JSON. También estoy usando enlaces estáticos usando importaciones de fieldName

HTML:

<lightning-input label="FirstName" value={realFormData.FirstName} if:true={realFormData} onchange={updateValue}  data-field="FirstName"></lightning-input>
<lightning-input label="LastName" value={realFormData.LastName} if:true={realFormData} onchange={updateValue}  data-field="LastName"></lightning-input>

{recordId} <br/>
<button onclick={saveRecord}> Save Record</button>

<br/>
<button onclick={createRecord}> Create new hardcored CONTACT Record and load in UI</button>

JS:

import { LightningElement ,wire,track,api } from 'lwc';
import getMyContact from "@salesforce/apex/ContactController.fetchContact";
import updateMyContact from "@salesforce/apex/ContactController.updateContact";
import createMyContact from "@salesforce/apex/ContactController.createContact";
import { refreshApex } from '@salesforce/apex';
import CONTACT_FIRSTNAME from '@salesforce/schema/Contact.FirstName';
import CONTACT_LASTNAME from '@salesforce/schema/Contact.LastName';


export default class MyCmp extends LightningElement {


 @api wiredContact;
 @api recordId;
 @api realFormData;


 @wire (getMyContact , { contactId: '$recordId' })
        fetchedContact( resp){
           this.wiredContact = resp;
           this.realFormData = {... this.wiredContact.data};

    }


    updateValue(event){



        this.realFormData = {...this.realFormData , [event.target.dataset.field] : event.detail.value};
        console.log( this.realFormData);
    }


    saveRecord(event ){

        updateMyContact({con : this.realFormData}).then(()=>{

            console.log('Refresh Apex called');
            refreshApex(this.wiredContact);
        });

    }


    createRecord(event ){
            let newContact = { [CONTACT_FIRSTNAME.fieldApiName] : 'Pikachdu' ,[CONTACT_LASTNAME.fieldApiName] : 'Raichu' };



            createMyContact({con : newContact}).then((resp)=>{
                            this.recordId = resp.Id; //this will auto call wireMethod/


            }).catch((err) => {
               // Handle any error that occurred in any of the previous
               // promises in the chain.

               console.log(JSON.stringify(err));
             });



        }




}

Apéndice:

public class ContactController {

    @AuraEnabled(cacheable=true)
    public static Contact fetchContact(Id contactId){
        return [SELECT Id,FirstName,LastName FROM COntact where id=:contactId LIMIT  1];
    }


    @AuraEnabled
    public static void updateContact(Contact con){
        update con;
    }

    @AuraEnabled
    public static contact createContact(Contact con){

        insert con;
        return con;


    }

}
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *