web-dev-qa-db-ger.com

Express - Senden Sie eine Seite UND benutzerdefinierte Daten in einer einzigen Anfrage an den Browser?

Wie gleichzeitig eine Seite gerendert und meine benutzerdefinierten Daten an den Browser übermittelt werden. Wie ich es verstanden habe, muss es zwei Ebenen senden: erstens mit Vorlage und zweitens mit JSON-Daten. Ich möchte diese Daten per Backbone behandeln.

Wie ich aus den Tutorials verstehe, interagieren express und bb app wie folgt:

  1. res.render eine Seite an den Browser senden
  2. wenn document.ready jQuery.get zu app.get('/post') auslöst 
  3. app.get('/post', post.allPosts) Daten an Seite senden

Dies sind drei Schritte und wie geht man mit einem Schritt voran?

var visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

exports.index = function(req, res, next){
  res.render('index');
  res.send({data: visitCard}); 
};

Und wie soll ich diese Variable auf der Seite document.card einfangen?

15
khex

Ich habe meine eigene kleine Middleware-Funktion erstellt, die dem renderWithData-Objekt eine Hilfsmethode namens res hinzufügt.

app.use(function (req, res, next) {
    res.renderWithData = function (view, model, data) {
        res.render(view, model, function (err, viewString) {
            data.view = viewString;
            res.json(data);
        }); 
    };
    next();
});

Es enthält den Namen der Ansicht, das Modell für die Ansicht und die benutzerdefinierten Daten, die Sie an den Browser senden möchten. Es ruft res.render auf, übergibt jedoch eine Rückruffunktion. Dadurch wird express angewiesen, das kompilierte Ansichts-Markup als Zeichenfolge an den Rückruf zu übergeben, anstatt es sofort in die Antwort zu leiten. Sobald ich die Ansichtszeichenfolge habe, füge ich sie als data.view dem Datenobjekt hinzu. Dann verwende ich res.json, um das Datenobjekt komplett mit der kompilierten Ansicht an den Browser zu senden :)

Bearbeiten:

Ein Nachteil ist, dass die Anfrage mit Javascript gemacht werden muss, damit es nicht eine vollständige Seitenanfrage sein kann. Sie benötigen eine erste Anfrage, um die Hauptseite aufzurufen, die das Javascript enthält, mit dem die Ajax-Anforderung erstellt wird.

Dies ist ideal für Situationen, in denen Sie versuchen, die Browser-URL und den Titel zu ändern, wenn der Benutzer über AJAX zu einer neuen Seite navigiert. Sie können die Teilansicht der neuen Seite zusammen mit einigen Daten zum Seitentitel zurück an den Browser senden. Dann kann Ihr clientseitiges Skript die teilweise Ansicht auf der Seite platzieren, die Titelleiste der Seite aktualisieren und ggf. die URL aktualisieren.

Wenn Sie ein komplettes HTML-Dokument zusammen mit einigen anfänglichen JavaScript-Daten an den Browser senden möchten, müssen Sie diesen JavaScript-Code in die Ansicht selbst kompilieren. Das ist definitiv möglich, aber ich habe noch nie einen Weg gefunden, der keine Saitenmagie erfordert.

Zum Beispiel:

// controller.js
var someData = { message: 'hi' };
res.render('someView', { data: JSON.stringify(someData) });

// someView.jade
script.
    var someData = !{data};

Hinweis: !{data} wird anstelle von #{data} verwendet, da Jade standardmäßig HTML entgeht, wodurch alle Anführungszeichen in "-Platzhalter umgewandelt werden.

Es sieht auf den ersten Blick WIRKLICH seltsam aus, aber es funktioniert. Im Grunde nehmen Sie ein JS-Objekt auf dem Server, wandeln es in einen String um, rendern diesen String in die kompilierte Ansicht und senden ihn dann an den Browser. Wenn das Dokument schließlich den Browser erreicht, sollte es so aussehen:

// someSite.com/someView
<script type="text/javascript">
    var someData = { "message": "hi" };
</script>

Hoffentlich macht das Sinn. Wenn ich meine ursprüngliche Hilfsmethode neu erstellen würde, um die Schmerzen in diesem zweiten Szenario zu lindern, würde dies ungefähr so ​​aussehen:

app.use(function (req, res, next) {
    res.renderWithData = function (view, model, data) {
        model.data = JSON.stringify(data);
        res.render(view, model);
    };
    next();
});

Alles, was Sie tun müssen, ist Ihr benutzerdefiniertes Datenobjekt zu nehmen, es für Sie zu stringifizieren, es dem Modell für die Ansicht hinzuzufügen und dann die Ansicht als normal darzustellen. Jetzt können Sie res.renderWithData('someView', {}, { message: 'hi' }); aufrufen. Sie müssen lediglich sicherstellen, dass Sie irgendwo in Ihrer Ansicht diese Datenzeichenfolge packen und in eine Anweisung für die Zuweisung von Variablen rendern.

html
    head
        title Some Page
        script.
            var data = !{data};

