UI not updating while using Knockout Observable Array - javascript

I am new to knockout. I am trying to use observable arrays to track the changes from UI. The UI is loading with the initial data which is stored in the array. And i am trying to add new object into the array dynamically from another screen.
Now i am able to add new object into the array. But UI is not getting reflected with new changes in the array. Below is my html and javascript code.
Am i missing something.
<html>
<head>
<link rel="stylesheet" href="bootstrap.css" type="text/css" />
<link rel="stylesheet" href="bootstrap-theme.css " type="text/css" />
<script src="jquery.js" type="text/javascript"></script>
<link rel="stylesheet" href="prodconfig.css " type="text/css" />
<script src="jquery.mobile.min.js" type="text/javascript"></script>
<link rel="stylesheet" href="cordys.min.css" type="text/css" />
<link rel="stylesheet" href="jquery.mobile.structure.min.css" type="text/css" />
<script src="knockout.js" type="text/javascript"></script>
<script src="prodconfig.js" type="text/javascript"></script>
</head>
<body>
<div data-role="page" id="productsPage" class="dataContainer">
<div id="productDetails">
<div data-role="content" id="productTable">
<table data-role="table" class="ui-responsive table">
<thead>
<tr>
<th data-priority="6">Product Name</th>
<th data-priority="1">Description</th>
<th data-priority="2">Parent?</th>
</tr>
</thead>
<tbody id="pBody" data-bind="foreach: products">
<tr class="success">
<td><span data-bind="text: name"></span></td>
<td><span data-bind="text: desc"></span></td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="prodButtons">
<button id="addProdProduct">Add Product</button>
<button id="addProdChar">Add Characteristics</button>
<button id="prodButton">Next</button>
</div>
</div>
<div id="addProductPage" data-role="page" >
<span><h3>Product Name</h3></span><input type="text" id="prodNameId"></input>
<span><h3>Product Desc</h3></span><input type="text" id="prodDescId"></input>
<span><h3>Is Parent</h3></span><input type="text" id="prodIsParentId"></input>
<button id="addProdButton">OK</button>
<div>
</body>
var configArray = new Array();
var products = [];
var services = new Array();
var chars = [];
var prd;
for(var i=0;i<2;i++){
var product = new Object();
product["name"] = "prod"+i+"Name";
product["desc"] = "prod"+i+"Desc";
product["isParent"] = "prme";
for(var j=0;j<2;j++){
var charr = new Object();
charr["name"] = "prod"+i+"char"+j;
charr["val"] = "prod"+i+"char"+j+"val";
chars[j] = charr;
}
product["chars"] = chars;
products[i] = product;
}
var ProductViewModel = function(items) {
this.items = ko.observableArray(items);
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd());
this.itemToAdd("");
}
}.bind(this);
};
$(function(){
$('#addProdProduct').click(function() {
window.location.href = "#addProductPage";
});
$('#addProdButton').click(function() {
addProduct();
});
prd = new ProductViewModel(products);
ko.applyBindings(prd);
});
function addProduct(){
var product = new Object();
product["name"] = $('#prodNameId').val();
product["desc"] = $('#prodDescId').val();
product["isParent"] = $('#prodIsParentId').val();
prd.itemToAdd(product);
prd.addItem();
window.location.href = '#';
}

You are binding to the products variable instead of to the items field on your viewmodel.
Change your binding to:
<tbody id="pBody" data-bind="foreach: items">

The code below shown a sample procedure for inserting data using knockoutjs MVVM pattern. Here i using bootstrap Modal Popup for inserting Data.
HTML
<div class="row">
<button type="button" class="btn btn-primary pull-right" data-bind="click:add" data-toggle="modal" data-target="#myModal1">
<i class="fa fa-plus"></i> Add New Data
</button>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Action</th>
</tr>
</thead>
<tbody data-bind="template:{name:'item-view-template',foreach:products}"></tbody>
</table>
</div>
<script type="text/html" id="item-view-template">
<tr>
<td class="text-left" data-bind="text:ProductName"></td>
<br />
/tr>
</script>
<div class="modal fade" id="myModal1" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Add Data</h4>
</div>
<div class="modal-body" data-bind="template:{name:'item-create-template',data:selectedData}">
<div class="row">
<div class="col-md-12"></div>
</div>
</div>
</div>
</div>
</div>
<script type="text/html" id="item-create-template">
<form>
<fieldset>
<div class="form-group col-md-6">
<label>Name</label>
<input type="text" class="form-control" data-bind="value:ProductName" />
</div>
<button class="btn btn-primary" data-bind="click:$parent.save">Save</button>
<button class="btn btn-default" data-dismiss="modal">Cancel</button>
</fieldset>
</form>
</script>
<script type="text/javascript">
var vm = new ProductViewModel();
ko.applyBindings(vm);
vm.init();
</script>
View Model Js File
function ProductViewModel() {
var self = {};
self.data = ko.observableArray();
self.products = ko.observableArray();
self.selectedData = ko.observable(Product({}));
self.init = function () {
$.get('/MyController/GetAll', function (data) {
$.each(data, function (key, value) {
self.products.push(Product(value));
});
});
};
self.add = function () {
self.selectedData(Product({}));
};
self.save = function() {
if (self.selectedData()) {
var jsonData = ko.toJS(self.selectedData);
$.post('/MyController/Create', jsonData, function(data) {
if (data.Status == true) {
$('#myModal1').modal('hide');
bootbox.alert("Product created successfully", function() {
self.Products.removeAll();
$.get('/MyController/GetAll', function (result) {
$.each(result.Products, function (key, value) {
self.Products.push(Product(value));
});
});
});
} else {
bootbox.alert("Duplicate values not allowed..!!");
}
});
} else {
bootbox.alert("Error!!");
}
};
return self;
}
And the Model
function Product(product) {
var self = {};
self.Id = ko.observable(product.Id || '');
self.ProductName = ko.protectedObservable(product.Name || '');
return self;
}
I think this will help you

Related

AngularJS / Replace Sorting within ng-click and ng-show with custom method

I have a set of table columns. Each column can be sorted straight and in reverse. Up until now, we were doing that sorting inside each ng-click and ng-show, like so:
HTML
<a style="color:black" href="" ng-click="sortReverse = !sortReverse; changeSortType('id')">
Group id
<span ng-show="sortType == 'id' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'id' && sortReverse" class="fa fa-caret-up"></span>
</a></th>
CONTROLLER
$scope.sortType = 'count';
$scope.sortReverse = true;
$scope.changeSortType = function (sortType){
$scope.sortType = sortType;
};
$scope.itemSortValue = function (item) {
return Service.sort(item, $scope.sortType)
};
Now we want to replace this with a method, that does the same so we can diminish the amount of code in the HTML.
I don't know how to do this exactly in AngularJS. Whatever I do doesn't work. I have also seen a couple of answers but they don't work, or I am doing something wrong. COuld you please help me with the code.
For <a> element remove sortReverse = !sortReverse; from ng-click and add $scope.sortReverse= !$scope.sortReverse it in the first line of method changeSortType;
and for ng-show create and method
$scope.myMethod = function (id, reverse){
return id && reverse;
};
and change html to
<span ng-show="myMethod('id',!sortReverse)" class="fa fa-caret-down"></span>
<span ng-show="myMethod('id',sortReverse)" class="fa fa-caret-up"></span>
Try with this code .
Html
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<body>
<div ng-app="myApp" ng-controller="customersCtrl">
<div class="jumbotron bg-dark" style="border-radius: 0px; margin-bottom: 0px;">
<h1 class="text-center" style="color:azure;">Table Sort</h1>
</div>
<table class="table table-dark table-striped">
<thead>
<tr>
<th ng-click="orderByMe('Name')" style="cursor: pointer;">Name <i class="fa fa-sort-desc" style="font-size:15px;color: whitesmoke" ng-if=" toggleCursor == 'desc'"></i> <i class="fa fa-sort-asc" ng-if=" toggleCursor == 'asc'"></i></th>
<th style="cursor: pointer;">City </th>
<th style="cursor: pointer;"> Country</th>
</tr>
</thead>
<tbody>
<tr ng-repeat=" data in myData | orderBy:myOrderBy">
<th>{{data.Name}}</th>
<th>{{data.City}}</th>
<th>{{data.Country}}</th>
</tr>
</tbody>
</table>
</div>
</body>
</html>
JS File
var app = angular.module('myApp', []);
app.controller('customersCtrl', function($scope, $http) {
$http.get("https://www.w3schools.com/angular/customers.php").then(function(response) {
$scope.myData = response.data.records;
});
$scope.toggleCursor = 'desc';
$scope.myOrderBy = 'Name';
$scope.orderByMe = function(sort) {
$scope.myOrderBy = sort;
$scope.sortArry = $scope.myData.sort();
if($scope.toggleCursor == 'desc')
{
$scope.toggleCursor = 'asc';
$scope.myOrderBy = $scope.sortArry;
}
else
{
$scope.toggleCursor = 'desc';
$scope.myOrderBy = $scope.sortArry.reverse();
}
}
});

Using AngularJS Trying to insert a <tr> in the middle

Here, I'm trying to insert a tr tag under a selected tr tag.I managed to insert the tr in the table but it was in the bottom.
This is a sample code:
"use strict";
(function(angular) {
var app = angular.module('app', []);
app.controller('MainController', MainController);
MainController.$inject = ['$compile', '$scope', '$templateCache'];
function MainController($compile, $scope, $templateCache) {
var publishShown = false;
var lastItemAppned;
var vm = this;
vm.targets = ['Single passenger', 'Single driver', 'All passengers', 'All drivers'];
vm.items = ["Cheese", "Pepperoni", "Black Olives"];
vm.cancel = cancel;
vm.fruits = [{
name: 'Orange',
color: 'Orange',
tree: 'Orange Tree!',
origin: 'China'
}, {
name: 'Apple',
color: 'Green',
tree: 'Apple Tree!',
origin: 'Moon'
}];
vm.message = 'Fruit store!';
function appendTemplate(event) {
if (publishShown) {
removeLastAppend();
}
publishShown = true;
var template = $templateCache.get('template.html');
var compiledHtml = $compile(template)($scope);
var parent = event.currentTarget.parentElement.parentElement.parentElement;
parent.append(compiledHtml[0]);
}
vm.showOptions = function(event) {
appendTemplate(event);
}
function removeLastAppend() {
angular.element('#publish-template').remove();
}
function cancel() {
removeLastAppend();
}
}
})(window.angular);
<!DOCTYPE html>
<html ng-app="app">
<head>
<link data-require="bootstrap-css#3.*" data-semver="3.3.7" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.css" />
<link data-require="bootstrap#*" data-semver="4.0.5" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" />
<script data-require="jquery#*" data-semver="3.1.1" src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script data-require="Tether#*" data-semver="1.4.0" src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<script data-require="bootstrap#*" data-semver="4.0.5" src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>
<script data-require="angularjs#1.6.*" data-semver="1.6.4" src="https://code.angularjs.org/1.6.4/angular.min.js"></script>
<script data-require="angular-animate#1.6.*" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular-animate.js"></script>
<script data-require="angular-touch#1.6.*" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular-touch.js"></script>
<script data-require="ui-bootstrap#*" data-semver="2.5.0" src="https://cdn.rawgit.com/angular-ui/bootstrap/gh-pages/ui-bootstrap-tpls-2.5.0.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="MainController as vm">
<script type="text/ng-template" id="template.html">
<tr id="publish-template">
<td colspan="5">
<div class="row">
<div class="col-sm-3">
<button class="btn btn-sm btn-success" ng-click="vm.command()">Command</button>
</div>
<div class="col-sm-3">
<button class="btn btn-sm btn-primary" ng-click="vm.sell()">Sell</button>
</div>
<div class="col-sm-3">
<button class="btn btn-sm btn-warning" ng-click="vm.eatSome()">Eat some!</button>
</div>
<div class="col-sm-3">
<button class="btn btn-sm btn-danger" ng-click="vm.cancel()">Cancel</button>
</div>
</div>
</td>
</tr>
</script>
<div class="container-fluid">
<h1>{{vm.message}}</h1>
<table class="table">
<thead>
<th>Name</th>
<th>Color</th>
<th>Tree</th>
<th>Origin</th>
<th>Action</th>
</thead>
<tbody>
<tr ng-repeat="fruit in vm.fruits">
<td>{{fruit.name}}</td>
<td>{{fruit.color}}</td>
<td>{{fruit.tree}}</td>
<td>{{fruit.origin}}</td>
<td>
<button class="btn btn-primary" ng-click="vm.showOptions($event)">Options</button>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
As you see, if you click the second options button, you'll not notice the problem, but if you click the first one, it adds a tr tag always in the bottom of the table!
My aim is to add the new tr under the selected on.
Thanks in advance
The ng-repeat is running on the tr element and you are looking to break the loop and insert another tr in the middle based on the row that was clicked on. To achieve this you need to break the table into 3 parts
TR ng-repeat on topFruits
TR row with option buttons (will be hidden initially)
TR ng-repeat on bottomFruits
Initially, you will need to assign all the fruits to topFruits and keep the bottomFruits null. Also keep the tr row with the option buttons hidden.
When the show Options button is clicked on a row, get the index of the row and then divide the fruits between topFruits and bottomFruits based on the index. Also make the row with buttons visible.
Here is a working demo
Here is the HTML body
<body ng-controller="MainCtrl as vm">
<div class="container-fluid">
<h1>{{vm.message}}</h1>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Color</th>
<th>Tree</th>
<th>Origin</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="fruit in vm.topFruits">
<td style="background:#bbb">{{fruit.name}}</td>
<td>{{fruit.color}}</td>
<td>{{fruit.tree}}</td>
<td>{{fruit.origin}}</td>
<td>
<button class="btn btn-primary" ng-click="vm.showOptionsTop($index)">Options</button>
</td>
</tr>
<tr id="publish-template" style="display:none">
<td colspan="5">
<div class="row">
<div class="col-sm-3">
<button class="btn btn-sm btn-success" ng-click="vm.command()">Command</button>
</div>
<div class="col-sm-3">
<button class="btn btn-sm btn-primary" ng-click="vm.sell()">Sell</button>
</div>
<div class="col-sm-3">
<button class="btn btn-sm btn-warning" ng-click="vm.eatSome()">Eat some!</button>
</div>
<div class="col-sm-3">
<button class="btn btn-sm btn-danger" ng-click="vm.cancel()">Cancel</button>
</div>
</div>
</td>
</tr>
<tr ng-repeat="fruit in vm.bottomFruits">
<td style="background:#fcc">{{fruit.name}}</td>
<td>{{fruit.color}}</td>
<td>{{fruit.tree}}</td>
<td>{{fruit.origin}}</td>
<td>
<button class="btn btn-primary" ng-click="vm.showOptionsBottom($index)">Options</button>
</td>
</tr>
</tbody>
</table>
</div>
</body>
Here is the controller code
vm.fruits = [{
name: 'Orange',
color: 'Orange',
tree: 'Orange Tree!',
origin: 'China'
}, {
name: 'Apple',
color: 'Green',
tree: 'Apple Tree!',
origin: 'Moon'
}];
vm.topFruits = vm.fruits;
vm.bottomFruits = [];
vm.message = 'Fruit store!';
function appendTemplate(index) {
if (vm.publishShown) {
removeLastAppend();
}
vm.publishShown = true;
vm.topFruits = [];
vm.bottomFruits = [];
document.getElementById('publish-template').style.display="block";
for(i=0;i<vm.fruits.length;i++) {
if(i<=index) {
vm.topFruits[i] = vm.fruits[i];
} else {
vm.bottomFruits[i-vm.topFruits.length] = vm.fruits[i];
}
}
}
vm.showOptionsTop = function(index) {
appendTemplate(index);
}
vm.showOptionsBottom = function(index) {
appendTemplate(index+vm.topFruits.length);
}
function removeLastAppend() {
document.getElementById('publish-template').style.display="none";
vm.publishShown = false;
}
function cancel() {
removeLastAppend();
}

