35 Stimmen

Knockout.js Jedes verschachtelte Objekt zu einem Observable machen

Ich verwende Knockout.js als MVVM-Bibliothek, um meine Daten an einige Seiten zu binden. Ich baue derzeit eine Bibliothek, um REST-Aufrufe an einen Webdienst zu machen. Mein RESTful-Webdienst gibt eine einfache Struktur zurück:

{
    id : 1,
    details: {
        name: "Johnny",
        surname: "Boy"
    }
}

Ich habe ein beobachtbares Haupt-Elternteil, myObject . Wenn ich das tue

myObject(ko.mapping.fromJS(data))

die Observablen in myObject sind:

  • id
  • name
  • surname

Wie kann ich die details (und theoretisch jedes Objekt in der Struktur ein Observable)? Ich brauche dieses Verhalten, so dass ich eine berechnete observable auf Details setzen kann und bemerkt werden, sobald einer der internen Daten ändert.

Ich habe eine einfache rekursive Funktion eingerichtet, die das Problem lösen sollte. Das tut sie natürlich nicht, myObject.details wird nicht zu einer Beobachtungsgröße.

// Makes every object in the tree an observable.
var makeAllObservables = function () {
    makeChildrenObservables(myObject);
};
var makeChildrenObservables = function (object) {
    // Make the parent an observable if it's not already
    if (!ko.isObservable(object)) {
        if ($.isArray(object))
            object = ko.observableArray(object);
        else
            object = ko.observable(object);
    }
    // Loop through its children
    for (var child in object()) {
        makeChildrenObservables(object()[child]);
    }
};

Ich bin mir ziemlich sicher, dass es etwas mit falschen Referenzen zu tun hat, aber wie kann ich das Problem lösen? Ich danke Ihnen.

23voto

Paolo del Mundo Punkte 2121

Ich würde die Knockout Mapping Plugin .

var jsonData = {
    id : 1,
    details: {
        name: "Johnny",
        surname: "Boy"
    }
}

var yourMapping = {
    'details': {
        create: function(options) {
            return Details(options.data);
        }
    }
}

function Details(data) {
    ko.mapping.fromJS(data, {}, this);
}

function YourObjectName() {
    ko.mapping.fromJS(jsonData, yourMapping, this);
}

Dadurch wird Ihre Objekthierarchie mit allen untergeordneten Objekten als Observablen erstellt.

14voto

Jason Goemaat Punkte 27951

Ich glaube nicht, dass Knockout über eine eingebaute Möglichkeit verfügt, Änderungen an untergeordneten Elementen zu beobachten. Wenn ich Ihre Frage richtig verstehe, möchten Sie, dass bei einer Namensänderung eine Änderung der Details als Entität bemerkt wird. Können Sie ein konkretes Beispiel dafür geben, wie Sie dies nutzen würden? Würden Sie ein Abonnement für die Details Observable verwenden, um eine Aktion durchzuführen?

Der Grund, warum Ihr Code nicht machen Details eine beobachtbare ist, weil Javascript ist pass by value, so dass die Änderung der Wert der "Objekt"-Argument in Ihrer Funktion nicht den tatsächlichen Wert, den Sie übergeben, nur den Wert des Arguments innerhalb Ihrer Funktion.

bearbeiten

Wenn Änderungen wird automatisch an die Eltern weitergegeben werden, sollte dies alle Kinder beobachtbar machen, denke ich, aber Ihre Wurzel, die Sie das erste Mal übergeben sollte bereits eine beobachtbare sein.

// object should already be observable
var makeChildrenObservables = function (object) {
    if(!ko.isObservable(object)) return;

    // Loop through its children
    for (var child in object()) {
        if (!ko.isObservable(object()[child])) {
            object()[child] = ko.observable(object()[child]);
        }
        makeChildrenObservables(object()[child]);
    }
};

3voto

Beans Punkte 1010

Meiner Erfahrung nach macht ko.mapping.fromJS kein Observable aus einem Objekt.

Nehmen wir an, Sie haben diesen ViewModel-Konstruktor:

var VM = function(payload) {
  ko.mapping.fromJS(payload, {}, this);
}

und dieses Datenobjekt:

var data1 = {
  name: 'Bob',
  class: {
    name: 'CompSci 101',
    room: 112
  }

}

und Sie verwenden data1, um VM1 zu erstellen:

var VM1 = new VM(data1);

Dann wird VM1.class kein ko.observable sein, sondern ein einfaches Javascript-Objekt.

Wenn Sie dann ein anderes Viewmodel erstellen, das ein Datenobjekt mit einem Null-Klassenmitglied verwendet, d.h.:

var data2 = {
  name: 'Bob',
  class: null
}
var VM2 = new VM(data2);

dann ist VM2.class ein ko.observable.

Wenn Sie dann ausführen:

ko.mapping(data1, {}, VM2)

dann bleibt VM2.class ein ko.observable.

Wenn Sie also ein ViewModel aus einem Seed-Datenobjekt erstellen, bei dem die Objektmitglieder null sind, und dann ein Popuplate mit einem gefüllten Datenobjekt erstellen, erhalten Sie beobachtbare Klassenmitglieder.

Dies führt zu Problemen, da die Objektmitglieder manchmal observable sind und manchmal nicht. Formularbindungen funktionieren mit VM1 und nicht mit VM2. Es wäre schön, wenn ko.mapping.fromJS immer alles zu einem ko.observable machen würde, damit es konsistent ist?

