angular-ui / ui-router

The de-facto solution to flexible routing with nested views in AngularJS
http://ui-router.github.io/
MIT License
13.54k stars 3k forks source link

resolve problem #3179

Closed ptitdje45 closed 7 years ago

ptitdje45 commented 7 years ago

Hi there,

Having a little problem on my resolve to avoid a user to go on a user that doesn't exist.

client.route.js `(function() { 'use strict';

angular .module('app.client') .run(appRun);

appRun.$inject = ['routerHelper', '$stateParams', 'dataservice']; / @ngInject / function appRun(routerHelper) { routerHelper.configureStates(getStates()); }

function getStates() { return [ { state: 'dashboard.clients.client', config: { url: '/:idCli', views: { '@': { templateUrl: 'app/clients/client/client.html', controller: 'ClientController', controllerAs: 'vm', resolve: { clientPrepService: clientPrepService } } }, title: 'Client', ncyBreadcrumb: { label: '{{vm.idCli}}' // angular-breadcrumb's configuration } } } ]; }

function clientPrepService(dataservice, $stateParams) { console.log('uh ?'); return dataservice.sendRequest('get','clients/idRefClient/' + $stateParams.idCli); }

})(); `

I don't even get my console.log in the clientPrepService. I'm trying to follow the styleguide of John Papa. All my clients are currently rejected. ( all works without the resolve ).

Thanks by advance,

Anber commented 7 years ago

resolve inside views is deprecated and doesn't work in ui-router 1.0. Which version do you use?

Also, you should annotate your clientPrepService.

ptitdje45 commented 7 years ago

"angular-ui-router": "^0.2.18", i did the annotate.

ptitdje45 commented 7 years ago

My new problem is that 204 http isn't considered as an error in angular. Is there a tricks to handle this ?

Anber commented 7 years ago

Can you show your ClientController?

Formally 204 isn't error. Error's codes start from 400.

ptitdje45 commented 7 years ago

