2016-06-01 1 views
2

Ich versuche, ein beobachtbares Array zu filtern, aber es treten Probleme auf, die ich glaube, dass die ko.utils.arrayFilter-Methode alle Feldnamen meines Modells ändert Kleinbuchstaben. Ich sollte beachten, Typoskript wird in diesem Projekt verwendet.Q.Promise und KO.mapping konvertiert Array von Objekten nicht in den richtigen Typ

Dies ist mein Modell:

export class MyListModel { 
    constructor(jsObject?: {}) { 
     if (jsObject) { 
      ko.mapping.fromJS(jsObject, {}, this); 
     } 
    } 
    Text = ko.observable<string>(); 
    Value = ko.observable<string>(); 
} 

In meinem Ansichtsmodell I das folgende Feld haben:

inches = ko.observableArray<Models.MyListModel>([]); 

In einem anderen Teil des Programms I die filterInches nennen() Methode der Array gegeben filtern einige Kriterien. Der Wertparameter ist der aktuell ausgewählte Wert in einem Dropdown-Menü.

filterInches(value) { 

     if (value == 6) { 
      var filtered = ko.utils.arrayFilter(this.inches(), 
       function (item) { 

        if (parseInt(item.Text()) <= 8) 
         return true; 
       }); 

      this.filteredInches(filtered); 
     } else { 
      this.filteredInches(this.inches()); 
     } 
    } 

Es werden keine Fehler bei der Kompilierung geworfen, aber wenn ich die Anwendung im Browser ausgeführt erhalte ich eine Fehlermeldung, dass „item.Text ist keine Funktion“. Wenn ich den Code in Chrome durchtrete, sieht es so aus, als würde das Objekt in ein anonymes Objekt mit einem Text- und Wertfeld konvertiert. Die Felder sind jetzt Kleinbuchstaben, was meiner Meinung nach die Probleme verursacht, denen ich begegne. Was könnte dieses Verhalten verursachen?

EDIT: Ich arbeite mit einem anderen aber verwandten Teil dieses Codes und ich denke, ich beginne zu sehen, warum es nicht funktioniert. Ich glaube, es hat etwas mit der Q-Promise-Bibliothek zu tun, aber ich verstehe nicht genug über diese Bibliothek, um herauszufinden, warum es nicht funktioniert (ja, ich lese die Dokumentation). Ich denke, die Entwickler, die diesen Code geschrieben haben, haben nicht gemerkt, dass sie nicht tun, was sie denken.

Das erste, was ich damit versucht habe, um zu überprüfen, dass etwas falsch ist, ist die Eigenschaftsnamen unseres Modell zu ändern:

export class MyListModel { 
constructor(jsObject?: {}) { 
    if (jsObject) { 
     ko.mapping.fromJS(jsObject, {}, this); 
    } 
} 
    Cat = ko.observable<string>(); 
    Chicken = ko.observable<string>(); 
} 

Nun, wenn wir uns auf einen überarbeiteten filterInches() -Methode zurück Ich werde das Element sehen. Cat funktioniert zur Kompilierzeit, aber wenn ich den Code in Chrome durchschreite, hat das Objekt tatsächlich keine Eigenschaft namens Cat (es ist nicht definiert). Seine Eigenschaften sind noch Text und Wert:

filterInches(value) { 

    if (value == 6) { 
     var filtered = ko.utils.arrayFilter(this.inches(), 
      function (item) { 

       if (parseInt(item.Cat()) <= 8) 
        return true; 
      }); 

     this.filteredInches(filtered); 
    } else { 
     this.filteredInches(this.inches()); 
    } 
} 

Das sagt mir, dass die Objekte wir von json Abrufen nicht abgebildet zu werden Objekte MyListModel. Ich glaube, dass die MyListModel-Klasse selbst in Ordnung ist.

Das Problem, das ich denke, ist aus dem Code ergeben, dass die Zoll in erster Linie bekommt:

refreshInches() { 
     this.DataService.getInches().done(entities => { 
      this.inches(entities); 
     }); 
    } 

und dann die getInches() -Methode wie folgt:

getInches(): Q.Promise<Array<Models.MyListModel>> { 
     return Q($.getJSON(this._baseUrl + 'GetInches')); 
    } 

ich denke, die Die ursprüngliche Absicht für diesen Code bestand darin, die Inch-Daten asynchron von einem Endpunkt abzurufen und die JSON-Daten in MyListModel-Objekte umzuwandeln. Wie ich bereits sagte, kenne ich Q.Promise nicht genug, um zu wissen, was mit der getInches() - Methode falsch sein könnte. Es ist mir jedoch klar, dass es einfach ein Array von anonymen Objekten aus den JSON-Daten zurückgibt.

Für die JSON-Objekte Referenz vom Endpunkt zurückgegeben wird wie folgt aussehen:

[{"text":"0","value":"0"},{"text":"1","value":"1"},...] 

Wer weiß, wie die getInches() Methode verbessert werden kann, tun, was es tun soll ist?