3voto

AR M Punkte 273

Durch die Verwendung von Knockout-Plugin Wir haben viele Möglichkeiten zu verwalten, wie wir unsere Daten beobachtbar machen wollen.

Hier ist ein Beispielcode:

var data = {
    people: [
        {
            id: 1,
            age: 25,
            child : [
                {id : 1,childname : "Alice"},
                {id : 2,childname : "Wonderland"}
            ]
        }, 
        {id: 2, age: 35}
    ],
    Address:[
        {
            AddressID : 1,
            City : "NewYork",
            cities : [
                {
                    cityId : 1,
                    cityName : "NewYork"
                },
                {
                    cityId :2,
                    cityName : "California"
                }
            ]
        },
        {
            AddressID : 2,
            City : "California",
            cities : [
                {
                    cityId :1,
                    cityName : "NewYork"
                },
                {
                    cityId :2,
                    cityName : "California"
                }
            ]
        }
    ],
    isSelected : true,
    dataID : 6
};
var mappingOptions = {
    people: {
        create: function(options) {
            console.log(options);
            return ko.mapping.fromJS(options.data, childmappingOptions);
        }
    },
    Address: {
        create: function(options) {
            console.log(options);
            return ko.mapping.fromJS(options.data, childmappingOptions);
        }
    }
};
var childmappingOptions = {
    child: {
        create: function(options) {
            return ko.mapping.fromJS(options.data, { observe: ["id","childname"]});
        }
    },
    cities :{
        create: function(options) {
            return ko.mapping.fromJS(options.data, { observe: ["cityId","cityName"]});
        }
    }
};
var r = ko.mapping.fromJS(data, mappingOptions);

Ich habe eine funktionierende Fiedel beigefügt: http://jsfiddle.net/wmqTx/5/

2voto

AlexRebula Punkte 813

Ich werde die Paolo del Mundo Antwort (die meiner Meinung nach im Moment die beste und einzige Lösung ist) mit meiner Beispiellösung zu vergleichen.

Erwägen Sie frapontillo das ursprüngliche Objekt:

{
    id : 1,
    details: {
        name: "Johnny",
        surname: "Boy"
    }
}

En details Eigenschaft selbst ist ein Objekt und kann als solches kein Observable sein. Das Gleiche gilt für die User Eigenschaft im folgenden Beispiel, die ebenfalls ein Objekt ist. Diese beiden Objekte können keine Observablen sein aber ihre LEAF-Eigenschaften können sein .

Jede Blatteigenschaft Ihres Datenbaums/Modells KANN EIN OBSERVABLE SEIN. Am einfachsten lässt sich das erreichen, indem Sie das Mapping-Modell richtig zu definieren, bevor es an das Mapping-Plugin übergeben wird als Parameter.

Siehe mein Beispiel unten.

BEISPIEL:

Nehmen wir an, wir müssen eine HTML-Seite / Ansicht anzeigen, auf der wir eine Liste von Benutzern in einem Raster haben. Neben dem Benutzerraster wird ein Formular zum Bearbeiten eines ausgewählten Benutzers aus dem Raster angezeigt.

SCHRITT 1: DEFINITION DER MODELLE

function UsersEdit() {
    this.User = new User();                    // model for the selected user      
    this.ShowUsersGrid = ko.observable(false); // defines the grid's visibility (false by default)
    this.ShowEditForm = ko.observable(false);  // defines the selected user form's visibility (false by default)      
    this.AllGroups = [];                       // NOT AN OBSERVABLE - when editing a user in the user's form beside the grid a multiselect of all available GROUPS is shown (to place the user in one or multiple groups)
    this.AllRoles = [];                        // NOT AN OBSERVABLE - when editing a user in the user's form beside the grid a multiselect of all available ROLES is shown (to assign the user one or multiple roles)
}

function User() {
    this.Id = ko.observable();
    this.Name = ko.observable();
    this.Surname = ko.observable();
    this.Username = ko.observable();
    this.GroupIds = ko.observableArray(); // the ids of the GROUPS that this user belongs to
    this.RoleIds = ko.observableArray();  // the ids of the ROLES that this user has
}

SCHRITT 2: MAPPING (UM VERSCHACHTELTE BEOBACHTUNGSWERTE ZU ERHALTEN)

Nehmen wir an, dies ist Ihr rohes JSON-Modell mit Daten, die Sie zuordnen und ein KO-Modell mit verschachtelten Observables erhalten möchten.

var model = {
    User: {
        Id: 1,
        Name: "Johnny",            
        Surname = "Boy",
        Username = "JohhnyBoy",
        GroupIds = [1, 3, 4],
        RoleIds = [1, 2, 5]
    }
};

Nun, da dies alles definiert ist, können Sie die Karte erstellen:

var observableUserEditModel = ko.mapping.fromJS(model, new UsersEdit());

UND SIE SIND FERTIG! :)

Das observableUserEditModel enthält alle Ihre Observables, auch verschachtelte. Das Einzige, worum Sie sich jetzt kümmern müssen, um dies zu testen, ist die Bindung der observableUserEditModel Objekt mit Ihrem HTML. Tipp: Verwenden Sie die with Bindung und Prüfung der beobachtbaren observableUserEditModel Datenstruktur und fügen diese in Ihre HTML-Ansicht ein:

<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X