Using jQuery dynatree with Knockout and Breeze

2019-03-03 20:23发布

OK, following the suggestion from PW Kad I'm splitting this part of the question off from where it started on question ID 17973991.

I have a viewmodel that utilises a datacontext built around breeze and it fetches the data I want and populates observable arrays. I have a requirement to use data already retrieved by Breeze to populate another (observable) array to use in a treeview.

As the existing data does not have the correct fieldnames, I need to be able to create a new array with correct fieldnames that the dynatree/fancytree plugin can use.

My first attempt: (subsequently shown to not work so don't do this!)

So in my viewmodel I added the following at the top of the .js file:

var treeMaterials = ko.observableArray();

var treeMaterial = function (data) {
    var self = this;

    self.name = ko.observable(data.name);
    self.id = ko.observable(data.id);
    self.children = ko.observableArray();

    $.each(data.children, function (index, item) {
        self.children.push(new Person(item));
    });
};

I then added an "asTreeMaterials" method to my module:

var asTreeMaterials = function (treeMatsObservable, matsObservable) {
    treeMatsObservable([]); //clear out array as we're rebuilding it in here
    var tmpArray = treeMatsObservable(); //create local temp array to avoid ko notifications on each push

    $.each(matsObservable, function (index, mat) {
        tmpArray.push(new treeMaterial({
            id: mat.id,
            name: mat.materialName,
            children: []
        }));
    });

    treeMatsObservable(tmpArray);
};

(borrowing heavily from John Papa's coding there, thanks John!) Note: there will be more code going into the "children" bit once I have the basics working

And finally changing the "activate" method to use the new method:

var activate = function () {
    // go get local data, if we have it
    return datacontext.getMaterialPartials(materials),
            asTreeMaterials(treeMaterials, materials);
};
....

and then returning the new array from the module:

var vm = {
    activate: activate,
    materials: materials,
    treeMaterials: treeMaterials,
    title: 'My test app page 1',
    refresh: refresh
};

means that I don't hit the server again for the treeview version of the data.

Edit 2. Following the guidance from PW Kad on the other question (will be added to this question shortly) I have modified the "asTreeMaterials" method as follows:

var asTreeMaterials = function () {
    treeMaterials([]); //clear out array as we're rebuilding it in here
    var matArray = materials().slice();
    var tmpArray = [];

    $.each(matArray, function (index, mat) {
        tmpArray.push(new treeMaterial({
            id: mat.id,
            name: mat.materialName,
            children: []
        }));
    });

    treeMaterials(tmpArray);
};

The reason (I think) I have to create a separate new array is that the existing "materials" observable that I slice does not contain the correct properties. Dynatree/fancytree requires (among other things) an "ID" and a "name". I have the ID, but I have "materialName" in the materials observable hence the "$.each" on the array created by the slicing of the materials observable to push the "materialname" property into the "name" property in my new array (tmpArray). I'm new to all this, I may be miles off the mark here!

Do I actually need an observable array...? I don't think I do if I understand what observable arrays are for... my materials are pretty much set in stone and will change very, very rarely. I presume I can simply leave "treeMaterials" as a standard javascribt object array and return that in the viewmodel instead of making it an observableArray?

Either way, currently the values for materialname and ID are not passed into the relevant properties in the tmpArray I'm making. Instead I'm getting the functions from the materials observable so I think I need to approach this with an "unwrap" of some sort to get at the actual values?

1条回答
We Are One
2楼-- · 2019-03-03 21:00

You are not populating the treeMaterials because you don't have any data in materials when you are sending it to asTreeMaterials. I am making some assumptions here but basically it looks like this is what you are trying to do -

At the top of your view model, I assume you have two observableArrays

var treeMaterials = ko.observableArray();
var materials = ko.observableArray();

For your activate method, you need to go get some data, and then when your datacontext returns a promise, go make a tree out of it of some object type -

var activate = function () {
    return datacontext.getMaterialPartials(materials).then(
            makeMyTree);
};

You don't need to pass treeMaterials or materials because they are within the scope of the view model already, and you are just trying to make a tree of objects out of your materials.

var makeMyTree = function () {
    treeMaterials([]);
    ko.utils.arrayForEach(materials(), function (mat) {
        treeMaterials.push(new treeMaterial(mat));
    });
};

This is going to make an observableArray of objects with observable properties, meaning if you are passing them or trying to get their value you would need to use something like treeMaterials()[0].name().

In case your dynatree doesn't take observables, or isn't playing well with them

I am not sure how your dynatree or w/e works with observables, so here is a standard array of non-observable objects instead of an observable array -

var treeMaterials = [];

var makeMyTree = function () {
    treeMaterials[];
    ko.utils.arrayForEach(materials(), function (mat) {
        treeMaterials.push(new treeMaterial(mat));
    });
};

var treeMaterial = function (data) {
    var self = this;

    self.name = data.name;
    self.id = data.id;
    self.children = [];

    $.each(data.children, function (index, item) {
        self.children.push(new Person(item));
    });
};
查看更多
登录 后发表回答