Search code examples
knockout.jsknockout-components

Knockout Component - Pass value across components


I am new to Knockout JS and learning how component works.

I have created two components to create employee and display list of created employees.

But I am facing couple of issues:

Issue# 1: Click event of button is not firing up

Issue# 2: As soon as employee is created, I want to reflect all created employees in another component.

I have tried as shown in below sample

Example : JSFiddle

Source Code:

/********************* COMPONENT-1 ************************/

ko.components.register('emp-list', {
		viewModel: function(params) {
    	var self = this;
      self.Name = ko.observable();
      self.Name(params.EmpName.firstName + ', ' + params.EmpName.lastName);
			
    },
    template: '<div class="name-container"><span data-bind="html: Name"></span></div>'
});

/********************* COMPONENT-2 ************************/

ko.components.register('emp-create', {
		viewModel: function(params) {
    	var self = this;
      self.firstName = ko.observable(),
      self.lastName = ko.observable(),
      self.EmpList = ko.observableArray(),
        
      self.AddEmp = function()
      {
        alert('1');
        self.Emplist.push(self.firstName() + ' ' + self.lastName());

        for( i=0; i< self.Emplist(); i++)
        {
          alert(self.Emplist()[i]);
        }
      }
			
    },
    template: '<div class="create"><input type="text" placeholder="First name" data-bind="text: firstName" /><input type="text" data-bind="text:lastName" placeholder="Last name" /><input type="button" value="Add" data-bind="click: $root.AddEmp" /></div>'
});

/******************** VIEW MODEL ************************/

var vm = function() {
		var self = this;
    self.Emps = [{'Details': {firstName: 'First Name-A', lastName: 'Last Name-A'}},
                 {'Details': {firstName: 'First Name-B', lastName: 'Last Name-B'}}];
             
}

ko.applyBindings(new vm());
.name-container{
  padding: 5px;
  margin: 2px;
  background-color: #cacaca;
  border: 1px solid #888;
  border-radius: 5px;
}

.name-container span {
  margin: 5px;
}

body {
  font-family: arial,sans-serif;
  font-size: 13px;
}

.action {
  cursor: pointer;
  color: blue;
  text-decoration: underline;
}

.create {
  background-color: #cacaca;
  padding: 5px;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div data-bind='component: {name: "emp-create"}'>
</div>

<div data-bind='foreach: Emps'>
  <div data-bind='component: {
       name: "emp-list",
       params: {EmpName: Details}
  }'></div>
</div>


Solution

  • In your ViewModel self.Emps is an array, it should be an observable array in order to refresh de UI when new Employees are added ( partially solves Issue# 2):

    self.Emps = ko.observableArray(
      [{'Details': {firstName: 'First Name-A', 
                    lastName: 'Last Name-A'}},
       {'Details': {firstName: 'First Name-B', 
                    lastName: 'Last Name-B'}}]
    ); 
    

    Now, you need to pass Emps to the emp-create component in order to add new elements (also solves Issue# 2):

    <div data-bind='component: {name: "emp-create",
           params: {EmpList: Emps}}'>
    </div>
    

    In the ViewModel of emp-create, change:

    self.EmpList = ko.observableArray(),
    

    to

    self.EmpList = params.EmpList,
    

    The self.AddEmp function has a typo. It has self.Emplist.push and it should be self.EmpList.push (EmpList).

    The click binding in the emp-create component is wrong, it has click: $root.AddEmp and should be click: AddEmp (without $root). It solves Issue# 1

    The self.AddEmp function should add an Employee:

      self.AddEmp = function()
      {
        self.EmpList.push(
          {'Details': {firstName: self.firstName(), 
                       lastName: self.lastName()}}
        )
      }
    

    Here is an updated fiddle

    Update:

    Your emp-create template has a wrong binding in the <input type="text">. You have data-bind="text: firstName" and it should be data-bind="value: firstName", that is, you need to change text to value in order the observable gets updated from the <input>. Here Value Binding more information.

    Here is an updated fiddle.