Question

I have an MVC 5 project that's using knockout and Web Api 2.

I have paging implemented with my knockout model being originally populated by the first page and the pagination along the bottom. Allowing the user to navigate to other pages within the result set.

The code is as follows:

Html markup that pushes the Photo Id to observable array when checkbox is checked:

<span>Add</span><input type="checkbox" class="photo" name="selected-photos" id="selected-photos" data-bind="checked:$root.selectedPhotos, checkedValue: $data.PhotoId" />

Knockout View Model

function FlickrPhotosViewModel() {

    var self = this;

    self.photos = ko.observableArray([]);
    self.noOfPages = ko.observable();

    self.selectedPhotos = ko.observableArray([]);

    self.pageNumber = ko.observable(1);
    self.selectPage = function(pageNumber) {

        jQuery.support.cors = true;
        $.ajax({
            url: '@Url.RouteUrl("DefaultApi", new {httproute = "", controller = "PhotosFromFlickr"})',
            type: 'GET',
            data: { pageNumber: ko.toJSON(pageNumber), selectedPhotos: ko.toJSON(self.selectedPhotos()) },
            dataType: 'json',
            success: function(data) {
                self.photos(ko.mapping.fromJS(data.Photos));
                self.noOfPages(data.NoOfPages);
                self.selectedPhotos(data.SelectedPhotos);
            },
            error: function(x, y, z) {
                alert(x + '\n' + y + '\n' + z);
            }
        });
    };

    jQuery.support.cors = true;

    $.ajax({
        url: '@Url.RouteUrl("DefaultApi", new {httproute = "", controller = "PhotosFromFlickr"})',
        type: 'GET',
        data: { pageNumber: ko.toJSON(self.pageNumber()), selectedPhotos: ko.toJSON(self.selectedPhotos()) },
        dataType: 'json',
        success: function (data) {
            self.photos(ko.mapping.fromJS(data.Photos));
            self.noOfPages(data.NoOfPages);
        },
        error: function (x, y, z) {
            alert(x + '\n' + y + '\n' + z);
        }
    });
};

$(document).ready(function () {
    ko.applyBindings(new FlickrPhotosViewModel());
});

Web Api Get Method This currently take two arguments, first being the page number I want to serve up and the second being the selected photo ids, currently passed as a string.

public PhotoCollectionModel Get(int pageNumber, string selectedPhotos )
    {}

The problem that I'm having is I want the second parameter to be a strongly typed. Or at the very least List selectedPhotos. Currently I'm having to deserialize the json string (selectedPhotos) in the Web Api Get method...

Is this possible or am I missing the point?

UPDATE

This is the request url it creates -

http://localhost/Mvc/api/PhotosFromFlickr?pageNumber=3&selectedPhotos=%5B%228060459369%22%2C%227895411674%22%2C%227067757145%22%5D

The Headers are:

Request Headers :

Accept:application/json, text/javascript, */*; q=0.01
Referer:http://localhost/Mvc/Profile?username=MarkHBrown
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36
X-Requested-With:XMLHttpRequest
Was it helpful?

Solution 3

I've managed to get it working as per my original requirement.

I wanted to be able to pass a complex object of photos that have been selected when I submit. For this to work, I've had to not use a GET (as that uses querystring) and use a POST instead.

I've kept the pageNumber within the url and passed the observableArray within the body of the request via the data parameter of my jQuery ajax call.

Here is the code:

Web Api Post Method

public PhotoCollectionModel Post([FromUri]int pageNumber, [FromBody]Photo[] selectedPhotos)
    {}

jQuery ajax Post - which is called via the knockout function which is attached to a click event on an html element

self.selectedPhotos is an array of type object (Photo) in this case.

jQuery.support.cors = true;
                $.ajax({
                    url: '@Url.RouteUrl("DefaultApi", new {httproute = "", controller = "PhotosFromFlickr"})/?pageNumber=' + pageNumber,
                    type: 'POST',
                    data: ko.toJSON(self.selectedPhotos),
                    dataType: 'json',
                    contentType: 'application/json; charset=utf-8',
                    success: function (data) {
                        self.photos(ko.mapping.fromJS(data.Photos));
                        self.noOfPages(data.NoOfPages);
                    },
                    error: function (x, y, z) {
                        alert(x + '\n' + y + '\n' + z);
                    }
                });

My checkbox markup

<span>Add</span><input type="checkbox" class="photo" name="selected-photos" id="selected-photos" data-bind="data-bind="checkedValue: $data, checked: $root.selectedPhotos" />" />

The problem i'm now having is that when paging it's not keeping the checked state when going back to previous pages... but this work if I was storing the photo Id in the observable array.

That being said - this is a great solution if you don't have paging...

OTHER TIPS

As mentioned, you have to decorate the method parameters with the [FromUri] attribute. Also, you can not pass an array inside a query string like this: ?selectedPhotos=[1,2,3].

The correct way is ?selectedPhotos=1&selectedPhotos=2&selectedPhotos=3

Web API should work just fine with the code you but you should change the actions parameters too..

public PhotoCollectionModel Get([FromUri]int pageNumber, [FromUri]IEnumerable<Photo> selectedPhotos )
    {}

Where photo is a type that matches the structure of the type in javascript... or if you are simply sending the id's of the photos then something like the following should be fine...

public PhotoCollectionModel Get([FromUri]int pageNumber, [FromUri]IEnumerable<int> selectedPhotos)
    {}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top