Nicht lügen, das Ganze fühlt sich ziemlich grob an, aber wenn es Ihnen eine zusätzliche Reise zum Server erspart und das ist es, was Sie suchen, dann müssen Sie es tun. Vielleicht kann sich jemand etwas Geschickteres vorstellen, aber ich sehe nicht, wie Sie sonst Daten erhalten, die bereits in einem vollständigen HTML-Dokument vorhanden sind, das zum ersten Mal gerendert wird.

Edit2:

Hier ist ein Funktionsbeispiel: https://c9.io/chevex/test

Sie benötigen ein (kostenloses) Cloud9-Konto, um das Projekt auszuführen. Melden Sie sich an, öffnen Sie app.js und klicken Sie oben auf die grüne Schaltfläche zum Ausführen.

9
Chev

Mein Ansatz ist, ein Cookie mit den Informationen zu senden und es dann vom Client zu verwenden.

server.js

const visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

router.get('/route', (req, res) => {
  res.cookie('data', JSON.stringify(pollsObj));
  res.render('index');
});

client.js

const getCookie = (name) => {
  const value = "; " + document.cookie;
  const parts = value.split("; " + name + "=");
  if (parts.length === 2) return parts.pop().split(";").shift();
};

const deleteCookie = (name) => {
  document.cookie = name + '=; max-age=0;';
};

const parseObjectFromCookie = (cookie) => {
  const decodedCookie = decodeURIComponent(cookie);
  return JSON.parse(decodedCookie);
};

window.onload = () => {
  let dataCookie = getCookie('data');
  deleteCookie('data');

  if (dataCookie) {
    const data = parseObjectFromCookie(dataCookie);
    // work with data. `data` is equal to `visitCard` from the server

  } else {
    // handle data not found
  }


Komplettlösung

Sie senden das Cookie vom Server, bevor Sie die Seite rendern. Das Cookie steht also zur Verfügung, wenn die Seite geladen wird.

Dann erhalten Sie vom Client den Cookie mit der gefundenen Lösung hier und löschen ihn. Der Inhalt des Cookies wird in unserer Konstanten gespeichert. Wenn das Cookie vorhanden ist, parsen Sie es als Objekt und verwenden es. Beachten Sie, dass Sie in der Variable parseObjectFromCookie zunächst den Inhalt dekodieren müssen und dann die JSON-Datei zu einem Objekt parsen.

Anmerkungen:

  • Wenn Sie die Daten asynchron abrufen, senden Sie bitte das Cookie vor dem Rendern. Andernfalls erhalten Sie eine Fehlermeldung, da res.render() die Antwort beendet. Wenn der Datenabruf zu lange dauert, können Sie eine andere Lösung verwenden, die das Rendern nicht so lange hält. Eine Alternative könnte darin bestehen, einen Socket vom Client aus zu öffnen und die Informationen zu senden, die Sie auf dem Server gespeichert haben. Siehe hier für diesen Ansatz.

  • Wahrscheinlich ist data nicht der beste Name für ein Cookie, da Sie etwas überschreiben könnten. Verwenden Sie etwas Sinnvolleres für Ihren Zweck.

  • Ich habe diese Lösung nirgendwo anders gefunden. Ich weiß nicht, ob die Verwendung von Cookies aus irgendeinem Grund nicht empfohlen wird. Ich dachte nur, dass es funktionieren könnte und habe es überprüft, aber ich habe es in der Produktion nicht verwendet.

4
Daniel Reina

Verwenden Sie res.send anstelle von res.render. Es akzeptiert Rohdaten in jeder Form: eine Zeichenfolge, ein Array, ein einfaches altes Objekt usw. Wenn es sich um ein Objekt oder ein Array von Objekten handelt, werden diese für Sie in JSON serialisiert.

var visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

exports.index = function(req, res, next){
  res.send(visitCard}; 
};
3
Brandon

Schauen Sie sich Steamer an, ein winziges Modul für genau diesen Zweck.

https://github.com/rotundasoftware/steamer

2
Brave Dave

Die eleganteste und einfachste Methode ist die Verwendung der Rendering-Engine (zumindest für die betreffende Seite). Verwenden Sie zum Beispiel EJS-Engine

node install ejs -s

Auf server.js:

let ejs = require('ejs');    
app.set('view engine', 'ejs');

benennen Sie dann die gewünschte index.html-Seite in index.ejs um und verschieben Sie sie in das Verzeichnis/views. Danach können Sie ein API-Endpoit für diese Seite erstellen (mit dem mysql-Modul):

app.get('/index/:id', function(req, res) { 
    db.query("SELECT * FROM products WHERE id = ?", [req.params.id], (error, results) => {
    if (error) throw error;
        res.render('index', { title: results[0] }); 
    });
});  

Am Frontend müssen Sie eine GET-Anfrage stellen, z. B. mit Axios oder direkt durch Klicken auf einen Link in der Vorlage index.ejs-Seite, die die Anfrage sendet:

<a v-bind:href="'/index/' + co.id">Click</a>

dabei ist co.id der Vue-Datenparameterwert 'co', den Sie zusammen mit der Anfrage senden möchten

0
Danny