【发布时间】:2017-08-24 13:54:24
【问题描述】:
我一直在尝试根据这个article呈现与状态和组件相关的某些模板
在我在 dev-server 下运行的项目中,一切正常,当我执行 $state.go("home") 时,组件模板按我的预期加载,但是当我在测试环境中执行此操作时,这不起作用。
之前,在测试中,当我使用“旧方式”使用“模板”而不是“组件”和 ui-router 时,执行 $rootScope.$digest() 足以在 <div ui-view></div> 内添加模板,但使用这种新方式不再起作用了。
我做错了什么?
编辑:我一直在尝试深入了解问题,发现问题与已完成的 HTTP 请求有关。也许这与我的承诺在使用 async/await 的解析回调上解析的方式有关。请检查服务:
服务
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
}
}
路由器
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()
}
})
}
测试
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)
})
})
未渲染的 HTML 结果
<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>
另外,我在 HTML 之前跟踪了 stateChange:
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")
我在转换中发现了问题,但没有给出原因。
================================================ ==========================
编辑 2
我们终于找到了问题,但我无法找出真正的问题。我在我的项目中创建了一个分支来显示问题。这与async/await javascript 功能有关:
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)
// }
}
所以我的新问题是:
- 为什么我使用 async/await 功能的方式在测试环境中与 ui-router 解析不兼容,但在生产代码中却可以使用?
- 可能与 $httpBackend.flush() 调用有关?
编辑 3 Angular UI 路由器存储库中报告的问题3522
【问题讨论】:
-
听起来问题是
async/await使用原生承诺,但您的组件需要一个 Angular 承诺(就像$http返回的承诺) -
我可能只是猜测。我只说我知道的。 Async/await 功能只是一个实验性功能,在非常有限的运行时支持。 Transplitter 使用状态机来模拟 async/await。这个状态机创建简单的承诺,而不是由 $q 服务创建的扩展。尝试比较在不同环境中运行的实际代码。
-
我注意到您的存储库中没有使用 babel-plugin-transform-async-to-generator。如果你插上它会发生什么?
-
@EduardLepner 使用“transform-regenerator”babel 插件和“stage-3”对于我的生产代码来说已经足够了。我一直在尝试添加 babel-plugin-transform-async-to-generator 但仍然无法正常工作。关于您的另一个响应,如果为运行时创建的承诺不是 $q 服务所期望的承诺,为什么如果我使用我的
npm start脚本运行代码并在浏览器上看到这项工作,为什么会这样?可能是与$httpBackend模拟服务相关的问题,因为这不仅在测试环境中有效? -
我已经提取了您的项目并运行了所有测试。它适用于我的机器。
标签: javascript angularjs angular-ui-router async-await