Aurelia JS-发出同步HTTP请求,以在页面加载之前更改数据吗? [英] Aurelia JS - Making a synchronous HTTP request, to change data before page load?

查看:83
本文介绍了Aurelia JS-发出同步HTTP请求,以在页面加载之前更改数据吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working with the contact list tutorial:

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

... 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.

可以在 https://gist.run/?id= c73b047c8184c052b4c61c69febb33d8 (仅适用于Chrome);而我为实现上述目的所做的更改位于:

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:

这是我尝试做的-首先,对开始按钮进行了更改(也在app-clist.*.在app-clist.js中,有PHP代码构成了一个新的联系人列表,并且由于我没有上载和运行服务器PHP代码的简便方法,因此我将该PHP代码发送到 http://phpfiddle.org 对其进行处理并返回结果(另请参见setContactList来更改联系人列表.

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.

这让我想起JavaScript中的HTTP调用通常是异步的-即它们只会启动该过程,并且在完成之前不会阻塞其余代码.我使用过的aurelia-http-client可能就是这种情况:

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);
 });

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

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...

所以我的问题是:如何使用Aurelia JS进行同步HTTP调用?或者,是否可以通过异步调用来实现类似于我的示例的内容,如果可以,怎么办?

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(/(?:\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:'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);
    });
  }
} 

推荐答案

好吧,我终于设法按照@LStarky的建议进行了一个异步调用来更新GUI.请注意,为此,必须确保ContactList类是单个实例类,以便只有一个属性contacts的绑定会更新HTML GUI.

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.

但是,由于所有这些都有些guess琐,因此从某个人那里获得正确答案仍然很高兴.

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:

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

... so basically, after the two alerts, the shown contacts will get updated with the data obtained from the PHP page.

这篇关于Aurelia JS-发出同步HTTP请求,以在页面加载之前更改数据吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