1

I have an ASP.NET Web API method that returns a list of products:

public async Task<IEnumerable<Product>> Get()
{
    var products = new IEnumerable<Product>();
    // Data populated here
    return products;
}

This API call is used by a Knockout.js observableArray:

// Define viewmodel
var myViewModel = {
    products = ko.observableArray([]);
};

// Apply knockout bindings
ko.applyBindings(vm);

// Function that obtains product data
function listProducts(viewmodel) {
    var productsQuery = "api/products/get";

    // Send an AJAX request
    $.getJSON(productsQuery).done(viewmodel.products);
}

I would like to apply a click binding to these products, which would be a method named myClickFunction in this case. If possible, the method should be a function belonging to the objects that are returned by my Web API call :

<ul data-bind="foreach: products">
    <li class="item" data-bind="text: productName, click: myClickFunction">
    </li>
</ul>

Is it possible for me to add a function to the Web API result, which I can then use in the observableArray?

miguelarcilla
  • 1,426
  • 1
  • 20
  • 38
  • So do you mean there will be only one function to all rows myClickFunction and onclick of li you want to call myClieckFunction for perticular row? – Dnyanesh Feb 14 '15 at 04:04
  • @Dnyanesh yes, I would like an action to happen to each product when I click it. I tried jQuery binding, but the binding doesn't work for dynamically added elements. – miguelarcilla Feb 14 '15 at 05:54
  • 2
    Do you mean that the code of your `myClickFunction` comes from the server (returned by Web API call), or that you want to decorate each object with this function in your client code? If this is the former (which is not a good idea, actually), see this [question](http://stackoverflow.com/questions/5373540/json-serializing-an-object-with-function-parameter), it it's the latter - just use array `map` function in your `done()` callback and add this callback to each of returned products. – Ilya Luzyanin Feb 14 '15 at 12:40
  • @IlyaLuzyanin It's the latter. I was considering the former but I agree that it is not a good idea. I will try your suggestion, thank you! – miguelarcilla Feb 15 '15 at 08:17

1 Answers1

1

You should consider your Product items as DTO's, and use proper view models in your front end code that can be passed such a DTO. For example, let's assume this server side class (since you haven't shown yours):

public class Product 
{
    public string Name { get; set; }
    public string Description { get; set; }
}

Then you could have a view model like this:

var ProductVm = function (dto) {
    var self = this;
    self.productName = dto.name;
    self.description = dto.description;
    self.myClickFunction = function() {
        // Your function...
    }
}

And pass them along these lines:

function listProducts(viewmodel) {
    var productsQuery = "api/products/get";

    // Send an AJAX request
    $.getJSON(productsQuery).done(function (result) {
        viewmodel.products(result.map(function (dto) { return new ProductVm(dto); }));
    });
}

Additionally, you may want to look at streamlining this to taste using the mapping plugin or by plainly extending self with the dto.

Jeroen
  • 60,696
  • 40
  • 206
  • 339
  • That was exactly what I was trying to accomplish. I had a server-side model and a client-side model that was structurally equivalent. I was not familiar with how the map function would allow me to easily create the client-side object from the server-side data. Thanks! – miguelarcilla Feb 15 '15 at 08:21