Vraag Knockout.js Maak van elk genest object een waarneembaar object


Ik gebruik Knockout.js als een MVVM-bibliotheek om mijn gegevens aan enkele pagina's te koppelen. Ik ben momenteel bezig met het bouwen van een bibliotheek om REST-oproepen naar een webservice te maken. Mijn RESTful-webservice retourneert een eenvoudige structuur:

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

Ik heb een waarneembare hoofdouder, myObject. Wanneer ik het doe

myObject(ko.mapping.fromJS(data))

de waarnemingen in myObject zijn:

  • id
  • name
  • surname

Hoe kan ik maken details (en theoretisch is elk object in de structuur waarneembaar)? Ik heb dit gedrag nodig zodat ik een berekende waarneembaarheid op details kan instellen en opgemerkt kan worden zodra een van de interne gegevens verandert.

Ik heb een elementaire recursieve functie opgezet die het zou moeten doen. Het is natuurlijk niet myObject.details wordt niet waarneembaar.

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

Ik ben er vrij zeker van dat het iets is met onjuiste referenties, maar hoe kan ik dit oplossen? Dank je.


34
2018-05-11 16:21


oorsprong


antwoorden:


Ik denk niet dat knock-out een ingebouwde manier heeft om veranderingen in onderliggende elementen waar te nemen. Als ik uw vraag begrijp, wanneer iemand de naam die u wilt verandert, verandert de details als een te opvallende entiteit. Kunt u een concreet voorbeeld geven van hoe u dit zou gebruiken? Zou je een abonnement op de waarneembare details gebruiken om wat actie te ondernemen?

De reden dat je code details niet zichtbaar maakt, is omdat javascript door de waarde wordt gepasseerd, dus het wijzigen van de waarde van het 'object'-argument in je functie verandert niets aan de werkelijke waarde die je hebt gepasseerd, alleen de waarde van het argument in je functie.

Bewerk

Als veranderingen zullen automatisch doorgeven aan de ouders, dit zou alle kinderen waarneembaar moeten maken denk ik, maar je wortel die je de eerste keer passeert, zou al waarneembaar moeten zijn.

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

14
2018-05-12 06:47



Ik zou de gebruiken knock-outmapping-plug-in.

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

Hiermee wordt uw objectenhiërarchie met alle kinderen als waarneembaar gemaakt.


22
2018-05-11 18:54



Van wat ik heb meegemaakt maakt ko.mapping.fromJS niet een waarneembaar object.

Laten we zeggen dat je deze ViewModel-constructor hebt:

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

en dit data-object:

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

}

en je gebruikt data1 om VM1 aan te maken:

var VM1 = new VM(data1);

Dan VM1.class zal geen ko.observable zijn, het is een eenvoudig javascript Object.

Als u vervolgens een ander viewmodel maakt met een gegevensobject met een null-klasse-lid, dat wil zeggen:

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

dan is VM2.class een ko.observable.

Als je dan uitvoert:

ko.mapping(data1, {}, VM2)

dan blijft VM2.class een ko.observable.

Dus als u een ViewModel maakt van een seed-gegevensobject waarbij objectleden null zijn en ze vervolgens met een ingevuld gegevensobject tonen, hebt u waarneembare klassen.

Dit leidt tot problemen, omdat de objectleden soms waarnemingen zijn en soms ook niet. Formulierbindingen werken met VM1 en werken niet met VM2. Het zou leuk zijn als ko.mapping.fromJS altijd alles een ko.observable maakte, zodat het consistent was?


3
2018-02-27 03:23



Door het gebruiken van Knockout-Plugin we kunnen onderliggende elementen waarneembaar maken. We hebben veel opties om te beheren hoe we willen dat onze gegevens waarneembaar zijn.

Hier is een voorbeeldcode:

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

Ik heb een werkende fiddle bijgevoegd: http://jsfiddle.net/wmqTx/5/


3
2017-07-10 07:30



Ik zal uitbreiden Paolo del MundoHet antwoord (dat volgens mij op dit moment de beste en de enige oplossing zou kunnen zijn) met mijn voorbeeldoplossing.

Overwegen frapontilloorigineel object:

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

De details eigendom zelf is een object en kan als zodanig NIET waarneembaar zijn. Hetzelfde geldt voor de User eigenschap in het onderstaande voorbeeld, dat ook een object is. Die twee objecten kunnen geen waarneembare dingen zijn maar hun LEAF-eigenschappen kunnen dat wel zijn.

Elke bladeigenschap van uw gegevensboom / model KAN EEN WAARNEMING ZIJN. De gemakkelijkste manier om dat te bereiken, is dat jij correct het toewijzingsmodel definiëren voordat het wordt doorgegeven aan de mapping-plug-in als parameter.

Zie mijn voorbeeld hieronder.

VOORBEELD:

Laten we zeggen dat we een html-pagina / weergave moeten weergeven waar we een lijst met gebruikers in een raster hebben. Naast het gebruikersraster wordt een formulier getoond voor het bewerken van een geselecteerde gebruiker uit het raster.

STAP 1: DE MODELLEN DEFINIËREN

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
}

STAP 2: KAARTEN (NESTELIJKE OBSERVABLES KRIJGEN)

Laten we zeggen dat dit uw onbewerkte JSON-model is met gegevens die u in kaart wilt brengen en een KO-model met geneste observables.

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

Nu dit allemaal is gedefinieerd, kunt u toewijzen:

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

EN JE BENT KLAAR! :)

Het waarneembareUserEditModel bevat al je waarneembare, zelfs geneste. Het enige waar je voor moet zorgen om dit te testen is om het te binden observableUserEditModel object met uw HTML. Tip: gebruik de with binden en test het waarneembare observableUserEditModel  gegevensstructuur invoegen in uw HTML-weergave:

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

2
2018-04-04 13:59



Misschien kan er in een toekomstige versie een configuratieoptie zijn die ervoor zorgt dat ko.mapping.fromJS altijd waarneembare objecten maakt. Het kan worden ingeschakeld voor nieuwe projecten of na het bijwerken van de bindingen van een bestaand project.

Wat ik doe om dit probleem te voorkomen, is ervoor te zorgen dat modelzaden op elk niveau altijd eigenschappen voor Objectleden hebben ingevuld. Op deze manier worden alle objecteigenschappen toegewezen als POJO's (Plain Old Javascript Objects), dus het ViewModel initialiseert ze niet als ko.observables. Het vermijdt het probleem "soms waarneembare, soms niet".

Beste wensen, Mike


1
2018-02-28 03:32