0

I'm moving my subject data to a service and trying to smart about the code but I'm running into some issues.

When I turn off my API server and run the create function, I expect just the onRejected callback to be fired. Rather both the onFulfilled and the onRejected callbacks are being fired.

Service

(function(){
  'use strict';

  angular
  .module('app')
  .factory('$cmSubjectData', $cmSubjectData);

  function $cmSubjectData($log, Restangular, $mdToast) {

    var service = {
      getAll: getAll,
      getById: getById,
      update: update,
      create: create,
      destroy: destroy
    };

    return service;

    function getAll() {
      return subjects().getList().then(handleSuccess, handleError('Error getting all subjects'));
    }

    function getById(id) {
      return subjects().one(id).get().then(handleSuccess, handleError('Error getting this subject'));
    }

    function update(subject) {
      Restangular.all('subjects')
      .post(subject)
      .then(handleSuccess, handleError('Error updating subject'));
    }

    function create(subject) {
      Restangular.all('subjects')
      .post(subject)
      .then(successToast(subject.basicInfo.firstName + ' has been created'), handleError('Error creating subject'));
    }

    function destroy(id) {
      var subject = subjects().one(id);
      subject.remove()
      .then(successToast('Subject has been deleted!'), handleError('There was an error deleting this subject'));
    }

    // Private Functions
    function subjects() {
      return Restangular.service('subjects');
    }

    function handleSuccess(data) {
      $log.info('Success');
      return data;
    }

    function handleError(error) {
      return function () {
        $log.warn(error);
      };
    }

    function successToast(content) {
      $mdToast.show(
        $mdToast.simple()
        .content(content)
      );
    }

  }

})();

Controller

(function() {
  'use strict';

  angular
  .module('app')
  .controller('SubjectNewCtrl', SubjectNewCtrl);

  function SubjectNewCtrl($cmSubjectData) {

    var vm = this;

    vm.subject = {};
    vm.createSubject = $cmSubjectData.create;

  }

})();

View where function is called:

<button ng-click="vm.createSubject(vm.subject)">Add</button>

Any help would be appreciated.

sharpmachine
  • 3,783
  • 6
  • 19
  • 24
  • Do you realize that when you get a rejected promise and it calls your reject handler and you then return anything other than a rejected promise that the resulting promise is fulfilled, not rejected? The promise infrastructure assumes you "handled" the promise at that point and thus the resulting promise is now fulfilled, not rejected. If you don't have a reject handler or you either return a rejected promise or you throw in the reject handler, then the resulting promise will be rejected. So, the reject handler can decide how it wants to leave the promise. – jfriend00 Aug 23 '15 at 03:58
  • See this answer from today for something similar: [Promise.settle and promise fulfillment vs rejection](http://stackoverflow.com/questions/32160055/promise-settle-and-promise-fulfillment-vs-rejection/32160109#32160109). – jfriend00 Aug 23 '15 at 04:05
  • If, on fulfilled, I run the handleSuccess function it works as expected, but when running the successToast function, it doesn't work. – sharpmachine Aug 23 '15 at 04:17
  • 1
    When you do `.then(successToast(...))`, you are running `successToast(...)` immediately and passing the return value from that to `.then()` which is not the timing you want. Remember, you have to pass a function reference to `.then()` if you want it executed later. You can either change `successToast()` to return a function like `handleError()` does or you can use `.then(function() { return successToast(...);})`. – jfriend00 Aug 23 '15 at 04:24
  • Okay! So I made `successToast()` return a function like `handleError()` so it's working as expecting (Would you like to post your solution as an answer so I can accept it as the correct answer?). What I don't get is I thought `successToast()` was a function, but now I've made it a function inside of a function. So I'm a little confused. – sharpmachine Aug 23 '15 at 14:37

1 Answers1

0

When you do

.then(successToast(...))

you are running successToast(...) immediately and passing the return value from that to .then() which is not the timing you want. Remember, you have to pass a function reference to .then() if you want it executed later. If you include the parens () with your function, then that tells Javascript to execute your function immediately. That is not what you want in this case. If you had no extra arguments to the function, you could just do:

.then(successToast)

But, because you have arguments you want to pass when you call it, you can't do it that way. So, you have two choices. You can make it work like handleError() where calling it with the arguments just returns a function (so that is what gets passed to .then() like this:

function successToast(content) {
  return function() {
      $mdToast.show($mdToast.simple().content(content));
  }
}

Then, it gets call like you have already:

.then(successToast(subject.basicInfo.firstName + ' has been created'))

Or you can insert your own stub function reference in the call itself:

.then(function() {
    return successToast(subject.basicInfo.firstName + ' has been created');
});

Either example passes a function reference to .then() and does not execute the core of your logic until later when .then() calls that function reference.

jfriend00
  • 683,504
  • 96
  • 985
  • 979