0

I'm working on a very modularized project and currently I'm building an Element Directive which changes templateUrl based on user login/logout.

To do that, I'm trying to execute a Factory's Function inside templateUrl. That particular functions calls another method from a JWT Factory and returns true if the user is logged or false if not.

Then, If in my templateUrl I receive true, I pick a certain url, if false another one.

But, sadly, I receive the following error:

[$http:badreq] Http request configuration url must be a string. Received: {}

All $log.log() print the correct result.

Of course, it won't render nor page1 nor page2

Directive

(function () {
  'use strict';

  angular
    .module('myApp')
    .directive('myDirective', ['SessionCheckerFactory',  function (SessionCheckerFactory) {

      return {
        restrict: 'E',
        templateUrl : function(){
          return SessionCheckerService.checkSession().then( function (res) {
            console.log(res);//true
            return res ? 'app/page1.html' : 'app/page2.html';
      });
        },
        controller       : 'MyController',
        controllerAs     : 'myCtrl',
        bindToController : true

      };
    }]);
})();

SessionCheckerFactory

(function () {
  'use strict';

  angular
    .module('myApp')
    .factory('SessionCheckerFactory', function (AuthTokenFactory) {

      function checkSession() {
          return AuthTokenFactory.isAuth();
      }          

      return {
        checkSession: checkSession
      }

    });
})();

AuthTokenFactory

(function() {
  'use strict';

  angular.module('myApp')
  .factory('AuthTokenFactory', function AuthTokenFactory(store, $cookies) {

      //Takes user's info from LocalStorage, if not empty returns a String with encoded string informations
      function getToken() {
       if (store.get(key)) {
         return store.get(key);
       }
        //Takes user's info from cookie
        var token = $cookies.get('token', {path: '/'});
        store.set(key, token);

        return token;
      }

      //If getToken is empty returns false, else true
      function isAuth() {
        return Promise.resolve(Boolean(getToken()));
      }

      return { 
              isAuth   : isAuth,
              getToken : getToken
      }
 });
})();

I read around that this problem is usually generated by $http requests, but that's not my case. I didn't find any solution to that so far.

How can I fix this?

Thanks in advance.

AndreaM16
  • 3,917
  • 4
  • 35
  • 74
  • I don't think it's related to your problem, but you should [avoid the `Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Sep 06 '16 at 07:44
  • What is `getToken`? – Bergi Sep 06 '16 at 07:44
  • Those IIFEs look pretty useless. – Bergi Sep 06 '16 at 07:45
  • @Bergi I need IFES to check which `url` I have to load. `getToken` checks if the suser exists in `LocalStorage` and `cookies`. It works. The problem is caused by `async tasks`. I'll give a look to the link you shared. Thanks. – AndreaM16 Sep 06 '16 at 07:47
  • I don't mean the `if`s, I mean the IIFEs around your modules. Drop them and put the `"use strict"` inside the directive/factories. – Bergi Sep 06 '16 at 08:00
  • Ok, didn't get it before. Tried but It does not seem to be the problem. I read answers to the link you sent but I don't find errors in my promises. If you find some please tell me. – AndreaM16 Sep 06 '16 at 08:04
  • Not related to the problem indeed, but better code nonetheless. It should read simply `function checkSession() { return AuthTokenFactory.isAuth(); }`, `function isAuth() { return Promise.resolve(Boolean(getToken())); }` and `function templateUrl() { return SessionCheckerFactory.checkSession().then( function (res) { return res ? "app/page1.html" : "app/page2.html"; }); }` – Bergi Sep 06 '16 at 08:55

2 Answers2

1

Then, If in my templateUrl I receive true, I pick a certain url, if false another one.

Actually you don't. If you receive true, you pick one url, if some truthy value, another url, and if something falsy then you don't pick any url:

 if (res) {
     if (res === true) {
         return resolve('app/page1.html');
     } // else
         return resolve('app/page2.html');
 }
 // else return undefined;

You probably want

templateUrl : function(){
    return SessionCheckerFactory.checkSession().then(function (res) {
        if (res) {
            return 'app/page1.html';
        } else {
            return 'app/page2.html';
        }
    })
},
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for your answer, I tried your solution but I get the same error. It seems like the error is generated when doing the resolve on `isAuth()`. I removed that, replacing it with the code you suggested but I get the same error. – AndreaM16 Sep 06 '16 at 09:07
  • What happens to the `templateUrl`? Are you sure there are no $http requests in your app? – Bergi Sep 06 '16 at 09:12
  • On that specific piece of code I have no `$http` requests. I can share what `getToken()` does. – AndreaM16 Sep 06 '16 at 09:13
  • So where does the error come from then? What is its stack trace, where do you catch it? Maybe `MyController` tries to fetch the template from the url? – Bergi Sep 06 '16 at 09:17
  • I updated the question. 1 - Error comes immediately after the checkSession`.then()`, even if it higlights `res = true` in debugging mode. 2 - I checked and the `Controller` gets called after that machinery. It may be due to that `templateUrl` goes in timeout before the return? – AndreaM16 Sep 06 '16 at 09:27
  • Your original code did go into a timeout, yes, your current one should not. However I don't see any reason to use promises in your `checkSession` code, it seems to be entirely synchronous? – Bergi Sep 06 '16 at 10:07
0

I managed to fix the issue using a link function and $templateRequest

Directive

link: function (scope, element) {
      SessionCheckerService.renderTemplate().then(function (temp){
        $templateRequest(temp).then(function (requestedTemplate) {
          element.html(requestedTemplate);
          $compile(element.contents())(scope);
        });
      });
    }

Factory

var templateConfig = './app/config/templates.config.json';

function getTemplate(){
  return $http.get(templateConfig)
    .then(function(templates) {
      return templates.data;
    });
}

function checkSession() {
  return Promise.resolve(AuthTokenFactory.isAuth());
}

function whichTemplate(template, result) {
  var myTemplate = '';
  if(result){
     myTemplate = template.logIn;
  } else {
     myTemplate = template.logOut;
  }
    if(myTemplate){
       return Promise.resolve(myTemplate);
    }

 }

//Chaining the methods and returning the correct template
function renderTemplate() {
  return new Promise(function (resolve) {
     checkSession().then(function(isAuth){
       getTemplate().then( function(templates){
         whichTemplate(templates, isAuth).then( function (temp) {
           return resolve(temp);
         });
       });
     });
   });
 }

 return {
   renderTemplate : renderTemplate
 }

Templates Config

{
  "logOut" : "app/page1.html",
  "logIn"  : "app/page2.html"
}

I hope It'll be helpful.

AndreaM16
  • 3,917
  • 4
  • 35
  • 74