[SalesForce] Calling a remote action in Angular

I'm trying to get Tasks that belong to the current user when the page loads.

This code works and can easily be copied and pasted in your dev console for testing and it prints all to the console.log. A simplified version.

VF page

<apex:page controller ="TestController" standardStylesheets="false" applyHtmlTag="false" sideBar="false" showHeader="false">

    <script>
    window.$userId = "{!$User.Id}";
    var app = angular.module('MyApp',[]);

    app.controller('myController',['$scope',function($scope)
                                   {

                                       $scope.getTaskData = Visualforce.remoting.Manager.invokeAction(
                                           '{!$RemoteAction.TestController.getTasks}',
                                           window.$userId,
                                           function(result, event)
                                           {
                                               $scope.$apply(function() {
                                                   console.log(result);
                                               });
                                           });
                                   }]);




    </script>
    <div ng-app="MyApp">

        <div ng-controller="myController">

        </div>
    </div>
    <apex:includeScript value="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"/>
    <apex:includeScript value="//ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"/>
</apex:page>

Controller

global class TestController {    

    @RemoteAction
    global static List<Task> getTasks(String ownerId) {
          return  [SELECT Id, Subject, ActivityDate, Status FROM Task WHERE Owner.Id =:ownerId limit 200];
    }
}

But this code below doesn't, and it uses the same getTaskData call to the remote action. 99% of all the development so far has been in the static resource for the app. You can download it here, zip it up and add it to your static resources. Then add the VF page and controller below to see what is fully going on. Be sure to go into the app.js to change your namespace if you have one. And name the the static resource when it's zip to 'taskCalendar'.

This file line 30. When I call the Visualforce Remote Action it breaks the app. Not sure why.

Sorry the long post, but I'm desperate and have been working on this for 15 hours today…..

VF Page

<apex:page applyHtmlTag="true" applyBodyTag="false" docType="html-5.0" showHeader="true" standardStylesheets="false" sidebar="false" controller="TaskCalendarController2">

     <meta charset="utf-8"/>
     <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
     <meta name="viewport" content="width=device-width, initial-scale=1"/>
     <meta name="description" content=""/>
     <meta name="author" content=""/>
     <link rel="icon" href="favicon.png"/>

    <script>

        window.$userId = "{!$User.Id}";

    </script>

    <apex:remoteObjects >
        <!--
            Each proxy object is defined by an apex:remoteObjectModel tag. The name attribute is set to the API name of the SObject you want perform DML operations on.
            The jsShorthand attribute is set to the JavaScript object name used to instantiate it.
            The fields attribute is set to a comma-delimited list of SObjectFields you want to load.
        -->
        <apex:remoteObjectModel name="Task" fields="Id, Subject, ActivityDate, Status">
            <!--Each field you load in to your proxy object can re-mapped to a friendlier name -->
        </apex:remoteObjectModel>
        <apex:remoteObjectModel name="User" fields="Id, FirstName, LastName, Email"/>
        <apex:remoteObjectModel name="Account" fields="Id,Name,CreatedDate"/>
    </apex:remoteObjects>    


    <apex:stylesheet value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/bs/css/bootstrap.min.css')}"/>
    <apex:stylesheet value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/fa/css/font-awesome.min.css')}"/>
    <apex:stylesheet value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/css/ie10-viewport-bug-workaround.css')}"/>

    <apex:stylesheet value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/fc/fullcalendar.css')}"/>
    <link href="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/fc/fullcalendar.print.css')}" rel='stylesheet' media='print' />


    <apex:stylesheet value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/typeahead/typeahead.css')}"/>

    <apex:stylesheet value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/css/custom.css')}"/>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]--> 


    <body ng-app="taskCalendar" class="wcs">

               <!-- Begin page content -->
        <div class="content-wrapper" ng-view="ng-view">
        </div>

        <div class="footer">
          <div class="container-fluid">
            <p class="text-muted">&copy; TouchPointCRM</p>
          </div>
        </div> 


        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/js/jquery.min.js')}"/>
        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/bs/js/bootstrap.min.js')}"/>
        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/js/ie10-viewport-bug-workaround.js')}"/>

        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/fc/lib/moment.min.js')}"/>
        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/fc/fullcalendar.min.js')}"/>

        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/typeahead/handlebars.min.js')}"/>
        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/typeahead/typeahead.bundle.min.js')}"/>

        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/assets/js/custom_tc.js')}"/>

        <apex:includeScript value="//ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"/>
        <apex:includeScript value="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.min.js"/>
        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/app/app.js')}"/>
        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/app/task/task-entity.js')}"/>

        <apex:includeScript value="{!URLFOR($Resource.taskCalendar, 'taskCalendar/app/task/task-calendar-controller.js')}"/>

    </body>

</apex:page>

Controller

global class TaskCalendarController2 {    
    @RemoteAction
    global static List<Task> getTasks(String ownerId) {
          return  [SELECT Id, Subject, ActivityDate, Status FROM Task WHERE Owner.Id = :ownerId limit 200];
    }
}

Best Answer

So the main problem is Merge fields only work in VF pages. If you check this line https://github.com/tylerzika/taskCalendar/blob/master/app/task/task-calendar-controller.js#L32

You have a merge field which when moved to static resource just evaluates to a string, whereas when you use in VF page , it actually renders a value.

So in VF page you can do something like

var remoteGetTask = '{!$RemoteAction.TestController.getTasks}';

And in the static resource file

$scope.getTaskData = function() {       
    Visualforce.remoting.Manager.invokeAction(
       remoteGetTask,
       window.$userId,
       function(result, event)
       {
           $scope.$apply(function() {
            console.log(result);
           });
       }
    );
}
Related Topic