forming an array from dynamic input fileds using angularjs

I am working on a employee attendance system where i need to know their attendance status.I am generating a dynamic form which contains a text input field and a checkbox for each employee using angularjs ng-repeat inside a table to know whether the the employee was present or absent along with comments.I want to save the values of these dynamic text filed and checkbox using a single save button.Text fields may have null values or any other values and checkbox may be all checked,all unchecked and few checked and few unchecked. If the check box is checked then i want to save "checked":"yes" otherwise as no.I have also a single date input field to save the record for this particular date.
I think the solution of my situation is forming a dynamic array from inputs and assign it to a variable but and don't know how to form array dynamically in angularjs and then pass the array to a php page.Can you help me on this issue?
My expected array format is :
[{"Did":"10","supervisor":"ms1001","date":"2017-06-01",
"info":
{"eid":"10","checked":"yes","cmnt":"on time"},
{"eid":"20","checked":"NO", "cmnt":"absent"},
{"eid":"30","checked":"yes","cmnt":""},
{"eid":"40","checked":"NO","cmnt":"OK"},
{"eid":"50","checked":"YES","cmnt":""},
{"eid":"60","checked":"YES","cmnt":""},
{"eid":"70","checked":"YES","cmnt":""},
{"eid":"80","checked":"NO","cmnt":"Late"},
{"eid":"90","checked":"YES","cmnt":""}
}];
I will store the input details in attendance table which schema is
attendance(did,eid,date,checked,comment,supervisor_id)
var myApp = angular.module('myApp',['ui.bootstrap']);
myApp.controller('MyCtrl', function($scope) {
$scope.list = [
{"dept_id":"d10","dname":"sales","supervisor":"ms1001"},
{"eid":"10","ename":"nam1"},
{"eid":"20","ename":"nam2"},
{"eid":"30","ename":"nam3"},
{"eid":"40","ename":"nam4"},
{"eid":"50","ename":"nam5"},
{"eid":"60","ename":"nam6"},
{"eid":"70","ename":"nam7"},
{"eid":"80","ename":"nam8"},
{"eid":"90","ename":"nam9"},
{"eid":"120","ename":"nam10"}
];
$scope.did= $scope.list[0].dept_id;
$scope.dname= $scope.list[0].dname;
$scope.sp_name= $scope.list[0].supervisor;
$scope.selectedText = 'Select All';
$scope.isAll = false;
$scope.selectAll = function() {
if($scope.isAll === false) {
angular.forEach($scope.list, function(data){
data.checked = true;
});
$scope.isAll = true;
$scope.selectedText = 'Deselect All';
} else {
angular.forEach($scope.list, function(data){
data.checked = false;
});
$scope.isAll = false;
$scope.selectedText = 'Select All';
}
};
$scope.selectedFriends = function () {
return $filter('filter')($scope.list, {checked: true });
};
//date picker
$scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened = true;
};
$scope.dateOptions = {
formatYear: 'yy',
startingDay: 1
};
$scope.format = 'dd-MMMM-yyyy';
//end of date picker
});
<html>
<head>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.datatables.net/1.10.15/css/dataTables.bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.angularjs.org/1.6.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-animate.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-sanitize.js"></script>
<script src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.5.0.js"></script>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<style>
.full button span {
background-color: limegreen;
border-radius: 32px;
color: black;
}
.partially button span {
background-color: orange;
border-radius: 32px;
color: black;
}
</style>
</head>
<div class="container">
<div ng-app="myApp" ng-controller="MyCtrl">
<div class="row">
<div class="col-sm-3" style="background-color:yellow;">
<p>Department ID::{{did}}</p>
</div>
<div class="col-sm-3" style="background-color:skyblue;">
<p>Dept Name:{{dname}}</p>
</div>
<div class="col-sm-3" style="background-color:pink;">
<p>Supervisor name name:{{sp_name}}</p>
</div>
<div class="col-sm-3">
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="{{format}}"
ng-model="list.dt" is-open="opened" min-date="minDate" max-date="'2018-06-22'"
ng-model-options="{timezone: 'UTC'}"
datepicker-options="dateOptions" date-disabled="disabled(date, mode)"
ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event)">
<i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Employee ID</th>
<th>name</th>
<th><label>Attendence</label><br><span id="selectall" ng-click="selectAll()"><input
type="checkbox">{{selectedText}}</span></th>
<th>comment</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="data in list" ng-if="$index">
<td> {{ data.eid }} </td>
<td> {{ data.ename }} </td>
<td> <input type="checkbox" value="{{ data.eid}}" ng-checked="data.checked" ng-model="data.checked"></td>
<td>
<input type="text" ng-model="data.cmnt" ></td>
</tr>
</tbody>
</table>
<pre>{{list}}</pre>
</div>
<button type="button" ng-click="saveAll()">Save all</button>
</div>
</html>
HTML
<table border="1">
<tr>
<td>Employee ID</td>
<td>Name</td>
<td>Attendance</td>
</tr>
<tr ng-repeat="employee in employees">
<td>{{employee.eid}}</td>
<td>{{employee.ename}}</td>
<td><input type="checkbox" name="check" ng-model="employee.att">
</td>
</tr>
</table>
<button ng-click="saveForm()">Save all</button>
<pre>{{employees}}</pre>
JS
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope, $http) {
//initial data to display on table.
$scope.employees = [
{eid:"10",ename:"nam1", att: false},
{eid:"20",ename:"nam2", att: false},
{eid:"30",ename:"nam3", att: false},
];
//on save
$scope.saveForm = function (){
$http({
method: 'POST',
url: '/ana/testone',
data: $.param({formData: angular.copy($scope.employees)}),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function(response) {
console.log(response);
});
}
});
PHP
$data = $_POST['formData'];
echo json_encode($data);

