简体   繁体   English

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

[英]Aurelia JS - Making a synchronous HTTP request, to change data before page load?

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. 单击此按钮后,应发出一个Web请求,该请求应返回JSON联系人数据。 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); 原始联系人列表应用的副本可以在https://gist.run/?id=c73b047c8184c052b4c61c69febb33d8 (仅适用于Chrome)上找到; 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)? ). 这是我尝试做的-首先,对开始按钮进行了更改(在Aurelia JS中也是如此-无法通过单击导航父级(未找到路由)? )。 * Then, in web-api.js there is a new setContactList function which should allow for change of the data container variable. *然后,在web-api.js有一个新的setContactList函数,该函数应允许更改数据容器变量。 * After the start "click me" button is clicked, app-clist.* is loaded. *单击开始“单击我”按钮后,将加载app-clist.* 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). 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的对象第一次加载(即在单击“开始”按钮之后)。 If this web call succeeds, then setContactList is called to change the contact list. 如果此Web调用成功,则将调用setContactList来更改联系人列表。

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 - ie they will just start the process, and will not block the rest of the code until they complete. 这提醒我JavaScript中的HTTP调用通常是异步的-即它们将仅启动该过程,并且在完成之前不会阻塞其余代码。 Likely that is the case for aurelia-http-client , from where I used: 我使用过的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);
 });

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

So my question is: how can I do a synchronous HTTP call with Aurelia JS? 所以我的问题是:如何使用Aurelia JS进行同步HTTP调用? 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 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 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(/(?:\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 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 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 btn-start.html

<template>
  <div id="startbtn" click.trigger="goClist()">Click here to start!</div>
</template>

btn-start.js 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 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; 好吧,我终于设法进行了一个异步调用,该更新按照@LStarky的建议更新了GUI。 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. 请注意,为此,必须确保ContactList类是单个实例类,以便只有一个属性contacts的绑定会更新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. ...因此,基本上,在两次警报之后,显示的联系人将使用从PHP页面获取的数据进行更新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM