Sie sind hier: Weblog

ASP.NET MVC 4 SPA: Überblick und Beispiel zu knockout.js

Foto ,
22.06.2012 17:51:00

Die sich gerade in Entwicklung befindliche Bibliothek upshot.js, welche kurz nach dem Erscheinen von ASP.NET MVC 4 nachgeliefert wird, soll das Zusammenspiel zwischen JavaScript und Services vereinfachen. Dazu können bestimmte REST-basierte Services als Datenquellen, welche über ein DataSoruce-JavaScript-Objekt angesprochen werden können, betrachtet werden.

Das nachfolgende Listing zeigt zur Demonstration, wie dieses DataSource-Objekt verwendet werden kann. HotelBuchung ist hier eine Konstruktor-Funktion, welche ein Knockout.js-basiertes View-Model für eine vom Service erhaltene HotelBuchung erzeugt. Diese Funktion ist sehr generisch gestaltet: upshot.map(...) erzeugt für jede Eigenschaft im übergebenen Objekt properties ein Knockout.js-basiertes Observable (bzw. Observable-Array) im von der Konstruktor-Funktion erzeugten Objekt. Im Zuge dessen wird auch der serverseitige Typ als String im Format „Assembly:#Namespace.Klasse“ angegeben. upshot.addEntityProperties(…) fügt dem zu erstellenden Objekt darüber hinaus einige Methoden hinzu, die upshot.js benötigt.

Nach dem Laden der Seite übergibt die hier abgebildete View dem Objekt upshot unter Verwendung dessen Methode metadata die benötigten Metadaten. Diese werden durch die serverseitige Routine Html.Metadata, welche via Reflection die entsprechende serverseitige Klasse analysiert, in Erfahrung gebracht. Anschließend kann eine DataSource, welche sich auf einen REST-Service bezieht erstellt werden. Neben der Url des Services und dem Namen der gewünschten Service-Operation wird auch die formale Bezeichnung des erwarteten Typs der bereitzustellenden Objekte sowie die Konstruktor-Funktion, welche zum Erzeugen der benötigten View-Models heranzuziehen ist, angegeben.

Anschließend werden typische Operationen, wie Filtern, Sortieren oder Paging auf die Daten angewandt. Die an refresh übergebene Funktion wird schließlich zur Ausführung gebracht, wenn die gewünschten Objekte abgerufen wurden. Alternativ dazu können auch die abgerufenen Entitäten via getEntities, welches ein Array mit den jeweiligen Objekten liefert, ermittelt werden.

<script src="~/Scripts/jquery-1.6.4.js" type="text/javascript"></script>
<script src="~/Scripts/upshot.js" type="text/javascript"></script>
<script src="~/Scripts/knockout-2.0.0.js" type="text/javascript"></script>
<script src="~/Scripts/upshot.knockout.extensions.js" type="text/javascript">
    </script>
<script src="~/Scripts/upshot.compat.knockout.js" type="text/javascript">
    </script>
<script src="~/Scripts/bindings.js" type="text/javascript"></script>
 

<script language="javascript">

function HotelBuchung(properties) {
    var self = this;
 
    upshot.map(properties, "HotelBuchung:#HotelReservierung.Data", self); 
    upshot.addEntityProperties(self); 
}
 
 
$(function () { 
 
    upshot.metadata(@(Html.Metadata<HotelReservierung.Controllers.BuchungenController>())); 
 
    dataSource = upshot.RemoteDataSource({
        providerParameters: { 
               url: "/data/Buchungen", operationName: "GetBuchungen" },
        //provider: …,
        entityType: "HotelBuchung:#HotelReservierung.Data",
        mapping: HotelBuchung
    }); 
 
    var buchungen = dataSource.getEntities();
 
    dataSource.setSort({ property: "Datum", descending: true });
 
    dataSource.setFilter({ property: "HotelId", value: 1 });
 
    dataSource.setPaging({ skip: 0, take: 2, includeTotalCount: true });
 
    dataSource.refresh(function (rows, count) {
            
        var pInfo = $("#info");
        pInfo.append("rows[0].Vorname(): " + rows[0].Vorname() + "<br>");
        pInfo.append("buchungen()[0].Vorname(): " + buchungen()[0].Vorname() + "<br>");
        pInfo.append("count: " + count);
            
        rows[0].Preis(rows[0].Preis() + 5);
           
    });
 
});
</script>

Durch Bereitstellung eines Providers im Zuge der Erzeugung eines DataSource-Objektes kann der Brückenschlag zu verschiedenen REST-basierten Services gemacht werden. Wird, wie hier, kein Provider definiert, muss der REST-Service einen bestimmten Aufbau haben. Wie das nachfolgende Listing zeigt, muss es sich in einem solchem Fall um einen auf ASP.NET WebAPI-basierenden Service, welcher von DbDataController erbt, handeln. Als Typ-Parameter ist ein Entity-Framework-Context anzugeben. Die einzelnen Methoden werden anhand von Namenskonventionen erkannt (Prefix Update, Insert, Delete). Methoden, welche Daten abrufen, müssen IQueryable liefern.

public partial class BuchungenController : DbDataController<HotelDbContext>
{
    public IQueryable<HotelBuchung> GetBuchungen()
    {
        return DbContext.HotelBuchung;//.Where(b => b.Datum.Date == datum.Date);
    }
 
    public void UpdateHotelBuchung(HotelBuchung b)
    {
        UpdateEntity(b);
    }
 
    public void InsertHotelBuchung(HotelBuchung b)
    {
        InsertEntity(b);
    }
 
    public void DeleteHotelBuchung(HotelBuchung b)
    {
        DeleteEntity(b);
    }
}

Das gesamte Beispiel hierzu findet sich unter [1].

[1] http://www.softwarearchitekt.at/downloads/MVC4_SPA.zip