Search code examples
javascriptknockout.jsknockout-components

Accessing KO component fields in viewmodel


I have created my first KO component :

components.js

ko.components.register('team-dropdown', {
    viewModel: function (params) {

        var self = this;
        self.teamNames = ko.observableArray([]);

        $.ajax({
                url: 'http://footballcomps.cloudapp.net/Teams',
                type: 'get',
                contentType: 'application/json',
                success: function (data) {
                    $.each(data['value'], function (key, value) {
                        self.teamNames.push(value.TeamName);
                    });
                    console.dir(self.teamNames);
                },
                error: function (err) {
                    console.log(err);
                }
            });

        self.selectedTeam = ko.observable();
    },
    template: { require: 'text!components/team-dropdown.html' }
});

team-dropdown.html

<div id="teams" class="inputBlock form-group">
<select class="form-control" name="teamName" data-bind="options: teamNames, value:selectedTeam"></select>
<label id="lblHomeTeam" data-bind="text: selectedTeam"></label>

And here is my view where I want to use the component :

<div class="row" id="divFixture">
<div class="col-md-4">
    <div class="panel panel-info">
        <div class="panel-heading">
            <h2 class="panel-title">Add new fixture</h2>
        </div>
        <div class="panel-body">

            <form data-bind="submit: fixture.addFixture">
                <div class="form-group">
                    <team-dropdown />
                </div>....
            </form>

And my stripped down view model :

define(['knockout', 'knockout.validation', 'common', 'components'], function (ko) {

    return function fixtureViewModel() {

        function fixture(fixtureId, fixtureDate, homeId, homeName, homeBadge, homeScore, awayId, awayName, awayBadge, awayScore) {
            this.FixtureId = fixtureId;
            this.FixtureDate = fixtureDate;
            this.HomeId = homeId;
            this.HomeName = homeName;
            this.HomeBadge = homeBadge;
            this.HomeScore = homeScore;
            this.AwayId = awayId;
            this.AwayName = awayName;
            this.AwayBadge = awayBadge;
            this.AwayScore = awayScore;
        }

        var self = this;

        self.Id = ko.observable();
        self.FixtureDate = ko.observable();
        self.HomeId = ko.observable();
        self.HomeName = ko.observable();
        self.HomeBadge = ko.observable();
        self.HomeScore = ko.observable();
        self.AwayId = ko.observable();
        self.AwayName = ko.observable();
        self.AwayBadge = ko.observable();
        self.AwayScore = ko.observable();

        self.selectedTeam = ko.observable();

        self.addFixture = function() {

            //how do I reference the selected item from my component here?

    };     
});

How do I reference the item I have selected in my component in self.addFixture?


Solution

  • Since the team-dropdown is meant to be a reusable component, you should provide a way to bind to it. As you have it, it is a standalone control, the outside world cannot interact with it except through the observables you have defined which doesn't make it very flexible.

    I would add parameters to it where you can set what observables to bind to the value. Your fixtures has a selectedTeam property so that seems like a likely candidate.

    ko.components.register('team-dropdown', {
        viewModel: function (params) {
            var self = this,
                teamNames = ko.observableArray([]),
                // default to a local observable if value not provided
                selectedTeam = params.value || ko.observable();
    
            // you probably don't want others to directly modify the teamNames array
            self.teamNames = ko.pureComputed(teamNames);
            self.selectedTeam = selectedTeam;
    
            $.ajax({
                url: 'http://footballcomps.cloudapp.net/Teams',
                type: 'get',
                contentType: 'application/json',
                success: function (data) {
                    $.each(data['value'], function (key, value) {
                        // push to the local `teamNames` array
                        teamNames.push(value.TeamName);
                    });
                    console.dir(teamNames);
                },
                error: function (err) {
                    console.log(err);
                }
            });
        },
        template: { require: 'text!components/team-dropdown.html' }
    });
    

    Then set the parameter when you use the component:

    <form data-bind="submit: fixture.addFixture">
        <div class="form-group">
            <team-dropdown params="value: fixture.selectedTeam" />
        </div>
    </form>
    

    The selected value should now be set in the selectedTeam of your fixture, so you can just use that.

    self.addFixture = function() {
        var selectedTeam = self.selectedTeam(); // should have the value
    };