Reputation: 31
I am trying to create a table of employees using knockout.js. I have an array that is looped over for a table, and I have functions to edit and delete, but I want to create a text input box for adding Employees to the array, and in turn updating the table with the new employee's information. I have tried everything I know how to do, but since I am a beginner in knockout and javascript for that matter, I don't really know how to accomplish this.
HTML:
<div class="form-group row">
<label for="txtEmployee" class="col-2 col-form-label">Name: </label>
<div class="col-6">
<input type="text"
data-bind="value: nameToAdd, valueUpdate: 'afterkeydown'"
class="form-control"
id="txtName" />
</div>
<div class="col-4">
<a href="#" data-bind="click: $root.add" class="btn btn-primary">Add</a>
</div>
</div>
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>EmployeeId</th>
<th>Name</th>
<th>Functions</th>
</tr>
</thead>
<tbody data-bind="foreach: Employees">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Name"></td>
<td>
<span>
<a href="#" class="btn btn-success" data-bind="click: $root.edit">
<i class="fa fa-pencil fa-lg"> </i> EDIT
</a>
</span>
|
<span>
<a href="#" class="btn btn-danger" data-bind="click: $root.remove">
<i class="fa fa-trash-o fa-lg"> </i> DELETE
</a>
</span>
</td>
</tr>
</tbody>
</table>
Javascript:
function Employee(id, name) {
this.Id = id;
this.Name = name;
};
var employeeList = [
new Employee(1, "Justin"),
new Employee(2, "John"),
new Employee(3, "Sarah"),
new Employee(4, "Tyler"),
new Employee(5, "Mason")
];
function PayrollViewModel() {
var self = this;
self.nameToAdd = ko.observable("");
self.Id = ko.observable("");
self.Name = ko.observable("");
var Employee = {
Id: self.Id,
Name: self.Name
};
self.Employee = ko.observable();
self.Employees = ko.observableArray(employeeList);
self.edit = function(Employee) {
self.Employee(Employee);
};
self.remove = function(Employee) {
self.Employees.remove(Employee);
};
self.cancel = function() {
self.Employee(null);
};
self.update = function() {
var l_employee = self.Employee();
self.Employees.remove(self.Employee());
self.Employees.push(l_employee);
};
self.add = function() {
var random = Math.floor((Math.random() * 100) + 1);
this.Employees = [
new Employee(random, nameToAdd)
]
this.nameToAdd("");
};
};
ko.applyBindings(new PayrollViewModel());
Upvotes: 1
Views: 296
Reputation: 423
HTML changes
I have changed your HTML slightly and have wrapped a form element around the input and submit elements. As you can see, the form element has the data-bind attached to the add() function. Hopefully this makes it a bit clearer where on submission of each input field which function will be fired.
JavaScript
I have created two view models, which is one of the underlying concepts of Knockout.js.
One view model is a config function that contains the users being created and another one which deals with adding the employees in the UI.
Ideally, the addEmployeeVM function should be a generic function that handles the removing, editing and adding but for learning purposes I have created to just add a user. You can change this to include the other methods.
I have not used the reference 'self' as many others do just because I think using a different term in each view model helps you identify what view model belongs to which one.
What I have done is extended the properties within employeeConfigVM so they can be accessed inside of addEmployeeVM. The idea is to try and continue building your application up like this, creating view models for different features/parts of your application, in different files so it is easy for you to scale up your app.
Look into activating knockout to understand how to bind to different parts of your page, as you may only want to provide the functionality to a very small part and not to the rest of the page.
var employeeConfigVM = function() {
var empConfigVM = this;
empConfigVM.employeeList = [
new Employee(1, "Justin"),
new Employee(2, "John"),
new Employee(3, "Sarah"),
new Employee(4, "Tyler"),
new Employee(5, "Mason")
];
function Employee(id, name) {
this.Id = id;
this.Name = name;
};
}
var addEmployeeVM = function(id, userName) {
var empVM = this;
empVM.employeeConfig = new employeeConfigVM();
empVM.Id = ko.observable("");
empVM.Name = ko.observable("");
empVM.Employee = ko.observable();
empVM.Employees = ko.observableArray(empVM.employeeConfig.employeeList);
empVM.add = function() {
var random = Math.floor((Math.random() * 100) + 1);
empVM.Employees.push({
Id: random,
Name: empVM.Name()
});
};
};
var addUserVM = new addEmployeeVM();
$(function() {
ko.applyBindings(addUserVM);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form class="form-group row" data-bind="submit: add">
<label for="txtEmployee" class="col-2 col-form-label">Name: </label>
<div class="col-6">
<input type="text" data-bind="value: $root.Name" class="form-control" id="txtName" />
</div>
<div class="col-4">
<button class="btn btn-primary " type="submit">Add</button>
</div>
</form>
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>EmployeeId</th>
<th>Name</th>
<th>Functions</th>
</tr>
</thead>
<tbody data-bind="foreach: $root.Employees">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Name"></td>
<td>
<span>
<a href="#" class="btn btn-success" data-bind="click: $root.edit">
<i class="fa fa-pencil fa-lg"> </i> EDIT
</a>
</span> |
<span>
<a href="#" class="btn btn-danger" data-bind="click: $root.remove">
<i class="fa fa-trash-o fa-lg"> </i> DELETE
</a>
</span>
</td>
</tr>
</tbody>
</table>
Upvotes: 0
Reputation: 1850
There is more than one problem in your code.
Number one is that new Employee()
called from PayrollViewModel.add()
will refer to Employee
object which is defined inside the model (and not being a constructor) instead of function Employee(id, name) {}
which you expect it to refer.
The second problem is that you are redefining PayrollViewModel.Employees
on every call of PayrollViewModel.add()
. This breaks knockout binding to the DOM - never redefine (rewrite) observables after bind, only assign em via observable(newValue)
or using .push()
method for observable arrays. So the right way to add new record in your case is self.Employees.push(newItem)
.
See working example below:
function Employee(id, name) {
this.Id = id;
this.Name = name;
};
var employeeList = [
new Employee(1, "Justin"),
new Employee(2, "John"),
new Employee(3, "Sarah"),
new Employee(4, "Tyler"),
new Employee(5, "Mason")
];
function PayrollViewModel() {
var self = this;
self.nameToAdd = ko.observable("");
self.Id = ko.observable("");
self.Name = ko.observable("");
self.Employee = ko.observable();
self.Employees = ko.observableArray(employeeList);
self.edit = function (Employee) {
self.Employee(Employee);
};
self.remove = function (Employee) {
self.Employees.remove(Employee);
};
self.cancel = function () {
self.Employee(null);
};
self.update = function () {
var l_employee = self.Employee();
self.Employees.remove(self.Employee());
self.Employees.push(l_employee);
};
self.add = function () {
this.Employees.push({
Id: self.Employees().length+1,
Name: self.nameToAdd()
});
this.nameToAdd("");
};
}
ko.applyBindings(new PayrollViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="form-group row">
<label for="txtEmployee" class="col-2 col-form-label">Name: </label>
<div class="col-6">
<input type="text"
data-bind="value:$root.nameToAdd,valueUpdate: 'afterkeydown'"
class="form-control"
id="txtName" />
</div>
<div class="col-4">
<a href="#" data-bind="click: $root.add" class="btn btn-primary">Add</a>
</div>
</div>
<table class="table table-dark table-striped table-hover">
<thead>
<tr>
<th>EmployeeId</th>
<th>Name</th>
<th>Functions</th>
</tr>
</thead>
<tbody data-bind="foreach: Employees">
<tr>
<td data-bind="text: Id"></td>
<td data-bind="text: Name"></td>
<td>
<span>
<a href="#" class="btn btn-success" data-bind="click: $root.edit">
<i class="fa fa-pencil fa-lg"> </i> EDIT
</a>
</span>
|
<span>
<a href="#" class="btn btn-danger" data-bind="click: $root.remove">
<i class="fa fa-trash-o fa-lg"> </i> DELETE
</a>
</span>
</td>
</tr>
</tbody>
</table>
Besides the above you problem with edit
and update
methods which are not bind to anything.
Upvotes: 3