Computed property based of external array Knockout JS

I just starting out with Knockout and running through a pluralsite course.
I have a (presumably basic) question about a computed property based on the parent array:
Given the below code how do I make it work so that the Amount property for each item in my.vm.attendees is always the totalCost divided by number of attendees. E.g. with 4 attendees in the collection amount for each should be 25.
This should be automatically updated as you add and remove items.
<script type="text/javascript">
$(function () {
var total = 100;
//Attendee construction
my.Attendee = function () {
this.Name = ko.observable();
this.Amount = ko.computed(function () {
return total / this.numberOfAttendees;
}, my.vm);
};
my.vm = {
//observable array of attendees
attendees: ko.observableArray([new my.Attendee()]),
addAttendee: function () {
my.vm.attendees.push(new my.Attendee());
},
numberOfAttendees: function () {
my.vm.attendees.lenght + 1; //zero based
}
}
ko.applyBindings(my.vm);
});
</script>
I would probably do something like this.
function attendee(name, amount) {
var self = this;
this.name = ko.observable(name);
this.amount = ko.observable(amount);
}
function model() {
var self = this;
this.attendees = ko.observableArray();
this.total = ko.computed(function() {
var total = 0;
ko.utils.arrayForEach(this.attendees(), function(item) {
var value = parseFloat(item.amount());
if (!isNaN(value)) {
total += value;
}
});
return total.toFixed(2);
}, this);
this.average = ko.pureComputed(function() {
return self.total() / self.attendees().length;
}, this);
this.remove = function(row) {
self.attendees.remove(row);
}
this.name = ko.observable('');
this.amount = ko.observable('');
this.add = function() {
self.attendees.push(new attendee(self.name(), self.amount()));
self.amount('');
self.name('');
}
}
var mymodel = new model();
$(document).ready(function() {
ko.applyBindings(mymodel);
mymodel.attendees.push(new attendee('Bob', 25));
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
<td>Delete</td>
</tr>
</thead>
<tbody data-bind="foreach: attendees">
<tr>
<td data-bind="text:name"></td>
<td data-bind="text:amount"></td>
<td>
<button class="btn btn-danger" data-bind="click: $parent.remove">
X
</button>
</td>
</tr>
</tbody>
<tfooter>
<tr>
<td colspan=2>Average:
<span data-bind="text:average">
</span>
</td>
</tr>
</tfooter>
</table>
<form class="form form-inline">
<label class="sr-only" for="inlineFormInput">Name</label>
<input type="text" class="form-control" id="inlineFormInput" placeholder="enter name" data-bind="textInput: name">
<label class="sr-only" for="inlineFormInputGroup">Amount</label>
<div class="input-group ">
<div class="input-group-addon">$</div>
<input type="text" class="form-control" id="inlineFormInputGroup" placeholder="amount" data-bind="textInput: amount">
</div>
<button type="buttont" class="btn btn-primary" data-bind="click: add">add row </button>
</form>
The "Amount" computed will be recalculated whenever any child observables are updated. Therefore you'll need to make "total" an observable if you want it to trigger a change, and you'll need to make "numberOfAttendees" a computed itself so that it can update when my.vm.attendees gets updated, and that can then cascade to an update of the "Amount" computed.

AngularJS ToDO List Edit error

I have an issue in the Edit Part of a Simple Angular Todo App That I created.
My App Adds, deletes and Edits an Entry. I have used AngularJS and BootStrap for this.
The prolem is, when I press edit button, all the entries, get into edit mode instead of the entry that I want to edit.
I am not sure why the in place edit is not working as expected.
My HTML:
<!DOCTYPE html>
<html ng-app="myApp">
<head lang="en">
<meta charset="UTF-8">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<script src="../bower_components/angular/angular.min.js"></script>
<script src="todo.js"></script>
</head>
<body ng-controller="myCtrl">
<div class="container">
<h1>My TODO List</h1>
<table class="table">
<thead>
<tr>
<td><input class="form-control" ng-model="todo"></td>
<td><button class="btn btn-primary" ng-click="addItem()">Add</button></td>
</tr>
<tr>
<th>Serial No</th>
<th>Tasks to be Completed</th>
<th>Action</th>
<th>&nbsp</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in todolist">
<td >{{$index+1}}</td>
<td ng-hide="todo.selected == true">{{todo.task}}</td>
<td><input class="form-control" ng-show="todo.selected == true" ng-model="todo.task">
<button class="btn btn-success" ng-show="todo.selected == true" ng-click="save($index)">Save</button>
<button class="btn btn-danger" ng-show="todo.selected == true" ng-click="cancel($index)">Cancel</button>
<button class="btn btn-warning" ng-click="edit($index)" ng-hide="todo.selected == true">Edit</button>
<button class="btn btn-danger" ng-click="deleteItem($index)" ng-hide="todo.selected == true">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
My JavaScript Code:
angular.module("myApp",[])
.controller("myCtrl", function($scope){
$scope.todolist = [];
$scope.addItem = function(){
console.log($scope.todo);
if($scope.todo === undefined){
return false ;
}
else{
$scope.todoObj = {};
$scope.todoObj["task"] = $scope.todo;
$scope.todoObj["selected"] = false;
$scope.todolist.push($scope.todoObj);
$scope.todo = "";
console.log($scope.todolist);
}
}
$scope.deleteItem = function($index){
var index =$index;
$scope.todolist.splice(index,1);
}
$scope.edit = function($index){
for(var i=0; i< $scope.todolist.length; i++)
if($index == i){
$scope.todolist[i].selected = true;
}
}
$scope.save = function($index){
console.log("saving contact");
console.log($scope.todolist.length)
for(var i=0; i< $scope.todolist.length; i++){
if($index == i){
console.log($scope.todolist[$index]);
$scope.todolist[i] = `enter code here`angular.copy($scope.todolist[$index]);
// $scope.todolist[i] = $scope.todolist[$index];
$scope.todolist[i].selected = false;
console.log("todo after save",$scope.todolist);
}
}
}
$scope.cancel = function($index) {
for(var i=0; i< $scope.todolist.length; i++){
if ($scope.todolist[$index].selected !== false) {
$scope.todolist[$index].selected = `enter code here`$scope.todolist[i];
$scope.todolist[$index].selected = false;
}
}
};
})
When you are setting this
$scope.editing = $scope.todolist.indexOf(item);
All of your ng-repeats are relying on this to hide/show themselves.
ng-show="editing"

Categories