+3

KnockoutJS ist open source, ich habe [den 'arrayFilter'Quellcode] (https://github.com/knockout/knockout/blob/master/src/utils.js#L149) überprüft, aber gefunden, dass es doesn mach keinen Fall. Auf jeden Fall würden wir eine [mcve] brauchen, um zu helfen, jede Chance, die Sie versuchen könnten, eine zu machen (z. B. indem Sie den vollständigen Code, falls hilfreich/mögliches kompiliertes Javascript oder den vollständigen TS-Code) einbinden. – Jeroen

+1

"parseInt (item.Text)" gibt NaN zurück, wenn "item.Text" definiert ist als "Text = ko.observable ();" – TSV

+2

Diese Zeile sollte wahrscheinlich 'parseInt (item.Text(), 10) 'sein. – Jeroen

Antwort

2

Gemäß Ihrem Code soll getInches eine Q.Promise<Array<Models.MyListModel>>, d. H. Ein Versprechen für ein Array von MyListModel Objekte zurückgeben.

jedoch in seiner derzeitigen Form:

getInches(): Q.Promise<Array<Models.MyListModel>> { 
    return Q($.getJSON(this._baseUrl + 'GetInches')); 
} 

es tun das nicht. Das obige gibt ein Versprechen für <whatever the server gives you>, in diesem Fall offenbar eine Reihe von einfachen Objekten.

es zu ändern, so dass es den vermeintlichen Typ übereinstimmt, wir also jetzt

getInches(): Q.Promise<Array<Models.MyListModel>> { 
    var jqXhr = $.getJSON(this._baseUrl + 'GetInches'); 

    return Q(jqXhr).then(data => { 
     return data.map(item => { 
      return new Models.MyListModel(item); 
     }); 
    }); 

    // or, if you must 
    return Q(jqXhr).then(data => data.map(item => new Models.MyListModel(item))); 
} 

tun konnte, können wir die frisch erstellten MyListModel Instanzen in einer beobachtbaren speichern:

refreshInches() { 
    this.DataService.getInches().done(this.inches); 
} 

Beachten Sie, dass this.inches eine ist Observable und Observables sind Funktionen, die es uns erlauben, sie direkt als Callback zu verwenden.

Eine Funktion, die als Promise-Handler verwendet wird, erhält den versprochenen Wert als erstes Argument. Ein beobachtbares Objekt speichert den Wert des ersten Arguments, mit dem Sie es aufrufen, also passt das gut.


Darüber hinaus ist Ihr filteredInches Ansatz zu kompliziert. Anstatt es zu einer Funktion zu machen, die Sie aufrufen müssen, machen Sie es zu einer berechneten, die von einigen beobachtbaren in Ihrem Viewmodel abhängt. Auf diese Weise wird es niemals mit dem Rest inkonsistent sein.

this.filteredInches = ko.pureComputed(() => { 
    var value = this.value(); 

    return ko.utils.arrayFilter(this.inches(), item => { 
     value != 6 || +item.Text() <= 8; 
    }); 
}); 

Ein Blick auf Ihre Viewmodel Schöpfung, würden Sie wahrscheinlich wollen einen numerischen beobachtbar (oder berechnet) irgendwo, so dass Sie brauchen mich nicht erinnern, die Text Eigenschaft in Berechnungen typ zu konvertieren.

+0

Hey @Tomalak vielen Dank für die sehr detaillierte Antwort. Es war sehr hilfreich. Ich denke, ich bin zu 99% dort! Ich bin so weit gegangen, Ihre Änderungen auf getInches() und refreshInches() anzuwenden, und der JSON wird irgendwie meinem Modell zugeordnet. Das Array von MyModel-Objekten wird mit den folgenden Requisiten gefüllt: Cat: Observable(), Chicken: Observable(), Text: Observable(), Wert: Observable(). 'This.inches() [0] .Cat()' gibt "undefined" zurück, aber 'this.inches() [0] .text()' gibt "0" zurück, was der Wert ist, den ich brauche. – Bruno

+1

Ja, so funktioniert das Mapping Plugin. Wenn keine zusätzlichen Optionen vorhanden sind (wie in diesem Fall), werden alle Eigenschaften des Eingabeobjekts in Observablen auf dem Zielansichtsmodell abgebildet. Ein Eingabeobjekt '{text:" bla "}' wird in ein Objekt '{text: ko.beobachtbar ("bla")} '. Die Observables, die Ihr Ziel-Viewmodel bereits hat ('Cat') wird entweder gefüllt (wenn ihr Name übereinstimmt) oder alleine gelassen. Da sich im Eingabeobjekt kein 'Cat' befindet, bleibt das' Cat' im Viewmodel undefiniert. Anscheinend gibt es einen 'Text' in der Eingabe, der in Ihrem Viewmodel abgebildet wird, also sehen Sie das. – Tomalak

+0

Sie sind die Besten, danke! – Bruno

Verwandte Themen