Dhanuka777
Dhanuka777

Reputation: 8636

Knockout access object values from observable array

My View Model has an observable array of objects of WizardSteps,

var steps = [
        new WizardStep(1, "step1", viewModel1), 
        new WizardStep(2, "step2", viewModel2),
    ];

self.stepModels = ko.observableArray(steps)

WizardStep just has an id, name and a personViewModel. viewModel1, viewModel2 are two PersonViewModels which contains name, age and phone number.

I can access stepModels by index $root.stepModels()[0].viewModel.name and get the name of the viewModel1. But I need to access it by step name, e.g.'step1'.

How can I do this?

Upvotes: 0

Views: 2646

Answers (3)

Stuart Bourhill
Stuart Bourhill

Reputation: 638

I had to create a 'wizard' type feature using knockout sometime ago. You could do something like this:

var steps = ko.observableArray([
        { stepName: "step1", person: viewModel1 },
        { stepName: "step1", person: viewModel2 }
    ]);

Firstly, it is worth noting that your steps array need not be an observable if you're not going to be modifying it once it has been populated. Second, you also might not need to have a step number - I assume that is what the 1 represents in the line:

new WizardStep(1, "step1")

Because arrays are ordered you're storing information you already have which is contained in the index of each element in your steps array. i.e. steps[0] would be step1 and so forth. If you need to keep track of where you are in your wizard you can create an observable in your viewModel and a function to set the step you're currently on like so:

var self = this;
self.currentStep = ko.observable(0); // starting step

self.goToStep = function(index){
  self.currentStep(index); 
};

Or you could:

var self = this;
self.currentStep = ko.observable(steps()[0]);  // starting step

self.goToStep = function(index){
  self.currentStep(steps()[index]);
  // if you only need the viewModel associated with this step you could use:
  self.currentPerson(steps()[index].viewModel.name);
};

In your view you can then use a knockout if binding to conditionally show or hide the step you are currently on / simply render the viewModel held in self.currentStep() and data-bind to a click event, for example.

If you really want to be able to access the step by stepName then you can use knockouts arrayFirst utility function:

self.getStepByName = function(stepName){
  return ko.utils.arrayFirst(steps(), function(step) {
        return step.stepName() === stepName;
  });
};

I'll let you to fill in the blanks and the missing declarations. You could also do something using computeds or writable computeds. At the end of the day there are many ways to skin a cat. I'm sure any one of the solutions offered here are viable.

Upvotes: 1

user3297291
user3297291

Reputation: 23397

If you need to access the models often, it might be easier and faster to create an object that uses the step value as a key:

var WizardStep = function(id, name) {
  this.id = id;
  this.name = name;
};

var steps = [
  new WizardStep(1, "step1"),
  new WizardStep(2, "step2"),
];

var stepsByName = steps.reduce(function(result, step) {
  result[step.name] = step;
  return result;
}, {});

// The object:
console.log(JSON.stringify(stepsByName, null, 2));

// Get a step by name:
console.log(stepsByName["step1"]);

Upvotes: 1

TSV
TSV

Reputation: 7641

You can use "filter" array function:

var foundModel = <container object>.stepModels().filter(function(model) { return model.name === "modelName"; })[0];
if(!!foundModel) {
    // some code working with found model
}

Note

It is better to hold such a function in the model and not to put calculations into html markup.

Upvotes: 1

Related Questions