@Anber

 ClientController.$inject = ['$q', 'dataservice','logger', '$state', '$scope','$stateParams','communicationservice','clientPrepService'];
  /* @ngInject */
  function ClientController($q, dataservice, logger, $state, $scope,$stateParams, communicationservice, clientPrepService) {
    var vm = this;
    vm.client = clientPrepService[0];
    activate();

ClientController. So when i call a good client, i can use it normally in my controller. This actually work perfectly.

I will talk about the logic because i'm new to resolve routing, don't hesitate to correct me if i'm wrong :

The problem is : - even if i have a empty client, the route resolve go the controller.

(function() {
  'use strict';

  angular
  .module('app.client')
  .run(appRun);

  appRun.$inject = ['routerHelper'];
  clientPrepService.$inject = ['dataservice','$stateParams'];
  /* @ngInject */
  function appRun(routerHelper) {
    var otherwise = '/404';
    routerHelper.configureStates(getStates(), otherwise);
  }

  function getStates() {
    return [
    {
      state: 'dashboard.clients.client',
      config: {
        url: '/:idCli',
        views: {
          '@': {
            templateUrl: 'app/clients/client/client.html',
            controller: 'ClientController',
            controllerAs: 'vm'
          }
        },
        resolve: {
          clientPrepService: clientPrepService
        },
        title: 'Client',
        ncyBreadcrumb: {
          label: '{{vm.idCli}}'
        }
      }
    }
    ];
  }

  function clientPrepService(dataservice, $stateParams) {
    return dataservice.sendRequest('get','clients/idReient/' + $stateParams.idCli);
  }
})();

Again my client.route.js, should i do something more to explain in my clientPrepService if the returned data is empty to do a $state.go('404') ? Note that the url is url: '/:idCli'

I just confirmed that the clientPrepService is called before my controller so it looks like it's pretty ok for the code structure. And even if i do a 404 the route go to the controller

Im a bit lost atm

ptitdje45 commented 7 years ago

      function handleRoutingErrors() {
        // Route cancellation:
        // On routing error, go to the dashboard.
        // Provide an exit clause if it tries to do it twice.
        $rootScope.$on('$stateChangeError',
          function(event, toState, toParams, fromState, fromParams, error) {
            if (handlingStateChangeError) {
              return;
            }
            stateCounts.errors++;
            handlingStateChangeError = true;
            var destination = (toState &&
              (toState.title || toState.name || toState.loadedTemplateUrl)) ||
              'unknown target';
            console.log('destination :' + destination + '!');
            var msg = 'Error routing to ' + destination + '. ' +
              (error.data || '') + '. <br/>' + (error.statusText || '') +
              ': ' + (error.status || '');
            logger.warning(msg, [toState]);
            $location.path('/dev/build');
          }
        );
      }

The route-helper from John papa

Anber commented 7 years ago

If the resolve return a bad request 204 / 404 etc.. I go directly to the 404.html

No. Resolve returns promise. If a http request returns 204, promise resolves with an empty payload. If a request returns 404, promise rejects. I don't know what is happening in your dataservice, but it looks like a simple wrapper for $http with default behavior. If I correctly understood you, we have three possible options:

  1. 200 OK with client;
  2. 204 with empty body;
  3. 404 and rejected promise.

You can merge 2nd and 3rd options to one: just add .catch(() => null) to sendRequest response. After this, you can catch empty client in onEnter hook.

P.S. You should use either $inject nor @ngInject otherwise your function will be annotated twice.

ptitdje45 commented 7 years ago

Yeah the problem is coming from my dataservice which allow everything and just return ''; if the code returneed by the api is '204' or '404' etc...

So i'm working on the dataservice to improve that.

Here is my actual .then() for the $http request

 function success(response) {
        if (typeof response.data.error !== 'undefined') {
          if (response.data.error.code === 404 || response.data.error.code === 204) {
            return exception.catcher('Request failed for ' + type + ' : ' + mainURL + path)(response);
          }
        }
        else {
          return response.data;
        }
      }

Works perfect but i'm forced to stop showing my loggers because there is too much view that does a 204 ( success but nothing to give back ). I don't really get ur .catch idea. Can you explain a little bit more ? :) Thank you by far @Anber

Anber commented 7 years ago

What is exception.catcher? Success callback isn't called for errors. You should use a second argument of then for suppress all errors and return null-client (https://docs.angularjs.org/api/ng/service/$q).

$http.get('…').then(response => response.data).catch(() => null)
or
$http.get('…').then(response => response.data, () => null)

Then you can do something like this

  …
  resolve: {
    clientPrepService: clientPrepService,
  },
  onEnter: /* @ngInject */ function(clientPrepService){
    if (!clientPrepService) $state.go(...); // redirect to 404
  }
  …
ptitdje45 commented 7 years ago
(function() {
  'use strict';

  angular
    .module('blocks.exception')
    .factory('exception', exception);

  /* @ngInject */
  function exception($q, logger) {
    var service = {
      catcher: catcher
    };
    return service;

    function catcher(message) {
      return function(e) {
        var thrownDescription;
        var newMessage;
        if (e.data && e.data.description) {
          thrownDescription = '\n' + e.data.description;
          newMessage = message + thrownDescription;
        }
        e.data.description = newMessage;
        logger.error(newMessage);
        return $q.reject(e);
      };
    }
  }
})();

This is my exeption catcher that return the $q.reject.

ptitdje45 commented 7 years ago

Success callback isn't called for errors.

In my case, all $http request are success, that's why im doing the $q.reject in my success. I need that when i got an empty $http response, that's not going into exception.catcher, except when i do my routes

Anber commented 7 years ago

Anyway you can add .catch(() => null) after your then and check client in onEnter hook.

ptitdje45 commented 7 years ago

Got it, thank you for ur patiente ! Perfect !!! :) love the way that you easely implement in es6.