![from from](https://c1.lmlphp.com/image/static/default_avatar.gif)
本文介绍了Aurelia JS - 发出同步 HTTP 请求,在页面加载前更改数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在使用联系人列表教程:
... 我想改变它,所以应用程序首先从点击我"按钮开始.单击此按钮后,应发出 Web 请求,该请求应返回 JSON 联系人数据.如果请求成功,响应应该更新联系人的主数据存储,页面应该开始呈现新的联系人列表;如果请求失败,页面应显示原始(硬编码)联系人列表.
可以在 https://gist.run/?id= 上找到原始联系人列表应用的副本c73b047c8184c052b4c61c69febb33d8(目前仅适用于 Chrome);而我为实现上述所做的更改在:
这是我尝试做的 - 首先,开始按钮有变化(也在 Aurelia JS - 无法通过单击导航路由父级(未找到路由)?).* 然后,在 web-api.js
中有一个新的 setContactList
函数,它应该允许更改数据容器变量.* 点击开始点击我"按钮后,加载app-clist.*
.在 app-clist.js
中,有构建新联系人列表的 PHP 代码,由于我没有简单的方法上传和运行服务器 PHP 代码,我将该 PHP 代码发送到 http://phpfiddle.org 处理它并返回结果(另见 https://softwarerecs.stackexchange.com/questions/39075/web-service-for-sharing-and-serving-php-code/39078#39078) 这是在 constructor()
函数中完成的,所以这是 app-clist.js
中的第一次对象代码> 加载(这是在单击开始按钮之后).如果此网络调用成功,则调用 setContactList
以更改联系人列表.
所以,问题来了 - 网络调用都成功了,但它们发生得太晚了 - 在页面呈现之后.单击开始按钮后,首先呈现页面(带有旧联系人);然后我收到警报:
gist.host 上的一个嵌入页面说:{"result":"[{"id":"1","firstName":"Bob","lastName":"Glass","email":"bob@glass.com","phoneNumber":"243-6593"},{"id":"2","firstName":"Chad","lastName":"Connor","email":"chad@connor.com","phoneNumber":"839-2946"}]"}
...这意味着网络调用成功,然后是第二个警报:
gist.host 上的一个嵌入页面说:设置联系人列表 2 2
... 说明接收到的contacts数组的长度和原来的一样,说明发生了更新.只是,它发生的太晚了.
这让我想起 JavaScript 中的 HTTP 调用往往是异步的 - 即它们只会启动进程,并且在它们完成之前不会阻塞其余的代码.我使用的 aurelia-http-client
可能就是这种情况:
this.http.createRequest('https://phpfiddle.org/api/run/code/json').asPost().withHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8').withContent("code="+encphpcode).发送().then(响应 => {警报(响应.响应);控制台日志(响应);var respobj = JSON.parse(response.response);var respdataArr = JSON.parse(respobj.result);this.api.setContactList(respdataArr);}).catch(err => {控制台日志(错误);});
所以对于我的概念 - 我在页面生命周期开始时调用服务,该服务返回应该在第一次显示时呈现在页面上的数据 - 我必须有一个同步调用,这会阻塞 app-clist
中的 constructor()
的执行,直到它完成(成功或失败),以便在页面渲染开始之前更新数据...
所以我的问题是:如何使用 Aurelia JS 进行同步 HTTP 调用?或者,是否可以使用异步调用来实现类似于我在此处的示例,如果可以,如何实现?
以下是一些更相关的文件供参考:
app-clist.html
<require from="bootstrap/css/bootstrap.css"></require><require from="./styles.css"></require><require from="./contact-list"></require><nav class="navbar navbar-default navbar-fixed-top" role="navigation"><div class="navbar-header"><a class="navbar-brand" href="#"><i class="fa fa-user"></i><span>联系人</span></a>
</nav><div class="容器"><div class="row"><contact-list class="col-md-4"></contact-list><router-view name="chldrt" class="col-md-8"></router-view>
app-clist.js
import {WebAPI} from './web-api';从'aurelia-http-client'导入{HttpClient};//对于多行字符串,使用反引号 `` - ES6 模板文字.让 phpcode = `<?php$outarr = array();$tObj = new StdClass();$tObj->{'id'} = '1';$tObj->{'firstName'} = 'Bob';$tObj->{'lastName'} = '玻璃';$tObj->{'email'} = 'bob@glass.com';$tObj->{'phoneNumber'} = '243-6593';array_push($outarr, $tObj);$tObj = new StdClass();$tObj->{'id'} = '2';$tObj->{'firstName'} = 'Chad';$tObj->{'lastName'} = '康纳';$tObj->{'email'} = 'chad@connor.com';$tObj->{'phoneNumber'} = '839-2946';array_push($outarr, $tObj);回声 json_encode($outarr);?>`;export class AppClist {//在gist中,错误地仍然是App静态注入(){ 返回 [WebAPI,HttpClient];}构造函数(API,http){this.api = api;this.http = http;var phpcodesl = phpcode.replace(/(?:
|
|
)/g, ' ');var encphpcode = encodeURIComponent(phpcodesl);//网址编码//警报(encphpcode);//注意:gist.run 由于 https 将不允许从 http 加载//this.http.post("https://phpfiddle.org/api/run/code/json", "code="+encphpcode )//.then(response => {alert(response.response); console.log(response);})//不起作用//这确实有效:this.http.createRequest('https://phpfiddle.org/api/run/code/json').asPost().withHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8').withContent("code="+encphpcode).发送().then(响应 => {警报(响应.响应);控制台日志(响应);var respobj = JSON.parse(response.response);var respdataArr = JSON.parse(respobj.result);this.api.setContactList(respdataArr);}).catch(err => {控制台日志(错误);});}//没有 configureRouter(config, router){ 这里和 app.js 一样!/**/configureRouter(配置,路由器){config.title = '联系人';config.map([//必须在此处包含空路由 '',否则在开始时找不到路由"{ route: ['','contacts'], viewPorts: { chldrt: { moduleId: 'no-selection' } }, title: 'Select'},{ 路线:'contacts/:id',viewPorts:{chldrt:{moduleId:'contact-detail'}},名称:'contacts'}]);this.router = 路由器;}}
app.html
<require from="bootstrap/css/bootstrap.css"></require><require from="./styles.css"></require><require from="./contact-list"></require><loading-indicator loading.bind="router.isNavigating || api.isRequesting"></loading-indicator><router-view name="mainrt"></router-view>
app.js
import {WebAPI} from './web-api';出口类应用{静态注入(){ 返回 [WebAPI];}构造函数(API){this.api = api;}配置路由器(配置,路由器){config.title = '应用通讯录';config.map([{ route: '', viewPorts: { mainrt: { moduleId: 'btn-start' } }, title: 'Start'},{ route: 'app-clist', viewPorts: { mainrt: { moduleId: 'app-clist' }, chldrt: { moduleId: 'no-selection' } }, name: 'app-clist', title: 'C List'}//,//{ route: 'contacts', viewPorts: { chldrt: { moduleId: 'no-selection' } }, title: 'Select'},//{ route: 'contacts/:id', viewPorts: { chldrt: { moduleId: 'contact-detail' } }, name:'contacts' }]);this.router = 路由器;}}
btn-start.html
<div id="startbtn" click.trigger="goClist()">点击这里开始!</div>
btn-start.js
import {WebAPI} from './web-api';从'aurelia-router'导入{路由器};从'./app'导入{App};导出类 BtnStart {静态注入(){返回[WebAPI,路由器,应用程序];}构造函数(API,路由器,应用程序){this.api = api;this.router = 路由器;this.app = 应用程序;}goClist() {this.app.router.navigateToRoute("app-clist");}}
web-api.js
让延迟 = 200;让id = 0;函数 getId(){返回++id;}让联系人 = [{id:getId(),firstName:'约翰',lastName:'托尔金',电子邮件:'tolkien@inklings.com',电话号码:'867-5309'},{id:getId(),firstName:'克莱夫',lastName:'刘易斯',电子邮件:'lewis@inklings.com',电话号码:'867-5309'},{id:getId(),firstName:'欧文',lastName:'巴菲尔德',电子邮件:'barfield@inklings.com',电话号码:'867-5309'},{id:getId(),firstName:'查尔斯',lastName:'威廉姆斯',电子邮件:'williams@inklings.com',电话号码:'867-5309'},{id:getId(),firstName:'罗杰',lastName:'绿色',电子邮件:'green@inklings.com',电话号码:'867-5309'}];导出类 WebAPI {isRequesting = false;setContactList(incontacts) {联系人 = 不联系;alert("setContactList " + incontacts.length + " " + contacts.length);console.log("setContactList", incontacts, contacts);}getContactList(){this.isRequesting = true;返回新的承诺(解决 => {setTimeout(() => {让结果 =contacts.map(x => { return {id:x.id,名字:x.firstName,姓氏:x.lastName,电子邮件:x.email}});解决(结果);this.isRequesting = false;}, 潜伏);});}getContactDetails(id){this.isRequesting = true;返回新的承诺(解决 => {setTimeout(() => {让找到=contacts.filter(x => x.id == id)[0];解析(JSON.parse(JSON.stringify(found)));this.isRequesting = false;}, 潜伏);});}保存联系(联系){this.isRequesting = true;返回新的承诺(解决 => {setTimeout(() => {让实例 = JSON.parse(JSON.stringify(contact));让 found = contacts.filter(x => x.id == contact.id)[0];如果(找到){让 index =contacts.indexOf(found);联系人[索引] = 实例;}别的{instance.id = getId();接触.推(实例);}this.isRequesting = false;解决(实例);}, 潜伏);});}}
解决方案
好吧,我终于按照@LStarky 的建议成功地进行了更新 GUI 的异步调用;请注意,为了做到这一点,必须确保 ContactList
类是单个实例类,以便只有一个属性 contacts
其绑定更新 HTML GUI.
然而,由于所有这些都有点像黑客猜测,所以从某人那里得到正确的答案仍然很好.
描述的单实例问题:
作为参考,我在此处将工作示例保存为 gist.run:
...所以基本上,在两次警报之后,显示的联系人将使用从 PHP 页面获得的数据进行更新.
I'm working with the contact list tutorial:
... and I wanted to change it, so the application first starts with a "click me" button. Upon click on this button, a web request should be made, which should return JSON contact data. If the request is successful, the response should update the main data store of contacts, and the page should start with rendering the new contact list; if the request fails, the page should show the original (hardcoded) list of contacts.
Copy of the original contact list app can be found on https://gist.run/?id=c73b047c8184c052b4c61c69febb33d8 (Chrome only for now); while the changes I've made to implement the above are in:
This is what I tried doing - first, there are the changes for the start button (also in Aurelia JS - cannot navigate route parent with click (Route not found)?).* Then, in web-api.js
there is a new setContactList
function which should allow for change of the data container variable.* After the start "click me" button is clicked, app-clist.*
is loaded. In app-clist.js
, there is PHP code that constructs a new contact list, and since I have no easy way of uploading and running server PHP code, I send that PHP code to http://phpfiddle.org which processes it and returns the results (see also https://softwarerecs.stackexchange.com/questions/39075/web-service-for-sharing-and-serving-php-code/39078#39078) This is done in the constructor()
function, so the very first time the object in app-clist.js
loads (which is after the start button is clicked). If this web call succeeds, then setContactList
is called to change the contact list.
So, here is the problem - the web calls all succeed, but they happen way too late - after the page has been rendered. After clicking the start button, first the page (with old contacts) is rendered; then I get an alert:
An embedded page at gist.host says:
{"result":"[{"id":"1","firstName":"Bob","lastName":"Glass","email":"bob@glass.com","phoneNumber":"243-6593"},{"id":"2","firstName":"Chad","lastName":"Connor","email":"chad@connor.com","phoneNumber":"839-2946"}]"}
... which means the web call succeeded, and then a second alert:
An embedded page at gist.host says:
setContactList 2 2
... which shows that the lengths of the received contacts array and the original one are the same, which means the update happened. Except, it happened to late.
This reminded me that HTTP calls in JavaScript tend to be asynchronous - i.e. they will just start the process, and will not block the rest of the code until they complete. Likely that is the case for aurelia-http-client
, from where I used:
this.http.createRequest('https://phpfiddle.org/api/run/code/json')
.asPost()
.withHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
.withContent("code="+encphpcode)
.send()
.then(response => {
alert(response.response);
console.log(response);
var respobj = JSON.parse(response.response);
var respdataArr = JSON.parse(respobj.result);
this.api.setContactList(respdataArr);
}).catch(err => {
console.log(err);
});
So for my concept - that I'm calling a service at the start of the lifetime of a page, which returns data that should be rendered on the page on first show - I'd have to have a synchronous call, which would block the execution of the constructor()
in app-clist
until it is complete (succeeds or fails), so that the data can be updated before the page rendering starts...
So my question is: how can I do a synchronous HTTP call with Aurelia JS? Alternatively, is it possible to something like my example here with asynchronous calls, and if so, how?
Here are some of the more relevant files for reference:
app-clist.html
<template>
<require from="bootstrap/css/bootstrap.css"></require>
<require from="./styles.css"></require>
<require from="./contact-list"></require>
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="navbar-header">
<a class="navbar-brand" href="#">
<i class="fa fa-user"></i>
<span>Contacts</span>
</a>
</div>
</nav>
<div class="container">
<div class="row">
<contact-list class="col-md-4"></contact-list>
<router-view name="chldrt" class="col-md-8"></router-view>
</div>
</div>
</template>
app-clist.js
import {WebAPI} from './web-api';
import {HttpClient} from 'aurelia-http-client';
// for multiline string, use backticks `` - ES6 template literals.
let phpcode = `
<?php
$outarr = array();
$tObj = new StdClass();
$tObj->{'id'} = '1';
$tObj->{'firstName'} = 'Bob';
$tObj->{'lastName'} = 'Glass';
$tObj->{'email'} = 'bob@glass.com';
$tObj->{'phoneNumber'} = '243-6593';
array_push($outarr, $tObj);
$tObj = new StdClass();
$tObj->{'id'} = '2';
$tObj->{'firstName'} = 'Chad';
$tObj->{'lastName'} = 'Connor';
$tObj->{'email'} = 'chad@connor.com';
$tObj->{'phoneNumber'} = '839-2946';
array_push($outarr, $tObj);
echo json_encode($outarr);
?>
`;
export class AppClist { // in gist, it is mistakenly still App
static inject() { return [WebAPI, HttpClient]; }
constructor(api, http){
this.api = api;
this.http = http;
var phpcodesl = phpcode.replace(/(?:
|
|
)/g, ' ');
var encphpcode = encodeURIComponent(phpcodesl); // urlencode
//alert(encphpcode);
// NOTE: gist.run due https will not allow loading from http
//this.http.post("https://phpfiddle.org/api/run/code/json", "code="+encphpcode )
//.then(response => {alert(response.response); console.log(response);}) // does not work
// this does work:
this.http.createRequest('https://phpfiddle.org/api/run/code/json')
.asPost()
.withHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
.withContent("code="+encphpcode)
.send()
.then(response => {
alert(response.response);
console.log(response);
var respobj = JSON.parse(response.response);
var respdataArr = JSON.parse(respobj.result);
this.api.setContactList(respdataArr);
}).catch(err => {
console.log(err);
})
;
}
// no configureRouter(config, router){ here same as in app.js!
/**/configureRouter(config, router){
config.title = 'Contacts';
config.map([
// must include empty route '' here, else "Route not found" at start
{ route: ['','contacts'], viewPorts: { chldrt: { moduleId: 'no-selection' } }, title: 'Select'},
{ route: 'contacts/:id', viewPorts: { chldrt: { moduleId: 'contact-detail' } }, name:'contacts' }
]);
this.router = router;
}
}
app.html
<template>
<require from="bootstrap/css/bootstrap.css"></require>
<require from="./styles.css"></require>
<require from="./contact-list"></require>
<loading-indicator loading.bind="router.isNavigating || api.isRequesting"></loading-indicator>
<router-view name="mainrt"></router-view>
</template>
app.js
import {WebAPI} from './web-api';
export class App {
static inject() { return [WebAPI]; }
constructor(api) {
this.api = api;
}
configureRouter(config, router){
config.title = 'App Contacts';
config.map([
{ route: '', viewPorts: { mainrt: { moduleId: 'btn-start' } }, title: 'Start'},
{ route: 'app-clist', viewPorts: { mainrt: { moduleId: 'app-clist' }, chldrt: { moduleId: 'no-selection' } }, name: 'app-clist', title: 'C List'} //,
//{ route: 'contacts', viewPorts: { chldrt: { moduleId: 'no-selection' } }, title: 'Select'},
//{ route: 'contacts/:id', viewPorts: { chldrt: { moduleId: 'contact-detail' } }, name:'contacts' }
]);
this.router = router;
}
}
btn-start.html
<template>
<div id="startbtn" click.trigger="goClist()">Click here to start!</div>
</template>
btn-start.js
import {WebAPI} from './web-api';
import { Router } from 'aurelia-router';
import {App} from './app';
export class BtnStart {
static inject() { return [WebAPI, Router, App]; }
constructor(api, router, app) {
this.api = api;
this.router = router;
this.app = app;
}
goClist() {
this.app.router.navigateToRoute("app-clist");
}
}
web-api.js
let latency = 200;
let id = 0;
function getId(){
return ++id;
}
let contacts = [
{
id:getId(),
firstName:'John',
lastName:'Tolkien',
email:'tolkien@inklings.com',
phoneNumber:'867-5309'
},
{
id:getId(),
firstName:'Clive',
lastName:'Lewis',
email:'lewis@inklings.com',
phoneNumber:'867-5309'
},
{
id:getId(),
firstName:'Owen',
lastName:'Barfield',
email:'barfield@inklings.com',
phoneNumber:'867-5309'
},
{
id:getId(),
firstName:'Charles',
lastName:'Williams',
email:'williams@inklings.com',
phoneNumber:'867-5309'
},
{
id:getId(),
firstName:'Roger',
lastName:'Green',
email:'green@inklings.com',
phoneNumber:'867-5309'
}
];
export class WebAPI {
isRequesting = false;
setContactList(incontacts) {
contacts = incontacts;
alert("setContactList " + incontacts.length + " " + contacts.length);
console.log("setContactList", incontacts, contacts);
}
getContactList(){
this.isRequesting = true;
return new Promise(resolve => {
setTimeout(() => {
let results = contacts.map(x => { return {
id:x.id,
firstName:x.firstName,
lastName:x.lastName,
email:x.email
}});
resolve(results);
this.isRequesting = false;
}, latency);
});
}
getContactDetails(id){
this.isRequesting = true;
return new Promise(resolve => {
setTimeout(() => {
let found = contacts.filter(x => x.id == id)[0];
resolve(JSON.parse(JSON.stringify(found)));
this.isRequesting = false;
}, latency);
});
}
saveContact(contact){
this.isRequesting = true;
return new Promise(resolve => {
setTimeout(() => {
let instance = JSON.parse(JSON.stringify(contact));
let found = contacts.filter(x => x.id == contact.id)[0];
if(found){
let index = contacts.indexOf(found);
contacts[index] = instance;
}else{
instance.id = getId();
contacts.push(instance);
}
this.isRequesting = false;
resolve(instance);
}, latency);
});
}
}
解决方案
Well, I finally managed to make an async call which updates the GUI, as recommended by @LStarky; note that in order to do that, one must ensure ContactList
class is a single instance class, so that there is only one property contacts
whose binding updates the HTML GUI.
However, since all of this was a bit of a hacky guesswork, it would still be nice to get a proper answer from someone.
The single instance issue described in:
For reference, I've left the working example saved as a gist.run here:
... so basically, after the two alerts, the shown contacts will get updated with the data obtained from the PHP page.
这篇关于Aurelia JS - 发出同步 HTTP 请求,在页面加载前更改数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!