0

I am currently following a tutorial online for creating a Telephone directory using Web API. Everything was working fine until I began to use javascript with knockout.js to bind my view model. Now no data is being loaded onto the page.

When I attempt to access a particular Telephone Directory id though a directory like http://localhost:{port_no}/api/Data/1 my browser attempts to download the JSON data I am storing my data in.

Can someone please explain why this might be happening and why the JSON data is not being parsed correctly?

Controller class:

namespace TelephoneDirectory.Controllers
{
    public class DataController : ApiController
    {
        public async Task<IEnumerable<TelephoneEntry>> Get()
        {
            using (var context = new DataContext())
            {
                return await context.TelephoneEntries.ToListAsync();
            }
        }

        public async Task<TelephoneEntry> Get(int id)
        {
            using (var context = new DataContext())
            {
                return await context.TelephoneEntries.FirstOrDefaultAsync(t => t.Id == id);
            }
        }


        public async Task<int> Post([FromBody] TelephoneEntry telephoneEntry)
        {
            using (var context = new DataContext())
            {
                if (telephoneEntry.Id == 0)
                {
                    context.Entry(telephoneEntry).State = EntityState.Added;
                }
                else
                {
                    context.Entry(telephoneEntry).State = EntityState.Modified;
                }

                await context.SaveChangesAsync();

                return telephoneEntry.Id;
            }
        }
    }
}

index.js (the code containing ko bindings)

function TelephoneEntry(data) {
    var self = this;

    self.id = data.id;
    self.firstName = data.firstName;
    self.lastName = data.lastName;
    self.number = data.number;
}

function TelephoneViewModel() {
    var self = this;

    self.id = ko.observable(0);
    self.firstName = ko.observable('');
    self.lastName = ko.observable('');
    self.number = ko.observable('');

    self.addText = ko.observable('Add');
    self.resetText = ko.observable('Reset');
    self.selectedIndex = -1;

    self.add = function () {

        var entry = new TelephoneEntry({
            id: self.id(),
            firstName: self.firstName(),
            lastName: self.lastName(),
            number: self.number()
        });

        if (self.addText() == 'Add') {
            self.telephoneEntries.push(entry);
        }
        else {
            var oldTelephoneEntry = self.telephoneEntries()[self.selectedIndex];
            self.telephoneEntries.replace(oldTelephoneEntry, entry);
        }

        self.post(entry);
        self.reset();
    };

    self.reset = function () {
        self.id(0);
        self.firstName('');
        self.lastName('');
        self.number('');

        self.addText('Add');
        self.resetText('Reset');
        self.selectedIndex = -1;
    };

    self.load = function () {
        $.getJSON('http://localhost:16257/api/Data/', function (data) {
            $.each(data, function (index, item) {
                self.telephoneEntries.push(new TelephoneEntry({
                    id: item.id,
                    firstName: item.firstName,
                    lastName: item.lastName,
                    number: item.number
                }));
            });
        });
    };

    self.post = function (telephoneEntry) {
        $.post('http://localhost:16257/api/Data/', telephoneEntry, function (id) {
            telephoneEntry.id = id;
        });
    };

    self.telephoneEntries = ko.observableArray([]);
    self.load();
}

ko.applyBindings(new TelephoneViewModel());

WebApiConfig class:

namespace TelephoneDirectory.App_Start
{
    public class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });

            JsonMediaTypeFormatter jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();

            jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        }
    }
}

Global.asax

public class Global : HttpApplication
    {

        protected void Application_Start()
        {
            GlobalConfiguration.Configure(WebApiConfig.Register);
            Database.SetInitializer(new Initializer());
        }
    }

index.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
    <link href="Content/site.css" rel="stylesheet" />
</head>
<body>
    <script src="Scripts/jquery-1.9.0.min.js"></script>
    <script src="Scripts/knockout-3.1.0.js"></script>
    <script src="Scripts/index.js"></script>

    <div class="container-narrow">
        <div class="row">
            <h1>Telephone Directory</h1>
        </div>
        <div class="row shaded padded">
            <div class="col-sm-3">
                <label for="firstName">First Name</label>
                <input id="firstName" name="firstName" type="text" class="form-control" data-bind="value: firstName" required="required" />
            </div>
            <div class="col-sm-3">
                <label for="lastName">Last Name</label>
                <input id="lastName" name="lastName" type="text" class="form-control" data-bind="value: lastName" required="required" />
            </div>
            <div class="col-sm-3">
                <label for="phoneNumber">Phone Number</label>
                <input id="phoneNumber" name="phoneNumber" type="text" class="form-control" data-bind="value: number" required="required" />
            </div>
            <div class="col-sm-12">
                <button id="add" name="add" type="submit" data-bind="click: add, text: addText">Add</button>
                <button id="reset" name="reset" type="reset" data-bind="click: reset, text: resetText">Reset</button>
            </div>
        </div>
    </div>
    <div class="container-narrow">
        <div class="row">
            <table class="table table-striped">
                <thead>
                    <tr>
                        <th>First Name</th>
                        <th>Last Name</th>
                        <th>Phone Number</th>
                        <th>&nbsp;</th>
                    </tr>
                </thead>
                <tbody data-bind="foreach: telephoneEntries">
                    <tr>
                        <td><span data-bind="text: firstName"></span></td>
                        <td><span data-bind="text: lastName"></span></td>
                        <td><span data-bind="text: number"></span></td>
                        <td>
                            <a href="#" data-bind="click: $parent.edit">Edit</a>&nbsp;<a href="#" data-bind="click: $parent.delete">Delete</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>

</body>
</html>{"id":1,"firstName":"Jon","lastName":"Preece","number":"4444"}
11
  • no error, just that no data loads on the page and clicking the Add, Edit and Delete buttons seems to do nothing Commented Apr 29, 2014 at 4:11
  • apply databindings in document ready. Commented Apr 29, 2014 at 4:13
  • Open network tab and console tab in chrome or firebug and check what is send to browser Commented Apr 29, 2014 at 4:14
  • 1
    Hi @MatthewPigram, you could look at the content of the JSON data returned back when you request the service using the browser. If it is what you expect, then the issue is in your binding. Commented Apr 29, 2014 at 5:14
  • 1
    On a different note, your question has a lot of code to go through, most of it not relevant to the issue. Could you trim it down to only the relevant parts, make it into a minimal repro? Makes it a lot easier (or at least: inviting) for us to help you. Commented Apr 29, 2014 at 6:17

1 Answer 1

2

To set the bindings on document ready, do the following.

Change this

ko.applyBindings(new TelephoneViewModel());

to this

$(function(){ 
    ko.applyBindings(new TelephoneViewModel());
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.