Создание веб-приложения SPA на Rust

Я хотел создать SPA (одностраничное приложение), использующее только файлы ржавчины, нет. JavaScript или же HTML файлы.

Я понял это, заметив, что WASM код должен вызываться из JS с использованием fetchтак что хотя может использовать XHR или же Fetch может использоваться для вызова необходимой функциональности из Rust а пользовательский интерфейс форм/страниц можно назвать html ответ тоже.

У меня было 2 проблемы, а именно:

  1. Необходимость отправки данных формы в виде json, поэтому я создал для этого глобальную функцию, чтобы избежать многократного кодирования одних и тех же строк.
  2. Загрузка первой страницы, так как до этого ничего нельзя сделать window.loaded так что даже первая страница была загружена при получении ответа XHR

Итак, я закодировал ниже для:

  1. Создание глобальной функции toJSONString(form),
  2. Вызов '/first' Пользовательский интерфейс и отображать его
#[get("/")]
fn index() -> content::Html<&'static str> { content::Html(r#" <script> (function (root, factory) { if ( typeof define === 'function' && define.amd ) { define([], factory(root)); } else if ( typeof exports === 'object' ) { module.exports = factory(root); } else { root.oryxPlugin = factory(root); } })(typeof global !== "undefined" ? global : this.window || this.global, function (root) {
; var oryxPlugin = {}; oryxPlugin.toJSONString = function(form){ var obj = {}; var elements = form.querySelectorAll( "input, select, textarea" ); for( var i = 0; i < elements.length; ++i ) { var element = elements[i]; var name = element.name; var value = element.value; if( name ) { obj[name] = value; } } return JSON.stringify( obj ); }; return oryxPlugin; }); var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { var s = document.createElement('script'); s.type="text/javascript"; if (this.readyState == 4 && this.status == 200) { s.appendChild(document.createTextNode(this.responseText)); document.body.appendChild(s); } }; xhr.open("GET", '/first'); xhr.send(); </script> "#)
}

И закодировал ниже как '/first' загрузка пользовательского интерфейса страницы, в которой я разделил страницу на 2 части:

  1. заголовок, который будет использоваться для меню и так далее
  2. контекст, быть empty а также refilled с каждым новым содержимым страницы
fn first() -> (content::Html<&'static str>) { content::Html(r#" var hdr = document.createElement("div"); hdr.id = 'header' hdr.innerHTML= ` <h1>Welcome to my app</h1><br> <button id = 'btn'>load second screen</button> `; hdr.querySelector(' var context = document.querySelector('#context') var xhr = new XMLHttpRequest(); xhr.open("GET", '/second'); xhr.onreadystatechange = function() { var s = document.createElement('script'); s.type="text/javascript"; if (this.readyState == 4 && this.status == 200) { s.appendChild(document.createTextNode(this.responseText)); document.body.appendChild(s); } }; xhr.send(); } document.body.appendChild(hdr); var context = document.createElement("div"); context.id = 'context' var form = document.createElement("form"); var button = document.createElement("button"); form.innerHTML =` <input type="text" name="fname" /> <input type="text" name="lname" /> ` button.onclick = function(){ var dataContainer = oryxPlugin.toJSONString(form); console.log(dataContainer); var xhr = new XMLHttpRequest(); xhr.open("POST", '/call', true); xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var obj = JSON.parse(this.responseText); console.log('Returned string is: ' + obj.fname + ', ' + obj.lname); } }; xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.send(dataContainer); }; button.innerHTML ='Click HERE' context.appendChild(<h5>This is the first screen</h5><br>`) context.appendChild(form) context.appendChild(button) document.body.appendChild(context); "#)
}

Каждая страница после first страница, может быть закодирована аналогично приведенному ниже, где содержимое context элемент постоянно обновляется:

#[get("/second")]
fn second() -> (content::Html<&'static str>) { content::Html(r#" var div = document.createElement("div"); div.innerHTML= ` <h5>This is the second screen</h5><br> `; while (context.hasChildNodes()) { context.removeChild(context.lastChild); } context.appendChild(div); "#)
}

Обрабатывать json получено/отправлено, ниже код для выполнения обоих шагов:

struct Name { fname: String, lname: String,
} fn call(name: Json<Name>) -> Json<Name> { let user = &name; println!("Form field is: {:?} ", user); dbg!(user); println!("Fist name: {0}, Family name: {1}", user.fname, user.lname); let x = Name{ fname : name.fname.to_owned(), lname : name.lname.to_owned() }; Json(x)
}

Это было сделано с помощью rocket.rsИтак main.rs заголовок должен быть:

use rocket_contrib::json::Json; use rocket::response::content;

И маршруты должны быть определены как:

fn main() { rocket::ignite() .mount("/", routes![index, call, first, second]) .launch();
}

Не забыть Cargo.toml является:

[package]
name = "workshop"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"

[dependencies]
rocket = "0.4.0"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"

[dependencies.rocket_contrib]
version = "0.4.0"
default-features = false
features = ["json"]

Nightly требуется ржавчина rocket.rs приложения до сих пор.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *