Aurelia JS - 发出同步 HTTP 请求,在页面加载之前更改数据?

2023-12-27

我正在使用联系人列表教程:

  • http://aurelia.io/hub.html#/doc/article/aurelia/framework/latest/contact-manager-tutorial/1 http://aurelia.io/hub.html#/doc/article/aurelia/framework/latest/contact-manager-tutorial/1

...我想更改它,因此应用程序首先以“单击我”按钮启动。单击此按钮后,应发出 Web 请求,该请求应返回 JSON 联系数据。如果请求成功,响应应更新联系人的主数据存储,并且页面应开始呈现新的联系人列表;如果请求失败,页面应显示原始(硬编码)联系人列表。

原始联系人列表应用程序的副本可以在以下位置找到https://gist.run/?id=c73b047c8184c052b4c61c69febb33d8 https://gist.run/?id=c73b047c8184c052b4c61c69febb33d8(目前仅限 Chrome);而我为实现上述内容所做的更改如下:

  • https://gist.run/?id=90d98563621fe49c1dde6b4f2fc6961d https://gist.run/?id=90d98563621fe49c1dde6b4f2fc6961d

这就是我尝试做的 - 首先,开始按钮发生了变化(也在Aurelia JS - 无法通过单击导航父路由(未找到路由)? https://stackoverflow.com/questions/41910246/aurelia-js-cannot-navigate-route-parent-with-click-route-not-found)。 * 然后,在web-api.js有一个新的setContactList应该允许更改数据容器变量的函数。 * 开始后点击“click me”按钮,app-clist.*已加载。在app-clist.js,有一个 PHP 代码可以构造一个新的联系人列表,并且由于我没有上传和运行服务器 PHP 代码的简单方法,因此我将该 PHP 代码发送到http://phpfiddle.org http://phpfiddle.org它处理它并返回结果(另请参见https://softwarerecs.stackexchange.com/questions/39075/web-service-for-sharing-and-serving-php-code/39078#39078 https://softwarerecs.stackexchange.com/questions/39075/web-service-for-sharing-and-serving-php-code/39078#39078)这是在constructor()函数,所以第一次对象app-clist.js加载(单击开始按钮后)。如果此网络调用成功,则setContactList被调用来更改联系人列表。

所以,问题就在这里——网络调用都成功了,但它们发生得太晚了——在页面渲染之后。单击开始按钮后,首先呈现页面(包含旧联系人);然后我收到警报:

An embedded page at gist.host says:

{"result":"[{\"id\":\"1\",\"firstName\":\"Bob\",\"lastName\":\"Glass\",\"email\":\"[email protected] /cdn-cgi/l/email-protection\",\"phoneNumber\":\"243-6593\"},{\"id\":\"2\",\"firstName\":\"Chad\",\"lastName\":\"Connor\",\"email\":\"[email protected] /cdn-cgi/l/email-protection\",\"phoneNumber\":\"839-2946\"}]"}

...这意味着网络调用成功,然后出现第二个警报:

An embedded page at gist.host says:

setContactList 2 2

...这表明接收到的联系人数组的长度与原始联系人数组的长度相同,这意味着发生了更新。只是,事情发生得太晚了。

这提醒我 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)
 .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);
 });

因此,对于我的概念 - 我在页面生命周期开始时调用一个服务,该服务返回应在首次显示时在页面上呈现的数据 - 我必须进行同步调用,这会阻止的执行constructor() in app-clist直到完成(成功或失败),以便在页面渲染开始之前更新数据...

所以我的问题是:如何使用 Aurelia JS 进行同步 HTTP 调用?或者,是否可以像我这里的示例一样使用异步调用,如果可以,如何实现?

以下是一些比较相关的文件供参考:

应用程序-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>

应用程序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'} = '[email protected] /cdn-cgi/l/email-protection';
$tObj->{'phoneNumber'} = '243-6593';
array_push($outarr, $tObj);
$tObj = new StdClass();
$tObj->{'id'} = '2';
$tObj->{'firstName'} = 'Chad';
$tObj->{'lastName'} = 'Connor';
$tObj->{'email'} = '[email protected] /cdn-cgi/l/email-protection';
$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(/(?:\r\n|\r|\n)/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:'[email protected] /cdn-cgi/l/email-protection',
    phoneNumber:'867-5309'
  },
  {
    id:getId(),
    firstName:'Clive',
    lastName:'Lewis',
    email:'[email protected] /cdn-cgi/l/email-protection',
    phoneNumber:'867-5309'
  },
  {
    id:getId(),
    firstName:'Owen',
    lastName:'Barfield',
    email:'[email protected] /cdn-cgi/l/email-protection',
    phoneNumber:'867-5309'
  },
  {
    id:getId(),
    firstName:'Charles',
    lastName:'Williams',
    email:'[email protected] /cdn-cgi/l/email-protection',
    phoneNumber:'867-5309'
  },
  {
    id:getId(),
    firstName:'Roger',
    lastName:'Green',
    email:'[email protected] /cdn-cgi/l/email-protection',
    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);
    });
  }
} 

好吧,我终于成功地进行了一个更新 GUI 的异步调用,正如 @LStarky 所建议的那样;请注意,为了做到这一点,必须确保ContactListclass 是单实例类,因此只有一个属性contacts其绑定更新了 HTML GUI。

然而,由于所有这些都有点靠猜测,所以从某人那里得到正确的答案仍然是件好事。

单实例问题描述如下:

  • Aurelia - 从注入访问更新的类属性? https://stackoverflow.com/questions/41934488/aurelia-accessing-updated-class-properties-from-injection/41936527#41936527

作为参考,我将工作示例保存为 gist.run :

  • https://gist.run/?id=f4bd01c99f9973cb76d8640f6248c2e3 https://gist.run/?id=f4bd01c99f9973cb76d8640f6248c2e3

...所以基本上,在两次警报之后,显示的联系人将使用从 PHP 页面获取的数据进行更新。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Aurelia JS - 发出同步 HTTP 请求,在页面加载之前更改数据? 的相关文章

随机推荐