2

Let's consider a Lightning component with a single button and which uses a CometD / Streaming API to handle some server actions. A button click calls a server-side controller's action which inserts a PushTopic's object. After receiving a Streaming API event I'd like to fire an application-type event. To make a case simplier, I handle that event inside of the same component.

The problem is that when I try to configure the event (at the line evt.setParams({testObject : testObject});), I receive browser's exception:

TypeError: Cannot read property 'setParams' of undefined

In my opinion it looks like the event is not visible and I'm really out of ideas how to make it work. Code below:

TestComponent.cmp:

<aura:component controller="TestController" implements="flexipage:availableForAllPageTypes,force:hasRecordId" access="global">
    <ltng:require scripts="{!$Resource.JsCometd}" afterScriptsLoaded="{!c.doInit}" />
    <aura:registerEvent name="testEvent" type="c:TestEvent"/>
    <aura:handler event="c:TestEvent" action="{!c.onTestEvent}"/>
    <ui:button label="Click Me" press="{!c.buttonClicked}"/>
</aura:component>

TestComponentController.js:

({
  // Button click handler
  buttonClicked : function(component, event, helper) {
    console.log("Button clicked.");
    var action = component.get("c.insertTestObject");
    action.setParams({testFieldValue : "testValue"});
    action.setCallback(this, function(response) {
        var state = response.getState();
        if (state === "SUCCESS") {
            console.log("Test object inserted!");
        } else if (state === "ERROR") {
            console.log("An error has occurred while test object inserting");
        }
    });
    $A.enqueueAction(action);
  },

  // Event handler
  onTestEvent : function(component, event, helper) {
    var testObject = event.getParam("testObject");
    console.log(testObject);
  },

  // CometD / Streaming API init
  doInit : function(component, event, helper) {
      let cometd = new org.cometd.CometD();

      function onMessage(message) {
          console.log('Platform event received. ' + JSON.stringify(message));
          var testObject = message.data.sobject;

          // Invoking an event
          var evt = $A.get("e.c:TestEvent");

// ----------------- HERE IS THE EXCEPTION THROWN ----------------

          evt.setParams({testObject : testObject});
          evt.fire();
      }

      function initCometd() {
          let channelUrl = '/topic/TestPushTopic';

          cometd.handshake($A.getCallback(function(handshakeReply) {
              console.log('handshake response');
              console.log(handshakeReply);

              if(handshakeReply.successful) {
                  console.log('Connected to CometD.');
                  let newSubscription = cometd.subscribe(channelUrl, onMessage);
              }
          }));
      }

      function onSessionId(sessionId) {
          cometd.configure({
              url: '/cometd/41.0/',
              requestHeaders: { Authorization: "OAuth " + sessionId },
              appendMessageTypeToURL: false
          });
          cometd.websocketEnabled = false;
          console.log('CometD configured.');
          setTimeout(initCometd, 1000);
      }

      function commonActionResponse(successCallback, errorCallback, incompleteCallback) {
          return function(response) {
              var state = response.getState();
              console.log("State: " + state);
              if (state === "SUCCESS" && successCallback) {
                  successCallback(response.getReturnValue());
              }
              else if (state === "INCOMPLETE") {
                  if(incompleteCallback) {
                      incompleteCallback(response);
                  }
                  console.log(`incomplete: ${JSON.stringify(state)}`);
              }
                  else if (state === "ERROR") {
                      var errors = response.getError();
                      if(errorCallback) {
                          errorCallback(errors);
                          return;
                      }
                      if (errors) {
                          if (errors[0] && errors[0].message) {
                              console.log("Error messagee: " +
                                          errors[0].message);
                          }
                          console.log(errors);
                      } else {
                          console.log("Unknown error");
                      }
                  }
          }
      }

      let sessionAction = component.get("c.getSessionId");
      sessionAction.setCallback(this, commonActionResponse(onSessionId));
      $A.enqueueAction(sessionAction);
    }
})

TestController.cls:

public with sharing class TestController {

    @AuraEnabled
    public static String getSessionId() {
        return UserInfo.getSessionId();
    }

    @AuraEnabled
    public static TestObject__c insertTestObject(String testFieldValue) {
        TestObject__c testObject = new TestObject__c(TestField__c = testFieldValue);
        insert testObject;
        return testObject;
    }
}

TestEvent.evt:

<aura:event type="APPLICATION">
    <aura:attribute name="testObject" type="TestObject__c"/>
</aura:event>
4

1 Answer 1

0

I've found a solution. As stated in Modifying Components Outside the Framework Lifecycle article:

Use $A.getCallback() to wrap any code that modifies a component outside the normal rerendering lifecycle

I wrapped onMessage() function with a $A.getCallback() so the line in which I subscribe a CometD event should look like this:

let newSubscription = cometd.subscribe(channelUrl, $A.getCallback(onMessage));

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .