Dieser Artikel wurde maschinell übersetzt.

Node.js

Erstellen von Web Apps auf dem MEAN-Stapel mit OData in Microsoft Azure

Lange Le

Laden Sie die Codebeispiele herunter

Microsoft .NET Entwickler erstellen in der Regel große Anwendungen mit Hilfe von JavaScript auf der Clientseite und ASP.NET (C#- oder Visual Basic .NET) auf der Serverseite. Aber was passiert, wenn könnten Sie eine gemeinsame Sprache verwenden, um Anwendungen auf allen Ebenen des Stapels, alles aus dem Browser und der Dienstschicht serverseitige Business Verarbeitung und auch Abfragen und Programmierung in der Datenbank zu erstellen? Jetzt kannst du mit Node.js. Node.js ist schon seit einigen Jahren, aber seine Annahme hat in den letzten Jahren deutlich abgeholt. Node.js Stapel, wie z. B. die MongoDB, Express, AngularJS, Node.js (Mittel)-Stack, viele Vorteile bringen, zum Erstellen von Anwendungen, einschließlich der Tatsache, dass es sehr wenig zu trennen (falls vorhanden), zwischen Front-End-, mittleren-Schicht und Back-End-Entwickler. In vielen Fällen kann der gleiche Programmierer alle Ebenen einer App entwickeln, weil es alle im JavaScript geschieht. Darüber hinaus können Sie jetzt Node.js apps direkt in Visual Studio 2013 mit Node.js-Tools für Visual Studio (NTVS), einschließlich vollständigem Debuggen erstellen.

Erste Schritte

In diesem Artikel werde ich Ihnen zeigen, dass mit den mittleren Stapel, Gebäude erstellen, lesen, aktualisieren und löschen (CRUD)-schwere Anwendungen können schnell und einfach sein. Ich werde angenommen, Sie haben ein konzeptionell Grundverständnis von AngularJS (angularjs.org), Node.js (nodejs.org), MongoDB (mongodb.org) und Express (expressjs.com). Wenn Sie planen, zu folgen, werden Sie sicher, dass Sie Folgendes installiert sein:

Der erste Schritt ist, öffnen Sie das Dialogfeld Neues Projekt in Visual Studio und wählen die Vorlage Leere Microsoft Azure Node.js Webanwendung wie in Abbildung 1. Kontextmenü könntest du ein paar Dinge durch die Wahl der grundlegenden Microsoft Azure Express Application-Vorlage, aber eine leere Vorlage ermöglicht eine präzisere Kontrolle über das, was als Middleware für die Node.js-Anwendung zu installieren.

erstellen Sie eine leere Microsoft Azure Node.js-Webanwendung
Abbildung 1 erstellen Sie eine leere Microsoft Azure Node.js-Webanwendung

Was ist Node.js Middleware? Um zu vereinfachen, ist es einfach so Module, die Sie der Anwendung Node.js Express HTTP Anforderungspipeline einstecken können. In der Regel wird die Middleware für jede HTTP-Anforderung ausgeführt.

Installieren Sie dann Express mit dem Knoten Paket-Manager (NPM). Wenn Sie NuGet Pakete kennen, NPM-Pakete sind im Grunde das gleiche, aber für Node.js Anwendungen.

Wie Sie, in sehen können Abbildung 2, habe ich noch @3 in anderen Npm Textfeld Argumente, um die neueste Version von Express 3 zu installieren. Obwohl Express 4 freigegeben wurde, müssen Sie mit Express 3 zu bleiben, weil die anderen Module, die installiert werden soll nicht zu einigen wichtigen Änderungen der Express 4 aktualisiert wurde.

suchen und Installieren von NPM-Paketen wie Express
Abbildung 2 suchen und Installieren von NPM-Paketen wie Express

Sie müssen den Rest der erforderlichen NPM-Pakete: Express, promoveaza-Server, stringify-Objekt und Körper-Parser, aber es gibt keine Notwendigkeit, keine "anderen Npm Argumente,", wie ich die neueste Version von jedem dieser Npm-Pakete verwenden werde.

Einrichten der Server.js-Datei

Die Datei server.js (manchmal benannte app.js), in Abbildung 3, ist im Grunde der Ausgangspunkt der Node.js app. Dies ist, wo Sie Ihre Anwendung konfigurieren und alle benötigten Middleware-Module zu injizieren.

Abbildung 3 die Server.js-Datei

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

Um die benötigten NPM-Pakete/Bibliotheken zu verarbeiten, die Sie herunterladen, müssen Sie mithilfe des Schlüsselworts erfordern ("Paketname"), diese Bibliotheken im Bereich für eine gegebene Node.js-Klasse zu bringen, wie gezeigt in den Zeilen 1 bis 6 in Abbildung 3. Ich werde den Inhalt des server.js schnell überprüfen:

  • Linie 1-6: Bringen Sie die erforderlichen Pakete in den server.js-Bereich, damit sie initialisiert und der HTTP-Anfrage-Pipeline angeschlossen werden können.
  • Linie 7: Initialisieren Sie eine neue Express-Web-Anwendung.
  • Linie 8: Definieren Sie die promoveaza-Konfiguration für die REST-Endpunkte; mehr dazu in Kürze.
  • Linie 10: Schließen Sie express.static, und übergeben Sie den Verzeichnispfad Pfad des Verzeichnisses verfügbar machen, der öffentlich übergeben wird. Dies kann jeder im Verzeichnis NodejsWebApp/öffentliche Inhalte zu erreichen. Http://localhost:1337/image/myImage.gif würde z. B. das Bild in NodejsWebApp/Public/image/myimage.gif an den Browser gerendert werden.
  • Linie 12: Richten Sie ein Standard-Seite, die mit der app.get-Methode. Der erste Parameter nimmt im Pfad (den Stammpfad der app). Ich bin hier einfach eine statische HTML-Datei rendern, durch die Bereitstellung des Weg.
  • Linie 15: Weisen Sie die Anwendung zu warten und bedienen, HTTP-Anforderungen an den angegebenen Anschluss; für Entwicklungszwecke verwende ich Port 1337, also meine Bewerbung für Anfragen bei Http://localhost:1337 hören wird.
  • Linie 16: Drucken Sie die Umgebungsvariablen im Konsolenfenster Node.js, einige Sichtbarkeit für die Node.js-Umwelt zu bringen.

Konfigurieren von promoveaza

Mit server.js eingerichtet werde ich mich konzentrieren jetzt auf Linie 8, wo ich die promoveaza REST-Endpunkte konfigurieren. Zuerst müssen Sie zwei Module zu erstellen: NodejsWebApp/server/data/northwind.js (Abbildung 4) und NodejsWebApp/server/data/odata.js (Abbildung 5).

Abbildung 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;

Abbildung 5-NodejsWebApp/server/data/odata.js-Modul

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

Beachten Sie, dass MongoDB eine NoSQL-Datenbank — d. h. ein nicht-relationale Datenbank. Beim Migrieren einer herkömmlichen Northwind-Datenbank zu MongoDB das NoSQL-Modell nutzen kann viele Möglichkeiten, es zu strukturieren. In diesem Artikel lasse ich die Northwind-Schemas, zum größten Teil, intakt. (Ich habe entfernt, andere Modell Definitionen, Registrierungen und Einsätze von Abbildung 4 der Kürze halber.)

In Abbildung 4 der Modelle und Entitäten sind einfach definiert, die später auf der Clientseite können, beim Durchführen von CRUD-Operationen wiederverwendet werden, wie das Erstellen von neuen Produkten, zum Beispiel. Außerdem wird die NorthwindContext.generateTestData-Methode die Datenbank Saatgut, jedes Mal, wenn die Anwendung neu gestartet ist das nützlich sein wenn Sie die Anwendung auf einer live-Demo-Website bereitstellen. Dies erleichtert die Daten bei Bedarf einfach durch das recycling der Anwendung aktualisiert. Ein eleganter Ansatz wäre, wickeln Sie diesen Code in einer Azure-WebJob und bei einer festgelegten Frequenz aktualisieren, sondern lassen wie es jetzt ist es zu planen. Die letzte Zeile in diesem Modul module.exports = Exporte = NorthwindContext, umschließt alles, so dass später weiter können Sie "verlangen" dieses Modul und verwenden Sie "neu" um eine neue Instanz des Objekttyps Northwind zu erstellen, die in das NodejsWebApp/server/data/odata.js-Modul, siehe erfolgt Abbildung 5.

MongoDB können abgefragt werden, über die Befehlszeile oder mithilfe einer der vielen MongoDB GUI Tools da draußen (z. B. RoboMongo) zu bestätigen, dass das Saatgut tatsächlich Daten eingefügt wurde. Da der Fokus dieses Artikels auf promoveaza, verwenden Sie LINQPad, da es einen integrierten Anbieter auf Abfrage mit LINQ gegen promoveaza Version 3 enthält.

Um die Endpunkte zu testen, laden und installieren LINQPad (linqpad.net), und dann die Anwendung ausführen (F5 in Visual Studio 2013). Dann starte LINQPad und richten Sie eine neue Verbindung mit dem promoveaza-Endpunkt. Hierzu klicken Sie auf Verbindung hinzufügen, und wählen Sie promoveaza als Ihren LINQPad Data Provider. Konfigurieren Sie anschließend die promoveaza LINQ Verbindung mit dem URI Http://localhost:1337/northwind.svc; Benutzername, Admin; und Passwort admin. LINQPad rendert die Hierarchie basierend auf der promoveaza CSDL -Endpunkt, wie Sie sehen in der oberen linken Ecke des Abbildung 6.

LINQ -Abfrage und die Ergebnisse mithilfe des ermittelten Datenmodells
Abbildung 6- LINQ -Abfrage und die Ergebnisse mithilfe des ermittelten Datenmodells

Daten für Produkte auf Basis der Saatgut-Daten auf der Serverseite (NodejsWebApp/server/northwind.js), also Sie wünschen eine schnelle LINQ -Abfrage zu tun auf Produkte mit LINQPad verwendet sollte vorhanden sein:

Products.Take(100)

Abbildung 6 zeigt auch die Abfrage und die Ergebnisse.

Wie Sie sehen können, der promoveaza-Server ordnungsgemäß eingerichtet ist, und du bist können LINQ -Abfragen über HTTP ausgeben und erhalten eine Liste der Produkte zurück. Wenn Sie auf der Registerkarte Protokoll anfordern klicken, sehen Sie tatsächlich, dass die HTTP-GET promoveaza URL LINQPad aus der LINQ -Anweisung generiert: Http://localhost:1337/northwind.svc/Produkte:)? $top = 100.

Sobald Sie bestätigt haben, dass Ihre promoveaza-Server tatsächlich auf die Node.js Express-Webanwendung ausgeführt wird, sollten Sie nutzen dies und beginnen Gebäude heraus einige häufige Anwendungsfälle, die jene promoveaza beste verbrauchen kann. Stellen Sie alles auf der Client-Seite in den "öffentlichen" Ordner und den gesamten Code, der auf dem Server in einem Ordner mit dem Namen Server ausgeführt wird. Erstellen Sie die Dateien, die für Ihre Anwendung im Voraus als Stubs oder Platzhalter benötigt und dann wiederkommen um und Füll die Lücken. Abbildung 7 zeigt die Struktur des Projektes NodejsWebApp.

das NodejsWebApp-Projekt
Abbildung 7 das NodejsWebApp-Projekt

Die Datei app.js (NodejsWebApp/public/app/app.js) in Abbildung 8 ist im Grunde der Ausgangspunkt der (clientseitig) AngularJS Anwendung. Ich gehe nicht in die Details; die Essen hier ist, dass Sie Ihre Client-seitige Routen für Ihre einseitige Anwendung (SPA) mit der $routeProvider zu registrieren möchten. Geben Sie für jede der Routen (mit definiertem ...Wenn-Methode) einen Pfad zur Ansicht (HTML) zu rendern, indem Sie die TemplateUrl-Eigenschaft und geben Sie eine View-Controller die Controller-Eigenschaft für eine bestimmte Route an. Der AngularJS-Controller wohnt alle des Codes zu erleichtern, was auch immer die Ansicht erfordert – kurz gesagt, alle JavaScript-code für die Ansicht. Die ...otherwise-Methode wird verwendet, um eine Standardroute (der Start-Ansicht) für alle eingehenden Anforderungen konfigurieren, die einer der Routen nicht übereinstimmen.

Abbildung 8 die App.js-Datei

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

Hier ist eine kurze Zusammenfassung der wie die Anliegen des Model-View-ViewModel (MVVM) Musters in der app vertreten sind:

  • Ansicht = *.html
  • ViewModel = *controller.js
  • Modell = Entitäten, die vom REST-Endpunkte zurückgegeben werden, sind in der Regel Domänenmodelle und/oder Entitäten

Abbildung 9 zeigt, welche Dateien in der Anwendungsadresse in das MVVM-Muster betreffen.

das Model-View-ViewModel-Muster
Abbildung 9 das Model-View-ViewModel-Muster

JayData Client-seitige DataContext definieren als einen AngularJS-Service

Da die meisten der Controller den Northwind-Kontext verwenden werden, sollten Sie eine Dienst/Factory mit dem Namen NorthwindFactory erstellen. Und da die Initialisierung des Kontextes Northwind Async ist, wollen Sie richten Sie ein JavaScript-Versprechen um sicherzustellen die Northwind Kontext Initialisierung abgeschlossen und ist bereit, zu der Zeit verwendet werden, dass einem der Controller geladen werden. Kurz gesagt, also werden der Northwind-Kontext geladen wurde, bevor jeder Controller mit einer Abhängigkeit zu den NorthwindFactory Lasten abgeschlossen. Beachten Sie, dass alle konfigurierten Routen eine "lösen"-Eigenschaft, die ist, wie Sie definieren was versprechen brauchen, um zu beheben, bevor der Controller geladen wird. In diesem Fall wird die Eigenschaft "Northwind" auf der NorthwindFactory festgelegt. Die Eigenschaft "Nordwind" werden auch den Namen der Instanz, die in den Controller injiziert wird. Sie sehen die Konstruktorfunktion für productController.js ein wenig (in Abbildung 11), wo die NorthwindFactory als Northwind, den Namen der Eigenschaft injiziert wird, die für NorthwindFactory in der Resolve-Eigenschaft in die Routen festgelegt ist.

Index.html, gezeigt Abbildung 10, werden grundsätzlich auf der Layoutseite und AngularJS werden wissen, welche Ansichten in das div-Tag mit dem Attribut ng-Ansicht zu wechseln. Beachten Sie, dass Sie haben, geben Sie die AngularJS-app durch die Konfiguration jedes HTML-Element, das ist ein übergeordnetes Element des Div zugeschrieben "ng-Blick." In diesem Fall möchten Sie festlegen "ng-app" zu "MyApp", das ist, was die Anwendung in der app.js benannt ist.

Abbildung 10 die Datei "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>

Beachten Sie, dass ich ein Content Delivery Network (CDN) für alle Client-seitige JavaScript Bibliothek benutze enthält. Sie können die Client-seitige Bibliotheken vor Ort mit Laube in der Befehlszeile ein (wie in der Regel für .NET Projekte mit NuGet mithilfe der Paket-Manager -Konsole) herunterladen. In der Microsoft .NET Framework Sie NuGet für clientseitige und serverseitige Pakete verwenden. Im Bereich Node.js ist jedoch Bower verwendet, um Client-seitige Bibliotheken/Pakete herunterladen, die NPM herunterladen und Installieren von Server-seitigen Bibliotheken/Pakete verwendet wird.

Für das Layout UI verwende ich eine Vanille bootstrap Thema, derjenige, der die Visual Studio ASP.NET MVC 5 Projektvorlage generiert.

Produktansicht

Für die Produktansicht (NodejsWebApp/public/app/views/products.html) sind nur ein paar Zeilen HTML erforderlich. Der erste Block ist die Kendo-Richtlinie für AngularJS um das Raster zu rendern:

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

Der zweite Block ist nur eine AngularJS-Vorlage für die benutzerdefinierte Ansicht Detail-Schaltfläche, die Sie das Raster-Symbolleiste hinzufügen.

Abbildung 11 zeigt die Produkt-Controller, NodejsWebApp/app/controllers/productController.js.

Abbildung 11 der Produkt-Controller

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

Um die Produkte-Grid-Hydrat, müssen Sie eine Kendo UI DataSource ($scope.options.dataSource) zu instanziieren. JayData bietet seine promoveaza REST-Endpunkte eine Hilfsmethode zum Initialisieren einer Kendo UI DataSource gebunden. Die JayData-AsKendoDataSourcehelper-Methode versteht zu erstellen die DataSource, basierend auf den Metadateninformationen von den promoveaza-Server (Http://localhost:1337/Northwindsvc), die dann verwendet wird, konfigurieren Sie die $data Instanz in NorthwindFactory in app.js veröffentlicht. Sehen Sie mehr von der Kendo-DataSource wenn ich visuelle Eindrücke mit dem Kendo DataViz-Chart-Framework nachweisen.

Zusammen mit den Out-of-the-Box-Schaltflächen (erstellen, speichern und Abbrechen) in das Raster Symbolleiste konfiguriert, fügen Sie eine benutzerdefinierte Schaltfläche zu einer anderen Ansicht navigieren, die die kompletten Details einer ausgewählten Produkt-Zeile ($scope.viewDetail) dargestellt wird. Beim Klicken auf die Schaltfläche "Details anzeigen" Eintritt Ereignis, das ausgewählte Produkt DataItem zu erhalten und, den AngularJS $location Dienst verwenden, navigieren Sie zu der Bearbeitungsansicht (MyNodejsWebApp/scripts/app/views/edit.html) für das Produkt.

Abbildung 12 zeigt die Edit.html-Datei, NodejsWebApp/public/app/views/edit.html.

Abbildung 12 die Edit.html-Datei

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

Beachten Sie, wie die Eingaben mit dem ng-Modell-Attribut versehen sind die AngularJS so deklarativ, der anzeigt, dass der Wert für diese Eingabe in einer Eigenschaft gespeichert werden, die auf dem Controller $scope der ng-Modell-Wert festgelegt ist, zu. Zum Beispiel in das erste Eingabefeld in dieser Ansicht, deren HTML-Element-Id soll ProductName (Id = "ProductName"), ng-Modell auf Produkt festgelegt ist.ProductName. Dies bedeutet, dass alles, was der Benutzer in das Eingabefeld ein (Textbox) eingibt, wird entsprechend der Wert für $scope.productName festgelegt. Darüber hinaus werden unabhängig $scope.product.productName in EditController programmgesteuert festgelegt wird, zu automatisch in den Wert des Eingabefeldes für ProductName übernommen.

Zum Beispiel beim ersten die Ansicht laden Laden Sie das Produkt durch die ID über URL übergeben, dann setzen Sie $scope.product auf dieses Produkt (siehe Abbildung 13).  Sobald dies geschieht, wird alles im Blick mit ng-Modell setzen auf $scope.property.* alle Eigenschaftenwerte in $scope.product widerspiegeln. In der Vergangenheit Entwickler in der Regel legen Werte in Eingabefeldern mit jQuery oder gerade JavaScript für jede Art von Manipulation des DOM. Beim Erstellen einer Anwendung mit dem MVVM-Muster (unabhängig von der Rahmen), hat sich bewährt, das DOM nur durch eine Änderung der ViewModel, nie direkt (z. B. mit JavaScript oder jQuery) zu manipulieren. Ich unterstelle nicht definitiv etwas falsch mit JavaScript oder jQuery gibt, aber wenn Sie sich entscheiden, eine Muster zu verwenden, um ein bestimmtes Problem (in meinem Fall, MVVM weiterhin die Trennung von Bereichen zwischen View, ViewModel und Modell) zu lösen, sollte es in der gesamten Anwendung konsistent sein.

Abbildung 13 editController.js Datei

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

Beachten Sie, dass Sie eine POST serverseitige Aktion auf Node.js implementieren könnte, das ist, was normalerweise mit der ASP.NET Web-API erfolgt. Allerdings ist der Zweck hier um veranschaulichen, wie mit Node.js und promoveaza dazu:

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

Für die Diagrammansicht (NodejsWebApp/public/app/views/chart.html) benötigen Sie nur eine Zeile von Markup:

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

Alles, was hier geschieht, wird die Kendo UI-Balkendiagramm-Richtlinie festlegen dieser Optionen zum Binden an eine Eigenschaft in der Steuerung mit dem Namen Optionen deklariert. Abbildung 14 zeigt die Produkt-Diagrammansicht und Abbildung 15 zeigt den Produkt-Grafik-Controller.

Produkt-Diagrammansicht
Abbildung 14-Produkt-Diagrammansicht

Abbildung 15 der Produkt-Grafik-Controller

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

Wie bei productController.js, hier werden Sie eingefügt auch die NorthwindFactory als Northwind in der Controller-Bau-Funktion wieder eine Kendo-DataSource anlegen, mit der JayData-AsKendoDataSource-Hilfsmethode. Hier finden Sie weitere Informationen darüber, was in der Grafik-Controller passiert:

$scope.options.series

  • Typ: Dadurch wird den Diagramm-Typ konfiguriert.
  • Feld: Das Feld aus der Model/Entität, die für den Reihenwert (x-Achse) verwendet werden.

$scope.options.valueAxis

  • MajorUnit: Das Intervall zwischen den wichtigsten Geschäftsbereiche. Wenn sich die valueAxis.type festgelegt ist, wird der MajorUnit-Wert für die Basis des Logarithmus verwendet.
  • PlotBands: Die Plot-Bands für das Diagramm die Produktmenge visuell verwendet werden. Unterschreitet die Menge einer bestimmten Ebene, muss der Benutzer das Produkt Wiederbelegung Prozess aufrufen.
  • Max: Der Maximalwert für die y-Achse.

$scope.options.categoryAxis

  • Feld: Die Feldbezeichnungen für die x-Achse.
  • Labels.Rotation: Der Grad um die Etiketten zu drehen. Hier konfigurieren Sie die Etiketten auf der x-Achse senkrecht zur laufenden x-Achse durch Festlegen des Werts auf-90 (Grad), d. h., die Bezeichnungen, die gegen den Uhrzeigersinn um 90 Grad zu drehen.
  • majorGridLines.visible: Die Hauptgitternetzlinien schaltet ein oder aus. Vielleicht möchten sie aus kosmetischen Gründen, um dem Diagramm einen Cleaner geben auszuschalten und mehr poliert aussehen.
  • ToolTip.Visible: QuickInfos wird dadurch, wenn ein Benutzer über ein vertikaler Balken bewegt.

Finden Sie in der Kendo-UI-Chart-API für Details unter bit.ly/1owgWrS.

Himmelblau Websitebereitstellung

Da der Quellcode bequem im CodePlex Git Repository gehostet wird, mithilfe von Azure Web-Sites kontinuierliche einzurichten Bereitstellung (kontinuierliche Lieferung) ist so einfach wie es geht:

  1. Navigieren Sie zu Ihrem Azure Web Site Dashboard und wählen Sie Set Bereitstellung aus der Quellcodeverwaltung.
  2. Wählen Sie Ihr Repository; in diesem Beispiel wählen Sie CodePlex.
  3. Klicken Sie auf Weiter.
  4. Wählen Sie Ihre CodePlex-Projekt.
  5. Wählen Sie die Verzweigung.
  6. Klicken Sie auf suchen.
  7. Für jeden Sync zum Git Repository erfolgt eine Erstellung und Bereitstellung.

Das war’s. Mit wenigen einfachen Mausklicks wird die Anwendung mit kontinuierlicher Integration und Bereitstellung bereitgestellt. Weitere Informationen zum Bereitstellen von mit Git, bit.ly/1ycBo9S.

Als .NET Entwickler ich bin gekommen, wie schnell wirklich zu genießen, und einfach es ist CRUD-schwere Anwendungen mit ASP.NET MVC, ASP.NET Web API, promoveaza, Entity Framework, AngularJS und Kendo UI erstellen. Nun kann durch die Entwicklung auf den mittleren Stapel, ich noch viel von dieser Domänenwissen und Erfahrung, mit Hilfe der JayData-Bibliotheken nutzen. Der einzige Unterschied zwischen den beiden Stapeln ist die serverseitige-Schicht. Wenn Sie mit ASP.NET MVC und ASP.NET Web API entwickeln, sollten nicht Node.js viele Probleme bereiten, da Sie bereits einige grundlegende JavaScript-Erfahrung haben. Finden Sie kompletten Source-Code für das Beispiel in diesem Artikel auf msdnmeanstack.codeplex.com, und eine live-Demo bei meanjaydatakendo.azurewebsites.net.


Lange Le ist der wichtigste app/Dev-Architekt bei CBRE Inc. und Telerik/Kendo UI MVP. Er verbringt seine Zeit mit der Entwicklung von Frameworks und Anwendungsblöcke, Beratung zur optimalen Vorgehensweise und Muster und Standardisierung der Enterprise-Technologie. In seiner Freizeit genießt er Blogging (blog.longle.net), spielen Call of Duty oder mentoring (codementor.io/lelong37). Sie erreichen ihn auf Twitter unter twitter.com/LeLong37.

Dank der folgenden technischen Experten für die Überprüfung dieses Artikels: Robert Balle (JayData), Burk Holland (WinForms) und Peter Zentai (JayData)