Il presente articolo è stato tradotto automaticamente.

Node.js

Creazione di app Web nello stack MEAN con OData in Microsoft Azure

Lungo Le

Scaricare il codice di esempio

Gli sviluppatori Microsoft .NET in genere costruire grandi applicazioni utilizzando JavaScript sul lato client e ASP.NET (c# o Visual Basic .NET) sul lato server. Ma cosa succede se si potrebbe usare un linguaggio comune per costruire applicazioni su tutti i livelli dello stack, tutto da browser e il livello di servizi di business sul lato server di elaborazione e anche per l'esecuzione di query e di programmazione nel database? Ora è possibile, con node. js. Node. js è stato intorno per un certo numero di anni, ma la sua adozione ha raccolto significativamente negli ultimi anni. Stack node. js, come il MongoDB, Express, AngularJS, stack di node. js (media), portare molti benefici per la costruzione di applicazioni, tra cui il fatto che non c'è molto poco scollegare (se presente), tra gli sviluppatori di front-end, strato middle e back-end. In molti casi il programmatore stesso può sviluppare tutti gli strati di un app, perché è tutto fatto in JavaScript. Inoltre, ora è possibile costruire applicazioni di node. js direttamente in Visual Studio 2013 con strumenti di node. js per Visual Studio (NTVS), compresa la piena funzionalità di debug.

Guida introduttiva

In questo articolo voglio mostrarvi che utilizza lo stack medio, edificio creare, leggere, aggiornare ed eliminare (CRUD)-applicazioni pesanti possono essere facile e veloce. Ti si presuppone una conoscenza di base concettuale di AngularJS (angularjs.org), node. js (nodejs.org), MongoDB (mongodb.org) ed Express (expressjs.com). Se hai intenzione di proseguire, si prega di essere sicuri di che avere installato il seguente software:

Il primo passo è aprire la finestra di dialogo Nuovo progetto in Visual Studio e scegliete il modello vuoto applicazione Web Microsoft Azure node. js, come indicato nella Figura 1. Potresti scorciatoia pochi oggetti scegliendo il modello di base Microsoft Azure Express applicazione, ma un modello vuoto fornisce un controllo più granulare su cosa installare come middleware per l'applicazione di node. js.

creare un'applicazione Web di node. js di vuoto Microsoft Azure
Figura 1 creare un'applicazione Web di node. js di vuoto Microsoft Azure

Che cosa è il middleware di node. js? Per semplificare eccessivamente, è semplicemente i moduli che possono collegare a pipeline delle richieste HTTP Express dell'applicazione node. js. In genere, il middleware viene eseguito per ogni richiesta HTTP.

Quindi, installare Express utilizzando il nodo Gestione pacchetti (NPM). Se hai familiarità con NuGet pacchetti, pacchetti NPM sono fondamentalmente la stessa cosa, ma per applicazioni di node. js.

Come si può vedere Figura 2, aggiunto @3 in altri npm argomenti campo di testo, al fine di installare l'ultima versione di Express 3. Sebbene Express 4 è stato rilasciato, è necessario attaccare con Express 3 perché gli altri moduli che verranno installati non sono stati aggiornati per alcune delle ultime modifiche di Express 4.

trovare e installare i pacchetti NPM come Express
Figura 2 trovare e installare i pacchetti NPM come Express

È necessario scaricare e installare il resto dei pacchetti richiesti NPM: esprimere, odata-server, la stringify-oggetto e corpo-parser, ma non non c'è nessun bisogno di avere "altri npm argomenti," come sarò con l'ultima versione di ciascuno di questi pacchetti di npm.

Creazione di Server.js File

Il file server.js (a volte denominato JS), mostrato Figura 3, è fondamentalmente il punto di partenza dell'app node. js. Questo è dove si configura l'applicazione e iniettano eventuali moduli necessari middleware.

Figura 3 il File Server.js

1    var http = require('http');
2    var express = require( 'express' );
3    var odata = require( './server/data/odata' );
4    var stringify = require( 'stringify-object' );
5    var config = require("./server/config/config");
6    var bodyParser = require("body-parser");
7    var app = express( );
8    odata.config( app );
9    app.use(bodyParser.json());
10   app.use( express.static( __dirname + "/public" ) );
11   var port = process.env.port || 1337;
12   app.get("/", function(req, res) {
13   res.sendfile("/public/app/views/index.html", { root: __dirname });
14   });
15   http.createServer(app).listen(port);
16   console.log(stringify( process.env ));

Per consumare i pacchetti/librerie NPM richieste che si scarica, è necessario utilizzare la parola chiave richiedono ("nome pacchetto") per portare queste librerie nell'ambito per una determinata classe di node. js, come indicato nelle linee da 1 a 6 Figura 3. Io esamineremo rapidamente il contenuto di server.js:

  • Linea 1-6: Portare tutti i pacchetti richiesti in ambito server.js, così possono essere inizializzati e inseriti nella pipeline delle richieste HTTP.
  • Linea 7: Inizializzare una nuova applicazione Web Express.
  • Linea 8: Definire la configurazione di OData per gli endpoint del resto; ulteriori informazioni su questo un po '.
  • Linea 10: Plug-in express.static e passare il percorso di directory per esporre il percorso di directory che viene passato in pubblicamente. Questo permette a chiunque raggiungere qualsiasi contenuto nella directory NodejsWebApp/pubblico. Ad esempio, http://localhost:1337/image/myImage.gif renderebbe l'immagine in NodejsWebApp/Public/image/myimage.gif al browser.
  • Linea 12: Impostata un pagina utilizzando il metodo app.get di destinazione di default. Il primo parametro prende il percorso (il percorso principale dell'app). Qui, sto rendendo semplicemente un file HTML statico fornendo il percorso ad esso.
  • Linea 15: Indicare l'applicazione per ascoltare e servire richieste HTTP sulla porta specificata; per scopi di sviluppo sto usando porta 1337, quindi la mia applicazione ascolteranno le richieste a http://localhost:1337.
  • Linea 16: Stampare le variabili di ambiente nella finestra di console di node. js per portare qualche visibilità all'ambiente node. js.

Configurazione OData

Con server.js istituito, vado a fuoco ora sulla linea 8, dove configurare endpoint OData resto. In primo luogo, è necessario creare due moduli: NodejsWebApp/server/data/northwind.js (Figura 4) e NodejsWebApp/server/data/odata.js (Figura 5).

Figura 4 NodejsWebApp/server/data/northwind.js

$data.Entity.extend( 'Northwind.Category', {
  CategoryID: { key: true, type: 'id', nullable: false, computed: true },
  CategoryName: { type: 'string', nullable: false, required: true, maxLength: 15 },
  Description: { type: 'string', maxLength: Number.POSITIVE_INFINITY },
  Picture: { type: 'blob', maxLength: Number.POSITIVE_INFINITY },
  Products: { type: 'Array', elementType: 'Northwind.Product', inverseProperty: 'Category' }
} );
$data.Entity.extend( 'Northwind.Product', {
  ProductID: { key: true, type: 'id', nullable: false, computed: true },
  ProductName: { type: 'string', nullable: false, required: true, maxLength: 40 },
  EnglishName: { type: 'string', maxLength: 40 },
  QuantityPerUnit: { type: 'string', maxLength: 20 },
  UnitPrice: { type: 'decimal' },
  UnitsInStock: { type: 'int' },
  UnitsOnOrder: { type: 'int' },
  ReorderLevel: { type: 'int' },
  Discontinued: { type: 'bool', nullable: false, required: true },
  Category: { type: 'Northwind.Category', inverseProperty: 'Products' },
  Order_Details: { type: 'Array', elementType: 'Northwind.Order_Detail', 
  inverseProperty: 'Product' },
  Supplier: { type: 'Northwind.Supplier', inverseProperty: 'Products' }
} );
$data.Class.define( "NorthwindContext", $data.EntityContext, null, {
  Categories: { type: $data.EntitySet, elementType: Northwind.Category },
  Products: { type: $data.EntitySet, elementType: Northwind.Product },
  // Other entity registrations removed for brevity, please see actual source code.
} );
// Other entity definitions removed for brevity, please see actual source code.
NorthwindContext.generateTestData = function( context, callBack ) {
  var category1 = new Northwind.Category( { CategoryName: 'Beverages',
    Description: 'Soft drinks, coffees, teas, beer, and ale' } );
  // Other category instances removed for brevity, please see actual source code.
  context.Categories.add( category1 );
  // Other category inserts removed for brevity, please see actual source code.
  context.Products.add( new Northwind.Product( 
   { ProductName: 'Ipoh Coffee', EnglishName: 'Malaysian Coffee',
    UnitPrice: 46, UnitsInStock: 670, Discontinued: false, Category: category1 } ) );
  // Other product inserts removed for brevity, please see actual source code.
  context.saveChanges( function ( count ) {
    if ( callBack ) {
      callBack( count );
    }
  } );
};
module.exports = exports = NorthwindContext;

Figura 5 NodejsWebApp/server/data/odata.js modulo

( function (odata) {
  var stringify = require( 'stringify-object' );
  var config = require( "../config/config" );
  console.log( stringify( config ) );
  odata.config = function ( app ) {
    var express = require( 'express' );
    require( 'odata-server' );
    var northwindContextType = require( './northwind.js' );
    var northwindContext = new NorthwindContext( {
      address: config.mongoDb.address,
      port: config.mongoDb.port,
      username: config.mongoDb.username,
      password: config.mongoDb.password,
      name: config.mongoDb.name,
      databaseName: config.mongoDb.databaseName,
      dbCreation: $data.storageProviders.DbCreationType.DropAllExistingTables
    } );
    console.log( "northwindContext :" );
    stringify( northwindContext );
    northwindContext.onReady( function ( db ) {
      northwindContextType.generateTestData( db, function ( count ) {
        console.log( 'Test data upload successful. ', count, 'items inserted.' );
        console.log( 'Starting Northwind OData server.' );
        app.use( express.basicAuth( function ( username, password ) {
          if ( username == 'admin' ) {
            return password == 'admin';
          } else return true;
        } ) );

MongoDB è un database NoSQL — che è, un database non relazionali documento. Durante la migrazione un tradizionale database Northwind di MongoDB per approfittare del modello NoSQL, ci possono essere molti modi di strutturare esso. Per questo articolo, lascio lo schema Northwind, per la maggior parte, intatte. (Ho rimosso altre definizioni del modello di entità, le registrazioni e inserti da Figura 4 per brevità.)

In Figura 4 le entità e i modelli sono semplicemente essere definita, che può essere riutilizzato successivamente sul lato client quando si eseguono operazioni CRUD, quali la creazione di nuovi prodotti, per esempio. Inoltre, il metodo NorthwindContext.generateTestData verrà semi il database ogni volta che l'applicazione viene riavviata, che sarà utile quando si distribuisce l'applicazione a un sito demo live. Ciò rende più semplice aggiornare i dati ogni volta che necessario semplicemente riciclando l'applicazione. Un approccio più elegante sarebbe avvolgere questo codice in un WebJob Azure e pianificarlo per aggiornare a una frequenza impostata, ma lasciarlo com'è per ora. L'ultima riga in questo modulo, module.exports = esportazioni = NorthwindContext, avvolge tutto così che successivamente si può "richiedere" questo modulo e utilizzare l'operatore "new" per creare una nuova istanza del tipo di oggetto Northwind, che avviene nel modulo NodejsWebApp/server/data/odata.js, mostrato Figura 5.

È possibile eseguire query MongoDB tramite la riga di comando oppure utilizzando uno dei molti strumenti GUI MongoDB là fuori (come RoboMongo) per confermare che il seme è stato infatti inseriti dati. Poiché l'obiettivo di questo articolo è su OData, utilizzare LINQPad perché include un provider incorporato per eseguire una query con LINQ contro OData versione 3.

Per testare gli endpoint, scaricare e installare LINQPad (linqpad.net), e quindi eseguire l'applicazione (F5 in Visual Studio 2013). Poi fuoco LINQPad e impostare una nuova connessione all'endpoint OData. A tale scopo, fare clic sul collegamento Aggiungi e selezionare OData come provider di dati LINQPad. Quindi configurare il OData LINQ connessione con l'URI http://localhost:1337/northwind.svc; nome utente, Admin; e la password, admin. LINQPad renderà la gerarchia basata su OData CSDL endpoint, come si può vedere nell'angolo superiore sinistro del Figura 6.

LINQ Query e i risultati utilizzando il modello di dati rilevati
Figura 6 LINQ Query e i risultati utilizzando il modello di dati rilevati

Ci dovrebbero essere dati per prodotti sulla base dei dati di seme utilizzati sul lato server (NodejsWebApp/server/northwind.js), quindi ti consigliamo di fare una rapida query LINQ su prodotti utilizzando LINQPad:

Products.Take(100)

Figura 6 Mostra anche la query e dei suoi risultati.

Come potete vedere, il server OData è impostato correttamente e siete in grado di eseguire query LINQ su HTTP e recuperare un elenco di prodotti. Se si fa clic sulla scheda richiesta Log, si può effettivamente vedere che la OData HTTP GET URL LINQPad genera dall'istruzione LINQ : () http://localhost:1337/Northwind/prodotti? $top = 100.

Una volta che hai confermato che il server OData infatti esegue sul node. js Express Web app, ti consigliamo di usufruire di questo e iniziare a costruire fuori alcuni casi di uso comune che può consumare che bontà OData. Mettere tutto sul lato client nella cartella "pubblica" e tutto il codice che viene eseguito sul lato server in una cartella denominata Server. Creare tutti i file necessari per l'app in anticipo come stub o segnaposto e poi tornare indietro e riempire gli spazi vuoti. Figura 7 Mostra la struttura del progetto NodejsWebApp.

il progetto NodejsWebApp
Figura 7 il progetto NodejsWebApp

Il file js (NodejsWebApp/public/app/app.js) indicato in Figura 8 è fondamentalmente il punto di partenza dell'applicazione AngularJS (lato client). Non voglio entrare in tutti i dettagli; l'asporto qui è che si desidera registrare i percorsi sul lato client per l'applicazione a pagina singola (SPA) con il $routeProvider. Per ognuno degli itinerari (definiti con il metodo quando), fornire un percorso alla visualizzazione HTML () per eseguire il rendering impostando la proprietà templateUrl e specificare il controller di una visualizzazione impostando la proprietà controller per un determinato percorso. Il controller AngularJS è dove tutto il codice di vita per facilitare tutto ciò che richiede la vista — in breve, tutti i JavaScript codice per la visualizzazione. Il metodo otherwise è utilizzato per configurare una route predefinita (la casa vista) per le richieste in arrivo che non corrisponde ad alcuna delle rotte.

Figura 8 il file js.

'use strict';
var myApp = angular.module('myApp',
  [
    'ngRoute',
    'ngAnimate',
    'kendo.directives',
    'jaydata'
  ])
  .factory("northwindFactory",
  [
    '$data',
    '$q',
    function($data, $q) {
      // Here you wrap a jquery promise into an angular promise.
      // Simply returning jquery promise causes bogus things
      var defer = $q.defer();
      $data.initService("/northwind.svc").then(function(ctx) {
        defer.resolve(ctx);
      });
      return defer.promise;
    }
  ])
  .config(function($routeProvider) {
    $routeProvider
      .when('/home',
      {
        templateUrl: 'app/views/home.html'
      })
      .when('/product',
      {
        templateUrl: 'app/views/product.html',
        controller: 'productController',
        resolve: {
          northwind: 'northwindFactory'
        }
      })
      .when('/edit/:id',
      {
        templateUrl: 'app/views/edit.html',
        controller: 'editController',
        resolve: {
          northwind: 'northwindFactory'
        }
      })
      .when('/chart',
      {
        templateUrl: 'app/views/chart.html',
        controller: 'chartController',
        resolve: {
          northwind: 'northwindFactory'
        }
      })
      .otherwise(
      {
        redirectTo: '/home'
      });
  });

Ecco un veloce riepilogo di come le preoccupazioni del pattern Model-View-ViewModel (MVVM) sono rappresentate in app:

  • Vista = *. html
  • ViewModel = *controller.js
  • Modello = entità restituite dagli endpoint del resto, di solito sono modelli di dominio e/o entità

Figura 9 illustrato quali file l'indirizzo di applicazione che riguardano il motivo MVVM.

il Pattern Model-View-ViewModel
Figura 9 il Pattern Model-View-ViewModel

Definendo il DataContext JayData Client-Side come un servizio di AngularJS

Perché la maggior parte dei controller utilizzerà il contesto di Northwind, ti consigliamo di creare una servizio/fabbrica denominata northwindFactory. E poiché l'inizializzazione del contesto Northwind è async, ti consigliamo di impostare una promessa di JavaScript per garantire l'inizializzazione del contesto Northwind ha completato ed è pronto per essere utilizzato al momento che uno dei controller vengono caricato. Così, in breve, il contesto di Northwind terminerà caricamento prima di qualsiasi controller con una dipendenza dei carichi northwindFactory. Si noti che tutte le route configurate dispongono di una proprietà di "risolvere", che è come definire ciò promesse devono risolvere prima il controller viene caricato. In questo caso, la proprietà, "northwind", è impostata per la northwindFactory. Il nome di proprietà "northwind" sarà anche il nome dell'istanza che verrà iniettato nel controller. Vedrai la funzione costruttore per productController.js un po' (in Figura 11), dove il northwindFactory viene iniettato come northwind, il nome della proprietà che è impostato per northwindFactory nella proprietà resolve negli itinerari.

Index. html, mostrato Figura 10, sarà fondamentalmente la pagina di layout e AngularJS saprà quali viste di swap in div con l'attributo ng-vista. Nota che devi specificare l'app AngularJS configurando qualsiasi elemento HTML che è un elemento padre del div attribuita con "ng-vista." In questo caso si desidera impostare "ng-app" per "myApp", che è ciò che l'applicazione è denominata nel js.

Figura 10 il File index. html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf-8" />
    <title>NodejsWebApp</title>
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"
      rel="stylesheet">
    <link href="//cdn.kendostatic.com/2014.2.716/styles/kendo.common.min.css"
      rel="stylesheet" />
    <link href="//cdn.kendostatic.com/2014.2.716/styles/kendo.bootstrap.min.css"
      rel="stylesheet" />
    <link href="//cdn.kendostatic.com/2014.2.716/styles/kendo.dataviz.min.css"
      rel="stylesheet" />
    <link href="//cdn.kendostatic.com/2014.2.716/styles/
      kendo.dataviz.bootstrap.min.css" rel="stylesheet" />
    <link href="../../css/site.css" rel="stylesheet" />
  </head>
    <body>
    <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle"
            data-toggle="collapse" data-target=".navbar-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="/">NodejsWebApp</a>
        </div>
        <div class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li>
              <a href="#/home">Home</a>
            </li>
            <li>
              <a href="#/about">About</a>
            </li>
            <li>
              <a href="#/contact">Contact</a>
            </li>
            <li>
              <a href="#/product">Product</a>
            </li>
            <li>
              <a href="#/chart">Chart</a>
            </li>
          </ul>
        </div>
       </div>
    </div>
    <!-- Binding the application to our AngularJS app: "myApp" -->
    <div class="container body-content" ng-app="myApp">
      <br />
      <br/>
      <!-- AngularJS will swap our Views inside this div -->
      <div ng-view></div>
      <hr />
      <footer>
        <p>&copy; 2014 - My Node.js Application</p>
      </footer>
    </div>
    <script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
    <script src=
      "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js">
    </script>
    <script src="//code.angularjs.org/1.3.0-beta.16/angular.min.js"></script>
    <script src="//code.angularjs.org/1.3.0-beta.16/angular-route.min.js"></script>
    <script src="//code.angularjs.org/1.3.0-beta.16/angular-animate.min.js"></script>
    <script src="//cdn.kendostatic.com/2014.2.716/js/kendo.all.min.js"></script>
    <script src="//include.jaydata.org/datajs-1.0.3-patched.js"></script>
    <script src="//include.jaydata.org/jaydata.js"></script>
    <script src="//include.jaydata.org/jaydatamodules/angular.js"></script>
    <script src="/lib/jaydata-kendo.js"></script>
    <!--<script src="//include.jaydata.org/jaydatamodules/kendo.js"></script>-->
    <script src="/app/app.js"></script>
    <script src="/app/controllers/productController.js"></script>
    <script src="/app/controllers/chartController.js"></script>
    <script src="/app/controllers/editController.js"></script>
  </body>
</html>

Nota che sto usando un content delivery network (CDN) per tutta la mia libreria di JavaScript lato client include. È possibile scaricare le librerie lato client utilizzando localmente Bower nella riga di comando (come in genere fareste per progetti .NET con NuGet utilizzando la console di Gestione pacchetti ). In Microsoft .NET Framework utilizzare NuGet per pacchetti sia sul lato client e lato server. Nel Regno di node. js, tuttavia, Bower è utilizzato per scaricare sul lato client librerie/pacchetti mentre NPM è usato per scaricare e installare le librerie lato server/pacchetti.

Per il layout dell'interfaccia utente, utilizzare un tema bootstrap alla vaniglia, quello che genera il modello di progetto MVC 5 Visual Studio ASP.NET .

Vista del prodotto

Solo poche righe di codice HTML sono necessari per la visualizzazione del prodotto (NodejsWebApp/public/app/views/products.html). Il primo blocco è la direttiva di Kendo per AngularJS eseguire il rendering della griglia:

<!-- Kendo UI's AngularJS directive for the grid -->
<div kendo-grid="grid" k-options="options"></div>
<!-- AngularJS template for our View Detail Button in the Grid Toolbar -->
<script type="text/x-kendo-template" id="viewDetail">
  <a
    class="k-button "
    ng-click="viewDetail(this)">View Detail</a>
</script>

Il secondo blocco è solo un modello AngularJS per il pulsante Mostra dettaglio personalizzato che si aggiunge alla barra degli strumenti della griglia.

Figura 11 illustrato il prodotto Controller, NodejsWebApp/app/controllers/productController.js.

Figura 11 il Controller prodotto

myApp.controller("productController",
  function($scope, northwind, $location) {
    var dataSource =
      northwind
        .Products
        .asKendoDataSource({ pageSize: 10 });
    $scope.options = {
      dataSource: dataSource,
      filterable: true,
      sortable: true,
      pageable: true,
      selectable: true,
      columns: [
        { field: "ProductID" },
        { field: 'ProductName' },
        { field: "EnglishName" },
        { field: "QuantityPerUnit" },
        { field: "UnitPrice" },
        { field: 'UnitsInStock' },
        { command: ["edit", "destroy"] }
      ],
      toolbar: [
        "create",
        "save",
        "cancel",
        {
          text: "View Detail",
          name: "detail",
          template: $("#viewDetail").html()
        }
      ],
      editable: "inline"
    };
    $scope.viewDetail = function(e) {
      var selectedRow = $scope.grid.select();
      if (selectedRow.length == 0)
        alert("Please select a row");
      var dataItem = $scope.grid.dataItem(selectedRow);;
      $location.url("/edit/" + dataItem.ProductID);
    };
  });

Per idratare la griglia di prodotti, è necessario creare un'istanza di un DataSource UI di Kendo ($scope.options.dataSource). JayData fornisce un metodo di supporto per inizializzare un DataSource di UI Kendo associato ai relativi endpoint OData resto. Il metodo di asKendoDataSourcehelper JayData sa come creare l'origine dati basato su metadati informazioni pubblicate dal server OData (http://localhost:1337/northwindsvc), che viene quindi utilizzato per configurare l'istanza di $data in northwindFactory in js. Si vedrà più di Kendo DataSource quando dimostro impressioni visive con il framework grafici Kendo DataViz.

Insieme con i pulsanti di out-of-the-box (creare, salvare e cancellare) configurato nella barra degli strumenti della griglia, si aggiunge un pulsante personalizzato per passare a un'altra visualizzazione che renderà i dettagli completi di una riga del prodotto selezionato ($scope.viewDetail). Quando si clicca sul pulsante di visualizzazione dettaglio evento si verifica, ottenere il prodotto selezionato DataItem e poi, utilizzando il servizio di $location di AngularJS, passare alla visualizzazione modifica (MyNodejsWebApp/scripts/app/views/edit.html) per il prodotto.

Figura 12 Mostra il file Edit.html, NodejsWebApp/public/app/views/edit.html.

Figura 12 il File Edit.html

<div class="demo-section">
  <div class="k-block" style="padding: 20px">
    <div class="k-block k-info-colored">
      <strong>Note: </strong>Please fill out all of the fields in this form.
    </div>
    <div>
      <dl>
        <dt>
          <label for="productName">Name:</label>
        </dt>
        <dd>
          <input id="productName" type="text"
            ng-model="product.ProductName" class="k-textbox" />
        </dd>
        <dt>
          <label for="englishName">English Name:</label>
        </dt>
        <dd>
          <input id="englishName" type="text"
            ng-model="product.Englishname" class="k-textbox" />
        </dd>
        <dt>
          <label for="quantityPerUnit">Quantity Per Unit:</label>
        </dt>
        <dd>
          <input id="quantityPerUnit" type="text"
            ng-model="product.QuantityPerUnit" class="k-textbox" />
        </dd>
        <dt>
          <label for="unitPrice">Unit Price:</label>
        </dt>
        <dd>
          <input id="unitPrice" type="text"
            ng-model="product.UnitPrice" class="k-textbox" />
        </dd>
        <dt>
          <label for="unitsInStock">Units in Stock:</label>
        </dt>
        <dd>
          <input id="unitsInStock" type="text"
            ng-model="product.UnitsInStock" class="k-textbox" />
        </dd>
        <dt>
          <label for="reorderLevel">Reorder Level</label>
        </dt>
        <dd>
          <input id="reorderLevel" type="text"
            ng-model="product.ReorderLevel" class="k-textbox" />
        </dd>
        <dt>
          <label for="discontinued">Discontinued:</label>
        </dt>
        <dd>
          <input id="discontinued" type="text"
            ng-model="product.Discontinued" class="k-textbox" />
        </dd>
        <dt>
          <label for="category">Category:</label>
        </dt>
        <dd>
           <select
             kendo-drop-down-list="dropDown"
             k-data-text-field="'CategoryName'"
             k-data-value-field="'CategoryID'"
             k-data-source="categoryDataSource"
             style="width: 200px"></select>
        </dd>
      </dl>
      <button kendo-button ng-click="save()"
        data-sprite-css-class="k-icon k-i-tick">Save</button>
      <button kendo-button ng-click="cancel()">Cancel</button>
      <style scoped>
        dd {
          margin: 0px 0px 20px 0px;
          width: 100%;
        }
        label {
          font-size: small;
          font-weight: normal;
        }
        .k-textbox { width: 100%; }
        .k-info-colored {
          margin: 10px;
          padding: 10px;
        }
      </style>
    </div>
  </div>
</div>

Si noti come gli ingressi sono decorati con l'attributo ng-modello, che è il modo per AngularJS in modo dichiarativo che indica che il valore per quell'input verrà archiviato in una proprietà, che il valore di ng-modello è impostato sul controller $scope. Ad esempio, nel primo campo di input in questa vista, il cui id dell'elemento HTML è impostata su productName (id = "productName"), modello di ng è impostato su prodotto.ProductName. Questo significa che qualunque cosa l'utente entra nel campo di input (textbox), il valore per $scope.productName sarà impostato di conseguenza. Inoltre, qualunque $scope.product.productName è impostato a livello di codice in editController verrà automaticamente riflesse nel valore del campo di input per productName.

Ad esempio, quando la vista prima carica, si carica il prodotto dall'ID passati tramite URL, quindi impostare $scope.product su quel prodotto (Vedi Figura 13).  Una volta che questo accade, tutto nella visualizzazione con ng-modello impostato $scope.property.* riflette tutti i valori di proprietà in $scope.product. In passato, gli sviluppatori in genere impostato i valori nei campi di input utilizzando jQuery o JavaScript dritto per qualsiasi tipo di manipolazione del DOM. Quando si compila un'applicazione con il pattern MVVM (a prescindere dal quadro), si consiglia di manipolare il DOM solo attraverso cambiamenti di ViewModel, mai direttamente (per esempio, con JavaScript o jQuery). Sicuramente sto non implicando non c'è niente di sbagliato con JavaScript o jQuery, ma se si decide di utilizzare un modello per risolvere un problema specifico (nel mio caso, MVVM per mantenere la separazione delle preoccupazioni tra la vista, il ViewModel e il modello), dovrebbe essere coerenza in tutta l'applicazione.

Figura 13 il File editController.js

myApp.controller("editController",
  function($scope, northwind, $routeParams, $location) {
    var productId = $routeParams.id;
    $scope.categoryDataSource = northwind.Categories.asKendoDataSource();
    northwind
      .Products
      .include("Category")
      .single(
        function(product) {
          return product.ProductID == productId;
        },
        { productId: productId },
        function(product) {
          $scope.product = product;
          northwind.Products.attach($scope.product);
          $scope.dropDown.value($scope.product.Category.CategoryID);
          $scope.$apply();
        });
    $scope.save = function() {
      var selectedCategory = $scope
                            .categoryDataSource
                            .get($scope.product.Category.CategoryID);
      console.log("selecctedCategory: ", selectedCategory.innerInstance());
      $scope.product.Category = selectedCategory.innerInstance();
      // Unwrap kendo dataItem to pure JayData object
      northwind.saveChanges();
    };
    $scope.cancel = function() {
      $location.url("/product");
    };
  });

Si noti che si potrebbe implementare un'azione di POST sul lato server su node. js, che è tipicamente quello è fatto con l'API Web ASP.NET . Tuttavia, lo scopo qui è quello di dimostrare come fare questo con node. js e OData:

app.post('/api/updateProduct', function(req, res) {
  var product = req.body;
  // Process update here, typically what is done with the ASP.NET Web API
});

Per la visualizzazione del grafico (NodejsWebApp/public/app/views/chart.html), avete bisogno di una sola riga di codice:

<kendo-chart k-options="options"></kendo-chart>

Tutto ciò che sta accadendo qui è che dichiara la direttiva Kendo UI Bar Chart, l'impostazione di queste opzioni per l'associazione a una proprietà nel controller denominata opzioni. Figura 14 viene illustrata la visualizzazione del grafico di prodotto e Figura 15 illustrato il controller grafico prodotto.

 Visualizzazione del grafico di prodotto
Visualizzazione del grafico di figura 14 prodotto

Figura 15 il Controller grafico prodotto

myApp.controller("chartController",
  function($scope, northwind) {
    var dataSource = northwind.Products.asKendoDataSource();
    $scope.options = {
      theme: "metro",
      dataSource: dataSource,
      chartArea: {
        width: 1000,
        height: 550
      },
      title: {
        text: "Northwind Products in Stock"
      },
      legend: {
        position: "top"
      },
      series: [
        {
          labels: {
            font: "bold italic 12px Arial,Helvetica,sans-serif;",
            template: '#= value #'
          },
          field: "UnitsInStock",
          name: "Units In Stock"
        }
      ],
      valueAxis: {
        labels: {
          format: "N0"
        },
        majorUnit: 100,
        plotBands: [
          {
            from: 0,
            to: 50,
            color: "#c00",
            opacity: 0.8
          }, {
            from: 50,
            to: 200,
            color: "#c00",
            opacity: 0.3
          }
        ],
        max: 1000
      },
      categoryAxis: {
        field: "ProductName",
        labels: {
          rotation: -90
        },
        majorGridLines: {
          visible: false
        }
      },
      tooltip: {
        visible: true
      }
    };
  });

Come con productController.js, qui si anche iniettare il northwindFactory come northwind nella funzione di costruzione controller, creando nuovamente un dataSource di Kendo con il metodo di asKendoDataSource di supporto JayData. Qui ci sono ulteriori dettagli su ciò che accade nel controller grafico:

$scope.options.series

  • tipo: Questo configura il tipo di grafico.
  • Campo: Il campo modello/entità che verrà utilizzato per il valore di serie (asse x).

$scope.options.valueAxis

  • majorUnit: L'intervallo tra le divisioni principali. Se il valueAxis.type è impostato per registrare, il valore di majorUnit serviranno per la base del logaritmo.
  • plotBands: Le bande di trama per il grafico, che sono usate per indicare visivamente la quantità di prodotto. Se la quantità scende sotto un livello specificato, l'utente deve richiamare il prodotto processo di ripopolamento.
  • Max: Il valore massimo dell'asse y.

$scope.options.categoryAxis

  • Campo: Le etichette dei campi per l'asse x.
  • labels.Rotation: I gradi per ruotare le etichette. Qui configurare le etichette sull'asse x essere perpendicolare all'asse x in esecuzione impostando il valore a -90 (gradi), cioè, ruotare le etichette in senso antiorario di 90 gradi.
  • majorGridLines.visible: Attiva o disattiva la griglia principale. Si consiglia di disattivarli per ragioni estetiche, per dare il grafico un pulitore e più lucido sguardo.
  • ToolTip.Visible: In questo modo le descrizioni comandi quando un utente si posiziona su una barra verticale.

Si veda il Kendo UI Chart API per dettagli a bit.ly/1owgWrS.

Distribuzione azzurro sito Web

Poiché il codice sorgente è comodamente ospitato in un repository Git di CodePlex, utilizzando siti Web Azure impostare continua distribuzione (erogazione continua) è così semplice come ottiene:

  1. Passare alla bacheca del sito Web di Azure e selezionare Set distribuzione dal controllo del codice sorgente.
  2. Seleziona il tuo repository; in questo esempio, selezionare CodePlex.
  3. Scegliere Avanti.
  4. Selezionare progetto CodePlex.
  5. Selezionare il ramo.
  6. Fare clic su Controlla.
  7. Per ogni sincronizzazione al tuo repository Git, si verificherà una compilazione e distribuzione.

Niente altro. Con pochi semplici clic, l'applicazione è distribuita con consegna e integrazione continua. Per ulteriori informazioni sulla distribuzione con Git, visitare bit.ly/1ycBo9S.

Come sviluppatore .NET, sono venuto a godere davvero quanto veloce ed è facile creare applicazioni CRUD pesante con MVC ASP.NET , ASP.NET Web API, OData, Entity Framework, AngularJS e UI di Kendo. Ora, sviluppando nello stack medio, io posso ancora sfruttare molto di questo dominio conoscenza ed esperienza, con l'aiuto delle librerie JayData. L'unica differenza tra i due stack è lo strato sul lato server. Se tu hai sviluppato con MVC ASP.NET e ASP.NET Web API, node. js non dovrebbe presentare molti problemi perché hai già qualche esperienza di JavaScript di base. Troverete il codice sorgente completo per l'esempio in questo articolo a msdnmeanstack.codeplex.com e una demo live a meanjaydatakendo.azurewebsites.net.


Lungo Le è l'architetto principale app/dev CBRE Inc. e Kendo/Telerik UI MVP. Egli trascorre la maggior parte del suo tempo lo sviluppo di quadri e blocchi di applicazione, fornendo orientamenti per le migliori pratiche e modelli e la standardizzazione dello stack di tecnologia di impresa. Nel suo tempo libero, egli gode di blogging (blog.longle.net), giocare a Call of Duty o mentoring (codementor.io/lelong37). Si può raggiungere e seguirlo su Twitter a twitter.com/LeLong37.

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Robert Bany (JayData), Burk Holland (Telerik) e Peter Zentai (JayData)