0

I'm in need of displaying the names of books which have read attribute set as true using the checkbox, on another component. Once the checkbox is clicked, the record gets updated but it does not get automatically updated in the second component. I get it only when I manually refresh the component. I'm trying to use event change handlers to track changes to the Books attribute. I see that console.log("Items change"); executes only at the time of loading. Is there any alternate way to achieve it?

Books4EveryoneHome.cmp

<aura:component implements="flexipage:availableForAllPageTypes" access="global" controller="Books4EveryoneHomeController">
  <aura:attribute name="Books" type="Book__c" />
    <aura:attribute name="ReadState" type="Boolean" />
    <aura:attribute name="UpdateResult" type="String" />
  <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>


  <lightning:card title="Books4Everyone Books">
    <aura:set attribute="body">
     <table class="slds-table slds-table_bordered slds-table_cell-buffer">
        <thead>
          <tr class="slds-text-title_caps">
            <th scope="col">Book Titles</th>
            <th scope="col">Book Descriptions</th>
              <th scope="col">Read</th>
          </tr>
        </thead>
        <tbody>
          <aura:iteration items="{!v.Books}" var="books">
            <tr scope="row">
              <td> {!books.Name}</td>
              <td> {!books.Description__c}</td>
                <td><input aura:id="chkbox" type="checkbox" name="{!books.Id}" value="" checked="{!books.Read__c}" onclick="{!c.onClick}" /></td>
            </tr>
          </aura:iteration>
        </tbody>
      </table>
      </aura:set> 
    </lightning:card>

   <aura:handler name="change" value="{!v.Books}" action="{!c.itemsChange}"/>
</aura:component>

Books4EveryoneHomeController.js

({
  doInit: function(component, event, helper) {
    var action = component.get("c.getBooks");
     // var result = event.target.value;

    action.setCallback(this, function(data) {
      component.set("v.Books", data.getReturnValue());
      console.log(data.getReturnValue());
    });
    $A.enqueueAction(action);
  },
    onClick: function(component, event, helper) {
    var action = component.get("c.setBooks");
      var result = event.target.checked;
        var cmpId = event.target.name;
        console.log(result);
        action.setParams({ "x" : result.toString(), "y" : cmpId });


    action.setCallback(this, function(data) {
      component.set("v.UpdateResult", data.getReturnValue());
      console.log(data.getReturnValue());
    });
    $A.enqueueAction(action);
  },
    itemsChange: function(cmp, evt) {
        console.log("Items change");
        //this.doInit(component, event, helper);
    }
})

Books4EveryoneHomeController.apxc

public with sharing class Books4EveryoneHomeController {
    @AuraEnabled
    public static List<Book__c> getBooks(){
        return [SELECT Id,Name,Description__c,Read__c
                FROM Book__c];
    }

    @AuraEnabled
    public static String setBooks(String x, String y){
        System.debug(x);
        Book__c book = [SELECT Id,Name,Description__c,Read__c
                        FROM Book__c WHERE Id = :y];
        book.Read__c = Boolean.valueOf(x);

        try{
            update book;    
            return ('Success');
        }
        catch (DmlException e) {
            return ('Fail'); 
        }
    }


}

BooksName.cmp

<aura:component implements="flexipage:availableForAllPageTypes" access="global" controller="BooksNameApexController">
    <aura:attribute name="Books" type="Book__c" />
    <aura:attribute name="ReadState" type="Boolean" />
    <aura:attribute name="UpdateResult" type="String" />
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="change" value="{!v.Books}" action="{!c.itemsChange}"/>


    <lightning:card title="Books4Everyone Books">
        <aura:set attribute="body">
            <table class="slds-table slds-table_bordered slds-table_cell-buffer">
                <thead>
                    <tr class="slds-text-title_caps">
                        <th scope="col">Book Titles</th>

                    </tr>
                </thead>
                <tbody>
                    <aura:iteration items="{!v.Books}" var="books">
                        <tr scope="row">
                            <td> {!books.Name}</td>
                        </tr>
                    </aura:iteration>
                </tbody>
            </table>
        </aura:set> 
    </lightning:card>


</aura:component>

BooksNameController.js

({
  doInit: function(component, event, helper) {
    var action = component.get("c.getBookNames");
     // var result = event.target.value;

    action.setCallback(this, function(data) {
      component.set("v.Books", data.getReturnValue());
      console.log(data.getReturnValue());
    });
    $A.enqueueAction(action);
  },
    itemsChange: function(cmp, evt) {
        alert("Items change");
        //this.doInit(component, event, helper);
    }
})

BooksNameApexController.apxc

public class BooksNameApexController {
 @AuraEnabled
  public static List<Book__c> getBookNames(){
    return [SELECT Id,Name,Description__c,Read__c
  FROM Book__c WHERE Read__c=true];
  }
}

BooksNameApp.app

<aura:application >
    <c:Books4EveryoneHome />
    <c:BooksName />
</aura:application>

1 Answer 1

1

In the application setup that you have now,

<aura:application >
    <c:Books4EveryoneHome />
    <c:BooksName />
</aura:application>

you have two components that are siblings (neither is the parent or owner of the other). Both components have an attribute called v.Books, and both of them have a change handler itemsChange registered for that property.

Here's the trick, though: because the components are siblings and neither one has its v.Books value bound to the other, those properties are completely independent. Their values don't correlate, and the change events on each component will go off only when that component's own v.Books property is updated in its server controller callback.

There's a lot of ways this kind of data can be coordinated. One possibility, but not the only one, would be to have your Books4EveryoneHome fire an application event at the point that its callback returns from marking a book as read. (Because the components are siblings, you can't use component events to communicate between them). Implement a handler for that event in BooksName to re-query for read books and update the component accordingly.

Because that approach involves server round-trips, you may find that it's not as fluid a UX as you need. If that's the case, you can use more sophisticated logic, like including the Id and details of the read Book in the application event and adding it without performing a query in BooksName. Alternately, you could implement logic in the parent component of both BooksName and Books4EveryoneHome to handle all the queries and filtering of the resulting book list, and managing keeping both child components in sync.

What's definite is you need some mechanism, some glue code, for communicating between the two components.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.