Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular UI router doesn't process the resolve function when i use async/await feature?

I've been trying to render certain template related to a state and component according to this article

In my project running under dev-server it all works fine and when I execute $state.go("home") the component template is loaded how I expect but when I do this in a testing environment, this doesn't work.

Before, in testing, when I use the "old way" using "template" instead "component" with ui-router, execute $rootScope.$digest() was enough for add the template inside the <div ui-view></div> but using this new way this doesn't work anymore.

What am I doing wrong?

Edit: I've been trying to deeply understand the problem and I see that the problem is related to the HTTP request that was done. Maybe it's related to the way that my promise resolves on the resolve callback using async/await. Please check the Service:

Service

export class TodoService {      constructor($http, BASE_URL) {          this.http = $http;          this.url = `${BASE_URL}/todos`      }      async getTodos() {          const apiResponse = await this.http.get(this.url)          return apiResponse.data.todos      }  }

Router

import '@uirouter/angularjs'    export function routes($stateProvider, $locationProvider) {      $locationProvider.html5Mode({          enabled: true,          requireBase: false,          rewriteLinks: true,      })        $stateProvider          .state("home", {              url: "/",              component: "todoList",              resolve: {                  todosList: TodoService => TodoService.getTodos()              }          })  }

Test

import { routes } from "routes"  import { TodoListComponent } from "components/todoList.component"  import { TodoService } from "services/todo.service"    describe("TodoListComponent rendering and interaction on '/' base path", () => {      let componentDOMelement      let stateService        beforeAll(() => {          angular              .module("Test", [                  "ui.router"              ])              .config(routes)              .constant("BASE_URL", "http://localhost:5000/api")              .component("todoList", TodoListComponent)              .service("TodoService", TodoService)              //I enable this for better logs about the problem              .run(['$rootScope','$trace', function($rootScope, $trace) {                 $trace.enable("TRANSITION")               }])      })      beforeEach(angular.mock.module("Test"))        beforeEach(inject(($rootScope, $compile, $state, $httpBackend) => {          //build the scene          //1st render the root element of scene: We needs a router view for load the base path          let scope = $rootScope.$new()          componentDOMelement = angular.element("<div ui-view></div>")            $compile(componentDOMelement)(scope)          scope.$digest()                     document.body.appendChild(componentDOMelement[0]) //This is a hack for jsdom before the $rootScope.$digest() call          //2nd let's create a fake server for intercept the http requests and fake the responses          const todosResponse = require(`${__dirname}/../../stubs/todos_get.json`)          $httpBackend              .whenGET(/.+\/todos/)              .respond((method, url, data, headers, params) => {                  return [200, todosResponse]              })            //3rd Let's generate the basic scenario: Go at home state ("/" path)          $state.go("home")          $rootScope.$digest()          $httpBackend.flush()      }))        it("Should be render a list", () => {          console.log("HTML rendered")          console.log(document.querySelectorAll("html")[0].outerHTML)      })  })

The HTML result that not rendering

<html>  <head>  <style type="text/css">  @charset "UTF-8";[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate) {    display:none !important;  }  ng\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{    position:absolute;  }  </style>  </head>  <body><!-- uiView: -->  </body>  </html>

Also, I traced the stateChange before the HTML:

console.log node_modules/@uirouter/core/_bundles/ui-router-core.js:1276     Transition #0-0: Started  -> "Transition#0( ''{} -> 'home'{} )"  console.log node_modules/@uirouter/core/_bundles/ui-router-core.js:1282     Transition #1-0: Ignored  <> "Transition#1( ''{} -> 'home'{} )"  console.log node_modules/@uirouter/core/_bundles/ui-router-core.js:1313     Transition #1-0: <- Rejected "Transition#1( ''{} -> 'home'{} )", reason: Transition Rejection($id: 0 type: 5, message: The transition was ignored, detail: "undefined") 

I see a problem in a transition but no reason was given.

========================================================================

Edit 2 Finally we found the problem but I can't figure out the real problem. I created a branch in my project for showing the problem. This it's related to async/await javascript feature:

export class TodoService {     constructor($http, BASE_URL) {         this.http = $http;         this.url = `${BASE_URL}/todos`     }     //Interchange the comment on the getTodos method and run `npm run tdd` for see the problem:     //When async/await doesn't used, the html associated to the resolve in the     // "/" route that used this service, the promise was resolved that expected.     //The idea for this branch it's research about the problem and propose a way     //for we can use async/await on the production code and on the testing environment     async getTodos() {         const apiResponse = await this.http.get(this.url)         return apiResponse.data.todos     }     // getTodos() {     //     return this.http.get(this.url).then(res => res.data.todos)     // } } 

The repository

So my new Questions are:

  • Why the way that I use the async/await functionality it's not compatible with the ui-router resolve on testing environment but in production code it works?
  • Maybe it's related to $httpBackend.flush() call?

Edit 3 The issue 3522 reported in angular UI router repository

like image 445
Gonzalo Pincheira Arancibia Avatar asked Aug 24 '17 13:08

Gonzalo Pincheira Arancibia


1 Answers

The issue is that angular expects an angular Promise that's why your then will work but your await won't, you can solve this by using a library like: https://www.npmjs.com/package/angular-async-await or make a construction like they're demonstrating here https://medium.com/@alSkachkov/using-async-await-function-in-angular-1-5-babel-6-387f7c43948c

Good luck with your problem!

like image 164
jovi De Croock Avatar answered Sep 24 '22 21:09

jovi De Croock