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>
  • are you using it inside a managed package – Manjot Singh Feb 8 '18 at 14:45
  • @ManjotSingh No, I'm not. – notmyf4ulty Feb 8 '18 at 15:01
  • I happen to be coding something similar today, and I'm using this as my reference: salesforce.stackexchange.com/questions/184889/… – PatMcClellan__c Feb 9 '18 at 0:42
  • So it's not recognizing evt, which you just got with $A.get("e.c:TestEvent"). This sounds silly, but I've had a similar issue. In your Component where you register the event, swap the order of name/type and see if that fixes it. – PatMcClellan__c Feb 9 '18 at 4:17
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));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.