angularjs - [$injector:modulerr] error - javascript
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Batch editable table</title>
<link rel='stylesheet prefetch' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>
<link rel='stylesheet prefetch' href='https://cdn.rawgit.com/esvit/ng-table/v0.8.1/dist/ng-table.min.css'>
<link rel='stylesheet prefetch' href='css/nqjzro.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div ng-app="myApp" class="container-fluid">
<div class="row">
<div class="col-xs-12">
<h2 class="page-header">Batch editable table</h2>
<div class="row">
<div class="col-md-6">
<div class="bs-callout bs-callout-info">
<h4>Overview</h4>
<p>Example of how to create a batch editable table.</p>
</div>
</div>
<div class="col-md-6">
<div class="bs-callout bs-callout-warning">
<h4>Notice</h4>
<p>There are several directives in use that track dirty state and validity of the table rows. Whilst they are reasonally capable they are <em>not production tested - use at your own risk!</em> More details...</p>
<div ng-show="isExplanationOpen">
<p>If you look at the declarative markup for the <code>ngTable</code> you'll see a bunch of nested <code>ngForm</code> directives, with a common ancestor <code>ngForm</code> at the level of the table element. Each nested <code>ngForm</code> propogates their <code>$dirty</code> and <code>$invalid</code> state to this top level <code>ngForm</code>. This works great as you can enable/disable the buttons for saving the table based on the status of this single top-level <code>ngForm</code>.</p>
<p>This works up till the point that the user select's a new page to display in the table. At which point the existing nested <code>ngForm</code> directives are swapped out for new instances as the new data page is loaded. These new <code>ngForm</code> directives are always pristine and valid and this status propogates setting the corrosponding state on the top-level to be pristine and valid even though rows from the previous page are dirty and possibly invalid.</p>
<p>The solution is to have a set of directives that sit parallel to the <code>ngForm</code> directives that remember the state of the rows when the corrosponding <code>ngFrom</code> directives are destroyed and recreated. When <code>ngForm</code> directives are recreated they have their status reset by the directives that have remembered this state.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6" ng-controller="demoController as demo">
<h3>ngTable directive</h3>
<div class="brn-group pull-right">
<button class="btn btn-default" ng-if="demo.isEditing" ng-click="demo.cancelChanges()">
<span class="glyphicon glyphicon-remove"></span>
</button>
<button class="btn btn-primary" ng-if="!demo.isEditing" ng-click="demo.isEditing = true">
<span class="glyphicon glyphicon-pencil"></span>
</button>
<button class="btn btn-primary" ng-if="demo.isEditing" ng-disabled="!demo.hasChanges() || demo.tableTracker.$invalid" ng-click="demo.saveChanges()">
<span class="glyphicon glyphicon-ok"></span>
</button>
<button class="btn btn-default" ng-click="demo.add()">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
<table ng-table="demo.tableParams" class="table table-bordered table-hover table-condensed editable-table" ng-form="demo.tableForm" disable-filter="demo.isAdding" demo-tracked-table="demo.tableTracker">
<colgroup>
<col width="70%" />
<col width="12%" />
<col width="13%" />
<col width="5%" />
</colgroup>
<tr ng-repeat="row in $data" ng-form="rowForm" demo-tracked-table-row="row">
<td title="'Name'" filter="{name: 'text'}" sortable="'name'" ng-switch="demo.isEditing" ng-class="name.$dirty ? 'bg-warning' : ''" ng-form="name" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{row.name}}</span>
<div class="controls" ng-class="name.$invalid && name.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="text" name="name" ng-model="row.name" class="editable-input form-control input-sm" required />
</div>
</td>
<td title="'Age'" filter="{age: 'number'}" sortable="'age'" ng-switch="demo.isEditing" ng-class="age.$dirty ? 'bg-warning' : ''" ng-form="age" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{row.age}}</span>
<div class="controls" ng-class="age.$invalid && age.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="number" name="age" ng-model="row.age" class="editable-input form-control input-sm" required/>
</div>
</td>
<td title="'Money'" filter="{money: 'number'}" sortable="'money'" ng-switch="demo.isEditing" ng-class="money.$dirty ? 'bg-warning' : ''" ng-form="money" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{row.money}}</span>
<div class="controls" ng-class="money.$invalid && money.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="number" name="money" ng-model="row.money" class="editable-input form-control input-sm" required/>
</div>
</td>
<td>
<button class="btn btn-danger btn-sm" ng-click="demo.del(row)" ng-disabled="!demo.isEditing"><span class="glyphicon glyphicon-trash"></span></button>
</td>
</tr>
</table>
</div>
<div class="col-md-6" ng-controller="dynamicDemoController as demo">
<h3>ngTableDynamic directive</h3>
<div class="brn-group pull-right">
<button class="btn btn-default" ng-if="demo.isEditing" ng-click="demo.cancelChanges()">
<span class="glyphicon glyphicon-remove"></span>
</button>
<button class="btn btn-primary" ng-if="!demo.isEditing" ng-click="demo.isEditing = true">
<span class="glyphicon glyphicon-pencil"></span>
</button>
<button class="btn btn-primary" ng-if="demo.isEditing" ng-disabled="!demo.hasChanges() || demo.tableTracker.$invalid" ng-click="demo.saveChanges()">
<span class="glyphicon glyphicon-ok"></span>
</button>
<button class="btn btn-default" ng-click="demo.add()">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
<table ng-table-dynamic="demo.tableParams with demo.cols" class="table table-bordered table-condensed table-hover editable-table" ng-form="demo.tableForm" disable-filter="demo.isAdding" demo-tracked-table="demo.tableTracker">
<colgroup>
<col width="70%" />
<col width="12%" />
<col width="13%" />
<col width="5%" />
</colgroup>
<tr ng-repeat="row in $data" ng-form="rowForm" demo-tracked-table-row="row">
<td ng-repeat="col in $columns" ng-class="rowForm[col.field].$dirty ? 'bg-warning' : ''" ng-form="{{col.field}}" demo-tracked-table-cell>
<span ng-if="col.dataType !== 'command' && !demo.isEditing" class="editable-text">{{row[col.field]}}</span>
<div ng-if="col.dataType !== 'command' && demo.isEditing" class="controls" ng-class="rowForm[col.field].$invalid && rowForm[col.field].$dirty ? 'has-error' : ''" ng-switch="col.dataType">
<input ng-switch-default type="text" name="{{col.field}}" ng-model="row[col.field]" class="editable-input form-control input-sm" required />
<input ng-switch-when="number" type="number" name="{{col.field}}" ng-model="row[col.field]" class="editable-input form-control input-sm" required />
</div>
<button ng-if="col.dataType === 'command'" class="btn btn-danger btn-sm" ng-click="demo.del(row)" ng-disabled="!demo.isEditing"><span class="glyphicon glyphicon-trash"></span></button>
</td>
</tr>
</table>
</div>
</div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.4/angular.min.js'></script>
<script src='https://rawgit.com/esvit/ng-table/master/dist/ng-table.min.js'></script>
<script src="js/index.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular-route.js"></script>
</body>
</html>
The above is html code
angular.module("myApp", ["ngTable", "ngTableDemos"]);
(function() {
"use strict";
angular.module("myApp").controller("demoController", demoController);
demoController.$inject = ["NgTableParams", "ngTableSimpleList"];
function demoController(NgTableParams, simpleList) {
var self = this;
var originalData = angular.copy(simpleList);
self.tableParams = new NgTableParams({}, {
dataset: angular.copy(simpleList)
});
self.deleteCount = 0;
self.add = add;
self.cancelChanges = cancelChanges;
self.del = del;
self.hasChanges = hasChanges;
self.saveChanges = saveChanges;
//////////
function add() {
self.isEditing = true;
self.isAdding = true;
self.tableParams.settings().dataset.unshift({
name: "",
age: null,
money: null
});
// we need to ensure the user sees the new row we've just added.
// it seems a poor but reliable choice to remove sorting and move them to the first page
// where we know that our new item was added to
self.tableParams.sorting({});
self.tableParams.page(1);
self.tableParams.reload();
}
function cancelChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
self.tableParams.settings({
dataset: angular.copy(originalData)
});
// keep the user on the current page when we can
if (!self.isAdding) {
self.tableParams.page(currentPage);
}
}
function del(row) {
_.remove(self.tableParams.settings().dataset, function(item) {
return row === item;
});
self.deleteCount++;
self.tableTracker.untrack(row);
self.tableParams.reload().then(function(data) {
if (data.length === 0 && self.tableParams.total() > 0) {
self.tableParams.page(self.tableParams.page() - 1);
self.tableParams.reload();
}
});
}
function hasChanges() {
return self.tableForm.$dirty || self.deleteCount > 0
}
function resetTableStatus() {
self.isEditing = false;
self.isAdding = false;
self.deleteCount = 0;
self.tableTracker.reset();
self.tableForm.$setPristine();
}
function saveChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
originalData = angular.copy(self.tableParams.settings().dataset);
}
}
})();
(function() {
"use strict";
angular.module("myApp").controller("dynamicDemoController", dynamicDemoController);
dynamicDemoController.$inject = ["NgTableParams", "ngTableSimpleList"];
function dynamicDemoController(NgTableParams, simpleList) {
var self = this;
var originalData = angular.copy(simpleList);
self.cols = [{
field: "name",
title: "Name",
filter: {
name: "text"
},
sortable: "name",
dataType: "text"
}, {
field: "age",
title: "Age",
filter: {
age: "number"
},
sortable: "age",
dataType: "number"
}, {
field: "money",
title: "Money",
filter: {
money: "number"
},
sortable: "money",
dataType: "number"
}, {
field: "action",
title: "",
dataType: "command"
}];
self.tableParams = new NgTableParams({}, {
dataset: angular.copy(simpleList)
});
self.deleteCount = 0;
self.add = add;
self.cancelChanges = cancelChanges;
self.del = del;
self.hasChanges = hasChanges;
self.saveChanges = saveChanges;
//////////
function add() {
self.isEditing = true;
self.isAdding = true;
self.tableParams.settings().dataset.unshift({
name: "",
age: null,
money: null
});
// we need to ensure the user sees the new row we've just added.
// it seems a poor but reliable choice to remove sorting and move them to the first page
// where we know that our new item was added to
self.tableParams.sorting({});
self.tableParams.page(1);
self.tableParams.reload();
}
function cancelChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
self.tableParams.settings({
dataset: angular.copy(originalData)
});
// keep the user on the current page when we can
if (!self.isAdding) {
self.tableParams.page(currentPage);
}
}
function del(row) {
_.remove(self.tableParams.settings().dataset, function(item) {
return row === item;
});
self.deleteCount++;
self.tableTracker.untrack(row);
self.tableParams.reload().then(function(data) {
if (data.length === 0 && self.tableParams.total() > 0) {
self.tableParams.page(self.tableParams.page() - 1);
self.tableParams.reload();
}
});
}
function hasChanges() {
return self.tableForm.$dirty || self.deleteCount > 0
}
function resetTableStatus() {
self.isEditing = false;
self.isAdding = false;
self.deleteCount = 0;
self.tableTracker.reset();
self.tableForm.$setPristine();
}
function saveChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
originalData = angular.copy(self.tableParams.settings().dataset);
}
}
})();
(function() {
"use strict";
angular.module("myApp").run(configureDefaults);
configureDefaults.$inject = ["ngTableDefaults"];
function configureDefaults(ngTableDefaults) {
ngTableDefaults.params.count = 5;
ngTableDefaults.settings.counts = [];
}
})();
/**********
The following directives are necessary in order to track dirty state and validity of the rows
in the table as the user pages within the grid
------------------------
*/
(function() {
angular.module("myApp").directive("demoTrackedTable", demoTrackedTable);
demoTrackedTable.$inject = [];
function demoTrackedTable() {
return {
restrict: "A",
priority: -1,
require: "ngForm",
controller: demoTrackedTableController
};
}
demoTrackedTableController.$inject = ["$scope", "$parse", "$attrs", "$element"];
function demoTrackedTableController($scope, $parse, $attrs, $element) {
var self = this;
var tableForm = $element.controller("form");
var dirtyCellsByRow = [];
var invalidCellsByRow = [];
init();
////////
function init() {
var setter = $parse($attrs.demoTrackedTable).assign;
setter($scope, self);
$scope.$on("$destroy", function() {
setter(null);
});
self.reset = reset;
self.isCellDirty = isCellDirty;
self.setCellDirty = setCellDirty;
self.setCellInvalid = setCellInvalid;
self.untrack = untrack;
}
function getCellsForRow(row, cellsByRow) {
return _.find(cellsByRow, function(entry) {
return entry.row === row;
})
}
function isCellDirty(row, cell) {
var rowCells = getCellsForRow(row, dirtyCellsByRow);
return rowCells && rowCells.cells.indexOf(cell) !== -1;
}
function reset() {
dirtyCellsByRow = [];
invalidCellsByRow = [];
setInvalid(false);
}
function setCellDirty(row, cell, isDirty) {
setCellStatus(row, cell, isDirty, dirtyCellsByRow);
}
function setCellInvalid(row, cell, isInvalid) {
setCellStatus(row, cell, isInvalid, invalidCellsByRow);
setInvalid(invalidCellsByRow.length > 0);
}
function setCellStatus(row, cell, value, cellsByRow) {
var rowCells = getCellsForRow(row, cellsByRow);
if (!rowCells && !value) {
return;
}
if (value) {
if (!rowCells) {
rowCells = {
row: row,
cells: []
};
cellsByRow.push(rowCells);
}
if (rowCells.cells.indexOf(cell) === -1) {
rowCells.cells.push(cell);
}
} else {
_.remove(rowCells.cells, function(item) {
return cell === item;
});
if (rowCells.cells.length === 0) {
_.remove(cellsByRow, function(item) {
return rowCells === item;
});
}
}
}
function setInvalid(isInvalid) {
self.$invalid = isInvalid;
self.$valid = !isInvalid;
}
function untrack(row) {
_.remove(invalidCellsByRow, function(item) {
return item.row === row;
});
_.remove(dirtyCellsByRow, function(item) {
return item.row === row;
});
setInvalid(invalidCellsByRow.length > 0);
}
}
})();
(function() {
angular.module("myApp").directive("demoTrackedTableRow", demoTrackedTableRow);
demoTrackedTableRow.$inject = [];
function demoTrackedTableRow() {
return {
restrict: "A",
priority: -1,
require: ["^demoTrackedTable", "ngForm"],
controller: demoTrackedTableRowController
};
}
demoTrackedTableRowController.$inject = ["$attrs", "$element", "$parse", "$scope"];
function demoTrackedTableRowController($attrs, $element, $parse, $scope) {
var self = this;
var row = $parse($attrs.demoTrackedTableRow)($scope);
var rowFormCtrl = $element.controller("form");
var trackedTableCtrl = $element.controller("demoTrackedTable");
self.isCellDirty = isCellDirty;
self.setCellDirty = setCellDirty;
self.setCellInvalid = setCellInvalid;
function isCellDirty(cell) {
return trackedTableCtrl.isCellDirty(row, cell);
}
function setCellDirty(cell, isDirty) {
trackedTableCtrl.setCellDirty(row, cell, isDirty)
}
function setCellInvalid(cell, isInvalid) {
trackedTableCtrl.setCellInvalid(row, cell, isInvalid)
}
}
})();
(function() {
angular.module("myApp").directive("demoTrackedTableCell", demoTrackedTableCell);
demoTrackedTableCell.$inject = [];
function demoTrackedTableCell() {
return {
restrict: "A",
priority: -1,
scope: true,
require: ["^demoTrackedTableRow", "ngForm"],
controller: demoTrackedTableCellController
};
}
demoTrackedTableCellController.$inject = ["$attrs", "$element", "$scope"];
function demoTrackedTableCellController($attrs, $element, $scope) {
var self = this;
var cellFormCtrl = $element.controller("form");
var cellName = cellFormCtrl.$name;
var trackedTableRowCtrl = $element.controller("demoTrackedTableRow");
if (trackedTableRowCtrl.isCellDirty(cellName)) {
cellFormCtrl.$setDirty();
} else {
cellFormCtrl.$setPristine();
}
// note: we don't have to force setting validaty as angular will run validations
// when we page back to a row that contains invalid data
$scope.$watch(function() {
return cellFormCtrl.$dirty;
}, function(newValue, oldValue) {
if (newValue === oldValue) return;
trackedTableRowCtrl.setCellDirty(cellName, newValue);
});
$scope.$watch(function() {
return cellFormCtrl.$invalid;
}, function(newValue, oldValue) {
if (newValue === oldValue) return;
trackedTableRowCtrl.setCellInvalid(cellName, newValue);
});
}
})();
This is javascript file
The above code is angularjs. It displays table loading data with add row, delete, edit.
Please solve this. Am getting error [$injector:modulerr] . Please help me to solve
Edit: They created different codepen for ngTableDemos look here
To run your sample you need to load that ngTableDemos related code first then your sample.
For codepen, to understand what are the files they are using to run any sample check Pen Setting JavaScript tab carefully.
Probably you didn't load "ngTableDemos" module, so if it is needed load associate file or remove it like
angular.module("myApp", ["ngTable"]);
Related
assign value to selected input box in a row using Angular js
row add using angular code. Input box model name is "code". when click this input box a model open and when any row select using angularjs then there is a problem when I assign value using $scope.code it will assign to all input box in but I want to assign that value by which ng-click performed. <table class="table table-bordered table-hover table-condensed"> <tr style="font-weight: bold"> <td style="width:35%">Code</td> </tr> <tr ng-repeat="user in users"> <td> <!-- editable username (text with validation) --> <span ng-show="editDel"> #{{ user.name || 'empty' }} </span> #{{code}} <input type="text" name="" ng-model="code" ng-show="saveCancel" ng-click="getCode()"> </td> <td style="white-space: nowrap"> <!-- form --> <form ng-show="saveCancel" class="form-buttons form-inline"> <button type="submit" class="btn btn-primary"> save </button> <button type="button" ng-click="isCancel()" class="btn btn-default"> cancel </button> </form> <div class="buttons" ng-show="editDel"> <button class="btn btn-primary" ng-click="editUser($index)">edit</button> <button class="btn btn-danger" ng-click="removeUser($index)">del</button> </div> </td> </tr> </table> $scope.users = []; $scope.saveCancel = false; $scope.editDel = true; $scope.saveUser = function(data, id) { //$scope.user not updated yet angular.extend(data, {id: id}); return $http.post('/saveUser', data); }; // remove user $scope.removeUser = function(index) { $scope.users.splice(index, 1); }; // add user add row $scope.addUser = function() { $scope.inserted = { id: $scope.users.length+1, name: '', status: null, group: null }; // $scope.taxNamePopup(); $scope.users.push($scope.inserted); }; $scope.getCode = function(){ // alert(input_id); this.code = 'nameValue';//**this assin value to selected box** // jQuery("#tax_modal").modal("hide"); var request = $http({ method: "get", url: base_url+"/load-tax", data: { }, }).then(function successCallback(response) { jQuery("#tax_list").modal("show"); $scope.tax_data1 = response; }, function errorCallback(response) { }); } $scope.getSelected = function(id,code,desc,rate){ alert(code); this.code = code;//**but i want to assign value to selected input box from here** jQuery("#tax_list").modal("hide"); } $scope.safeApply(function() { $scope.editUser = function($id){ this.saveCancel = true; this.editDel = false; } }); $scope.isCancel = function(){ this.saveCancel = false; this.editDel = true; }
it is because ng-model is codefor every input. Change it with following code example <input type="text" name="" ng-model="code_$index" ng-show="saveCancel" ng-click="getCode($index)"> Now above code will change the ng-model unique for each input field and retrieve the value in getCode on the basis of index.
the model should be different for each ng-repeat item EDIT change from getCode(user.code) to getCode(user) <input type="text" name="" ng-model="user.code" ng-show="saveCancel" ng-click="getCode(user)"> use the param how ever you want ;) $scope.getCode = function(user){ angular.forEach($scope.users, function (value, key) { if (value.id == user.id) { $scope.users[key]=user; } }); }
Angular Js updating view in an event not working
I'm looking to build an book shelf application which add, removes, edits books shelf data using angular routing. But when I try to prepopulate a record to edit it, I'm not able to get the prepopulated values. I have tried to assign value using $scope.{model name}. but it is not working App.js var myApp = angular.module('myApp', []); myApp.config(['$routeProvider', function($routeProvider) { $routeProvider.when('/viewBook', { template : '<div ng-controller="ViewBookController"> <h2>Books</h2> <table class="table table-bordered table-condensed"> <thead> <tr> <td>Name</td> <td>Category</td> <td>Price</td> <td>Actions</td> </tr> </thead> <tr ng-repeat ="book in books"> <td>{{book.name}}</td> <td>{{book.category}}</td> <td>{{book.price}}</td> <td><span class="glyphicon glyphicon-edit">Edit</span> | <span class="glyphicon glyphicon-trash">Delete</span> </td> </tr> </table> </div>', controller : 'ViewBookController'}). when('/addBook', { template : '<div ng-controller = "ViewBookController"> <form class="well"> <label>Name*</label> <input type="text" name="name" ng-model="addBook.name" /><br/> <label>Category*</label> <input type="text" name="email" ng-model="addBook.category" /><br/> <label>Price*</label> <input type="text" name="phone" ng-model="addBook.price" /><br/> <br/> <input type="hidden" ng-model="addBook.id"/> <button ng-click ="add(addBook)">Add </button> <button type="button" class="btn btn-danger"><a href="#/viewBook">Cancel</button> </form> </div> ', controller : 'ViewBookController'}). otherwise({redirectTo: '/viewBook'}); }]); myApp.controller('ViewBookController', function($scope, ViewBookService) { $scope.tempbook =""; $scope.books = ViewBookService.list(); $scope.delete = function (id) { ViewBookService.delete(id); // if ($scope.addBook.id == id) // $scope.addBook = {}; } $scope.add = function() { ViewBookService.save($scope.addBook); $scope.addBook = {}; alert('book added successfully'); } $scope.edit = function (addBook,id) { //$scope.addbook = $scope.tempbook; $scope.addBook= (angular.copy(ViewBookService.get(id))); } }); myApp.service('ViewBookService', function() { //to create unique book id var uid = 3; //books array to hold list of all books var books = [{ id : 0, name : 'Java', category : 'software', price : 600, }, { id : 1, name : 'Sherlock Holmes', category : 'fiction', price : 350, }, { id : 2, name : 'Wings of Fire', category : 'autobiography', price : 250, }]; //save method create a new book if doesnt exist else update the existing object this.save = function(book) { if(book.id == null) { book.id =uid++; books.push(book) } else { for(i in books) { if(books[i].id == book.id) { books[i] ==book; } } } } //simply search books list for given id and returns the book object if found this.get = function(id) { for(i in books) { if( books[i].id == id) { return books[i]; } } } //iterate through books list and delete book if found this.delete = function (id) { for (i in books) { if (books[i].id == id) { books.splice(i, 1); } } } //simply returns the books list this.list = function () { return books; } }); index.html <!DOCTYPE html> <html ng-app="myApp"> <head> <title>Hello AngularJS</title> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.min.js"></script> <script src="app.js"></script> </head> <body> <div class="container"> <div class="col"> <div class="col-md-3"> <ul class="nav"> <li> View Book </li> <li> Add Book </li> </ul> </div> <div class="col-md-9"> <div ng-view></div> </div> </div> </div> </body> </html>
angular-formly formatters do not work
Invalid first value in formatter with datapicker field Similar formatter with text input works fine You can see an example in the code bellow: /* global angular */ (function() { 'use strict'; var app = angular.module('formlyExample', ['formly', 'formlyBootstrap', 'ui.bootstrap']); app.run(function(formlyConfig) { var attributes = [ 'date-disabled', 'custom-class', 'show-weeks', 'starting-day', 'init-date', 'min-mode', 'max-mode', 'format-day', 'format-month', 'format-year', 'format-day-header', 'format-day-title', 'format-month-title', 'year-range', 'shortcut-propagation', 'datepicker-popup', 'show-button-bar', 'current-text', 'clear-text', 'close-text', 'close-on-date-selection', 'datepicker-append-to-body' ]; var bindings = [ 'datepicker-mode', 'min-date', 'max-date' ]; var ngModelAttrs = {}; angular.forEach(attributes, function(attr) { ngModelAttrs[camelize(attr)] = { attribute: attr }; }); angular.forEach(bindings, function(binding) { ngModelAttrs[camelize(binding)] = { bound: binding }; }); formlyConfig.setType({ name: 'datepicker', templateUrl: 'datepicker.html', wrapper: ['bootstrapLabel', 'bootstrapHasError'], defaultOptions: { ngModelAttrs: ngModelAttrs, templateOptions: { datepickerOptions: { format: 'MM.dd.yyyy' } } }, controller: ['$scope', function($scope) { $scope.datepicker = {}; $scope.datepicker.opened = false; $scope.datepicker.open = function($event) { $scope.datepicker.opened = !$scope.datepicker.opened; }; } ] }); function camelize(string) { string = string.replace(/[\-_\s]+(.)?/g, function(match, chr) { return chr ? chr.toUpperCase() : ''; }); // Ensure 1st char is always lowercase return string.replace(/^([A-Z])/, function(match, chr) { return chr ? chr.toLowerCase() : ''; }); } }); app.controller('MainCtrl', function MainCtrl(formlyVersion) { var vm = this; // funcation assignment vm.onSubmit = onSubmit; // variable assignment vm.author = { // optionally fill in your info below :-) name: 'Kent C. Dodds', url: 'https://twitter.com/kentcdodds' }; vm.exampleTitle = 'UI Bootstrap Datepicker'; // add this vm.env = { angularVersion: angular.version.full, formlyVersion: formlyVersion }; vm.model = { year: 2005, text: 'hello' }; vm.options = {}; vm.fields = [{ key: 'year', type: 'datepicker', templateOptions: { label: 'Year', type: 'text', datepickerPopup: 'dd-MMMM-yyyy', datepickerOptions: { format: 'yyyy', minMode: 'year' } }, parsers: [dateToYear], formatters: [yearToDate] }, { key: 'text', type: 'input', templateOptions: { label: 'Text', }, parsers: [toLowerCase], formatters: [toUpperCase] }]; vm.originalFields = angular.copy(vm.fields); // function definition function onSubmit() { vm.options.updateInitialValue(); alert(JSON.stringify(vm.model), null, 2); } function yearToDate(value) { console.log('yearToDate: ' + value); if (!value) { return new Date(); } return (typeof value === 'object') ? value : (new Date()).setFullYear(value); } function dateToYear(value) { console.log('dateToYear: ' + value); return (typeof value === 'object') ? value.getFullYear() : null; } function toUpperCase(value) { console.log('toUpperCase: ' + value); return (value || '').toUpperCase(); } function toLowerCase(value) { console.log('toLowerCase: ' + value); return (value || '').toLowerCase(); } }); })(); body { margin: 20px } .formly-field { margin-bottom: 16px; } <!DOCTYPE html> <html> <head> <!-- Twitter bootstrap --> <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet"> <!-- apiCheck is used by formly to validate its api --> <script src="//npmcdn.com/api-check#latest/dist/api-check.js"></script> <!-- This is the latest version of angular (at the time this template was created) --> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script> <!-- Angular-UI Bootstrap has tabs directive we want --> <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.2/ui-bootstrap-tpls.js"></script> <!-- This is the latest version of formly core. --> <script src="//npmcdn.com/angular-formly#latest/dist/formly.js"></script> <!-- This is the latest version of formly bootstrap templates --> <script src="//npmcdn.com/angular-formly-templates-bootstrap#latest/dist/angular-formly-templates-bootstrap.js"></script> <title>Angular Formly Example</title> </head> <body ng-app="formlyExample" ng-controller="MainCtrl as vm"> <div> <h1>angular-formly example: {{vm.exampleTitle}}</h1> <div> This is a <strong>very</strong> basic example of how to use the UI Boostrap datepicker with angular-formly. </div> <hr /> <form ng-submit="vm.onSubmit()" name="vm.form" novalidate> <formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form"> <button type="submit" class="btn btn-primary submit-button" ng-disabled="vm.form.$invalid">Submit</button> <button type="button" class="btn btn-default" ng-click="vm.options.resetModel()">Reset</button> </formly-form> </form> <hr /> <h2>Model Value</h2> <pre>{{vm.model | json}}</pre> <h2>Fields <small>(note, functions are not shown)</small></h2> <pre>{{vm.originalFields | json}}</pre> <h2>Form</h2> <pre>{{vm.form | json}}</pre> </div> <div style="margin-top:30px"> <small> This is an example for the angular-formly project made with ♥ by <strong> <span ng-if="!vm.author.name || !vm.author.url"> {{vm.author.name || 'anonymous'}} </span> <a ng-if="vm.author.url" ng-href="{{::vm.author.url}}"> {{vm.author.name}} </a> </strong> <br /> This example is running angular version "{{vm.env.angularVersion}}" and formly version "{{vm.env.formlyVersion}}" </small> </div> <!-- Put custom templates here --> <script type="text/ng-template" id="datepicker.html"> <p class="input-group"> <input type="text" id="{{::id}}" name="{{::id}}" ng-model="model[options.key]" class="form-control" ng-click="datepicker.open($event)" uib-datepicker-popup="{{to.datepickerOptions.format}}" is-open="datepicker.opened" datepicker-options="to.datepickerOptions" /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="datepicker.open($event)" ng-disabled="to.disabled"><i class="glyphicon glyphicon-calendar"></i></button> </span> </p> </script> </body> </html> Look console logs: first time formatter get EMPTY value, but have to 2005 (as defined in vm.model) P.S. In this example I noticed one more bug: initial value datepicker is not formatted I got Tue May 17 2016 18:25:03 GMT+0300 (EEST) but have to 2016 I check datapicker same version without formly and it works fine. Example bellow: angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']); angular.module('ui.bootstrap.demo').controller('DatepickerPopupDemoCtrl', function($scope) { $scope.dt = '2005-01-31'; $scope.today = function() { $scope.dt = new Date(); }; $scope.today(); $scope.clear = function() { $scope.dt = null; }; $scope.inlineOptions = { minDate: new Date(), showWeeks: true }; $scope.dateOptions = { formatYear: 'yy', startingDay: 1 }; $scope.open1 = function() { $scope.popup1.opened = true; }; $scope.open2 = function() { $scope.popup2.opened = true; }; $scope.setDate = function(year, month, day) { $scope.dt = new Date(year, month, day); }; $scope.popup1 = { opened: false }; $scope.popup2 = { opened: false }; }); body { padding: 20px; } <!doctype html> <html ng-app="ui.bootstrap.demo"> <head> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.js"></script> <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.3.2.js"></script> <script src="example.js"></script> <link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div ng-controller="DatepickerPopupDemoCtrl"> <pre>Selected date is: <em>{{dt | date:'fullDate' }}</em></pre> <h4>Popup</h4> <div class="row"> <div class="col-md-6"> <p class="input-group"> <input type="text" class="form-control" uib-datepicker-popup="MMM dd, yyyy" ng-model="dt" is-open="popup1.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button> </span> </p> </div> <div class="col-md-6"> <p class="input-group"> <input type="text" class="form-control" uib-datepicker-popup ng-model="dt" is-open="popup2.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="open2()"><i class="glyphicon glyphicon-calendar"></i></button> </span> </p> </div> </div> <hr /> <button type="button" class="btn btn-sm btn-info" ng-click="today()">Today</button> <button type="button" class="btn btn-sm btn-default" ng-click="setDate(2009, 7, 24)">2009-08-24</button> <button type="button" class="btn btn-sm btn-danger" ng-click="clear()">Clear</button> </div> </body> </html> I suppose formatters not calling first time when template initializing. An example of input[type=number] throwing an error if the initial value is a string: /* global angular */ (function() { 'use strict'; var app = angular.module('formlyExample', ['formly', 'formlyBootstrap', 'ui.bootstrap']); app.run(function(formlyConfig) { var attributes = [ 'date-disabled', 'custom-class', 'show-weeks', 'starting-day', 'init-date', 'min-mode', 'max-mode', 'format-day', 'format-month', 'format-year', 'format-day-header', 'format-day-title', 'format-month-title', 'year-range', 'shortcut-propagation', 'datepicker-popup', 'show-button-bar', 'current-text', 'clear-text', 'close-text', 'close-on-date-selection', 'datepicker-append-to-body' ]; var bindings = [ 'datepicker-mode', 'min-date', 'max-date' ]; var ngModelAttrs = {}; angular.forEach(attributes, function(attr) { ngModelAttrs[camelize(attr)] = { attribute: attr }; }); angular.forEach(bindings, function(binding) { ngModelAttrs[camelize(binding)] = { bound: binding }; }); formlyConfig.setType({ name: 'datepicker', templateUrl: 'datepicker.html', wrapper: ['bootstrapLabel', 'bootstrapHasError'], defaultOptions: { ngModelAttrs: ngModelAttrs, templateOptions: { datepickerOptions: { format: 'MM.dd.yyyy' } } }, controller: ['$scope', function($scope) { $scope.datepicker = {}; $scope.datepicker.opened = false; $scope.datepicker.open = function($event) { $scope.datepicker.opened = !$scope.datepicker.opened; }; } ] }); function camelize(string) { string = string.replace(/[\-_\s]+(.)?/g, function(match, chr) { return chr ? chr.toUpperCase() : ''; }); // Ensure 1st char is always lowercase return string.replace(/^([A-Z])/, function(match, chr) { return chr ? chr.toLowerCase() : ''; }); } }); app.controller('MainCtrl', function MainCtrl(formlyVersion) { var vm = this; // funcation assignment vm.onSubmit = onSubmit; // variable assignment vm.author = { // optionally fill in your info below :-) name: 'Kent C. Dodds', url: 'https://twitter.com/kentcdodds' }; vm.exampleTitle = 'UI Bootstrap Datepicker'; // add this vm.env = { angularVersion: angular.version.full, formlyVersion: formlyVersion }; vm.model = { year: 2005, text: 'hello', number: '123.4' }; vm.options = {}; vm.fields = [{ key: 'year', type: 'datepicker', templateOptions: { label: 'Year', type: 'text', datepickerPopup: 'dd-MMMM-yyyy', datepickerOptions: { format: 'yyyy', minMode: 'year' } }, parsers: [dateToYear], formatters: [yearToDate] }, { key: 'text', type: 'input', templateOptions: { label: 'Text', }, parsers: [toLowerCase], formatters: [toUpperCase] }, { key: 'number', type: 'input', templateOptions: { label: 'Number', type: 'number' }, formatters: [strToNumber] }]; vm.originalFields = angular.copy(vm.fields); // function definition function onSubmit() { vm.options.updateInitialValue(); alert(JSON.stringify(vm.model), null, 2); } function yearToDate(value) { console.log('yearToDate: ' + value); if (!value) { return new Date(); } return (typeof value === 'object') ? value : (new Date()).setFullYear(value); } function dateToYear(value) { console.log('dateToYear: ' + value); return (typeof value === 'object') ? value.getFullYear() : null; } function toUpperCase(value) { console.log('toUpperCase: ' + value); return (value || '').toUpperCase(); } function toLowerCase(value) { console.log('toLowerCase: ' + value); return (value || '').toLowerCase(); } function strToNumber(value) { console.log('strToNumber: ' + value); return parseFloat(value); } }); })(); body { margin: 20px } .formly-field { margin-bottom: 16px; } <!DOCTYPE html> <html> <head> <!-- Twitter bootstrap --> <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet"> <!-- apiCheck is used by formly to validate its api --> <script src="//npmcdn.com/api-check#latest/dist/api-check.js"></script> <!-- This is the latest version of angular (at the time this template was created) --> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script> <!-- Angular-UI Bootstrap has tabs directive we want --> <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.2/ui-bootstrap-tpls.js"></script> <!-- This is the latest version of formly core. --> <script src="//npmcdn.com/angular-formly#latest/dist/formly.js"></script> <!-- This is the latest version of formly bootstrap templates --> <script src="//npmcdn.com/angular-formly-templates-bootstrap#latest/dist/angular-formly-templates-bootstrap.js"></script> <title>Angular Formly Example</title> </head> <body ng-app="formlyExample" ng-controller="MainCtrl as vm"> <div> <h1>angular-formly example: {{vm.exampleTitle}}</h1> <div> This is a <strong>very</strong> basic example of how to use the UI Boostrap datepicker with angular-formly. </div> <hr /> <form ng-submit="vm.onSubmit()" name="vm.form" novalidate> <formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form"> <button type="submit" class="btn btn-primary submit-button" ng-disabled="vm.form.$invalid">Submit</button> <button type="button" class="btn btn-default" ng-click="vm.options.resetModel()">Reset</button> </formly-form> </form> <hr /> <h2>Model Value</h2> <pre>{{vm.model | json}}</pre> <h2>Fields <small>(note, functions are not shown)</small></h2> <pre>{{vm.originalFields | json}}</pre> <h2>Form</h2> <pre>{{vm.form | json}}</pre> </div> <div style="margin-top:30px"> <small> This is an example for the angular-formly project made with ♥ by <strong> <span ng-if="!vm.author.name || !vm.author.url"> {{vm.author.name || 'anonymous'}} </span> <a ng-if="vm.author.url" ng-href="{{::vm.author.url}}"> {{vm.author.name}} </a> </strong> <br /> This example is running angular version "{{vm.env.angularVersion}}" and formly version "{{vm.env.formlyVersion}}" </small> </div> <!-- Put custom templates here --> <script type="text/ng-template" id="datepicker.html"> <p class="input-group"> <input type="text" id="{{::id}}" name="{{::id}}" ng-model="model[options.key]" class="form-control" ng-click="datepicker.open($event)" uib-datepicker-popup="{{to.datepickerOptions.format}}" is-open="datepicker.opened" datepicker-options="to.datepickerOptions" /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="datepicker.open($event)" ng-disabled="to.disabled"><i class="glyphicon glyphicon-calendar"></i></button> </span> </p> </script> </body> </html> Also problem described in this issue Maybe somebody can help me. Appreciate your help.
Angularjs Form $valid not update
I have a custom directive that is an ng-modal window. My issue is when I set one of the fields(patientId) on show in code, the form's $valid won't update to true even tho all the required fields are true. When I updated the patientid field thru the UI, $valid updates correctly. Since the field in question is disabled in current cases, I can't rely on the user manually updated the field. As a quick fix I used the $error.required for the disable for the Save button but want to do it the correct way. 'use strict'; var mod try { mod = angular.module('DCI'); } catch (err) { mod = angular.module('DCI', []); } mod.directive('labResultEntry', function () { return { restrict: 'E', scope: { labRanges: '=', show: '=', patientList: '=', editResult: '=', saveLabCallback: '=' }, replace: true, link: function (scope, element, attrs) { scope.dialogStyle = {}; scope.result = {}; scope.autoComplateControl1 = {}; scope.autoComplateControl2 = {}; scope.result.SensitiveData = 'N'; scope.editdisable = scope.isOpen = false; var rootScope = scope.$parent; if (attrs.saveResultCallback) scope.saveResultCallback = attrs.saveResultCallback if (attrs.width) scope.dialogStyle.width = attrs.width; if (attrs.height) scope.dialogStyle.height = attrs.height; scope.$watch('show', function () { if (scope.show == true) { if (scope.editResult != undefined) { scope.editdisable = true; scope.autoComplateControl1.insertInput(rootScope.patientName); scope.result.patientId = rootScope.patientId; scope.result.sampledate = scope.editResult.sampledate; scope.result.TestCode = scope.editResult.TestCode scope.result.Result = parseFloat(scope.editResult.Result); scope.result.LabResultId = scope.editResult.LabResultId; angular.forEach(scope.labRanges, function (test) { if (test.TestCode == scope.editResult.TestCode) { scope.result.TestDescription = test.TestDescription; scope.result.ShortName = test.ShortName; scope.result.MaxValue = test.MaxValue; scope.result.MinValue = test.MinValue; scope.result.UOM = test.UOM; scope.autoComplateControl2.insertInput(test.TestDescription); } }) } else scope.editdisable = false; SetPatientId(); } }, true); function SetPatientId() { if (rootScope.patientId) { scope.autoComplateControl1.insertInput(rootScope.patientName); rootScope.safeApply(function(){ scope.result.patientId = rootScope.patientId; }); } else { if (scope.autoComplateControl1.clearnInput != undefined) { scope.autoComplateControl1.clearnInput(); } scope.result.patientId = undefined; } }; function reset() { scope.result = {}; scope.result.SensitiveData = 'N'; SetPatientId(); scope.autoComplateControl2.clearnInput(); scope.editResult = undefined; }; scope.hideModal = function () { reset(); scope.show = false; }; scope.autoCompleteSelect = function (item) { if (item) { scope.result.MaxValue = item.description.MaxValue; scope.result.MinValue = item.description.MinValue; scope.result.UOM = item.description.UOM; scope.result.ShortName = item.description.ShortName; scope.result.TestCode = parseFloat(item.description.TestCode, 10); scope.result.TestDescription = item.description.TestDescription; } }; scope.open = function ($event) { $event.preventDefault(); $event.stopPropagation(); scope.isOpen = true; }; scope.selectPatient = function (item) { if (item) { var found = false; angular.forEach(scope.patientList, function (itemp) { if (item.originalObject.PatientId = itemp.PatientId) found = true; }); if (found) scope.result.patientId = item.originalObject.PatientId; } }; scope.LookUpTestCode = function () { angular.forEach(scope.labRanges, function (test) { if (test.TestCode == scope.result.TestCode) { scope.result.TestDescription = test.TestDescription; scope.result.ShortName = test.ShortName; scope.result.MaxValue = test.MaxValue; scope.result.MinValue = test.MinValue; scope.result.UOM = test.UOM; scope.autoComplateControl2.insertInput(test.TestDescription); } }) }; scope.saveResult = function () { var result = {}; angular.copy(scope.result, result); result.labLocation = "002060"; scope.saveLabCallback(result); scope.hideModal() }; }, template: "HTML code below as single string" <div class="ng-modal colored-header" ng-show="show"> <div class="ng-modal-overlay" ng-click="hideLabModal()"></div> <div class="ng-modal-dialog md-content" ng-style="dialogStyle"> <div class="modal-header"> <div class="ng-modal-close" ng-click="hideModal()">X</div> <h3>Lab Result</h3> </div> <div class="ng-modal-dialog-content"> <form name="labEntry" role="form" class="modal-body"> <div angucomplete-alt id="ex2" placeholder="Patient*" maxlength="50" pause="400" selected-object="selectPatient" local-data="patientList" field-required="!result.patientId" field-required-class="ng-invalid" search-fields="PatientName" disable-input="editdisable" title-field="PatientName" minlength="4" input-class="form-control-small form-control" match-class="highlight" control="autoComplateControl1" /> <br /> <input class="form-control col-xs-9 col-sm-9 col-md-9" style="margin-top: 3px; margin-bottom: 3px;" type="text" ng-disabled="true" ng-model="result.labLocation" placeholder="OTHER (LAB)" /> <br /> <br /> <br /> <input ng-style="{ 'border-color' : (labEntry.TestCode.$valid == false ? 'red' : 'null') }" name="TestCode" class="form-control col-xs-9 col-sm-9 col-md-9" ng-disabled="editdisable" ng-required="!result.TestCode" ng-model="result.TestCode" placeholder="Test code*" ng-blur="LookUpTestCode()" /> <br /> <br /> <br /> <div angucomplete-alt id="ex3" placeholder="Description*" maxlength="50" pause="400" selected-object="autoCompleteSelect" local-data="labRanges" search-fields="TestDescription" disable-input="editdisable" title-field="TestDescription" minlength="4" input-class="form-control form-control-small" match-class="highlight" control="autoComplateControl2" /> <br /> <input ng-style="{ 'border-color' : (labEntry.Result.$valid == false ? 'red' : 'null') }" style="margin-top: 3px; margin-bottom: 3px;" name="Result" class="form-control col-xs-9 col-sm-9 col-md-9" ng-required="!result.Result" ng-model="result.Result" placeholder="Result*" /> <br /> <br /> <br /> <p class="input-group"> <input name="sdate" ng-style="{ 'border-color' : (labEntry.sdate.$valid == false ? 'red' : 'null') }" class="input-group-addon form-control" is-open="isOpen" datepicker-popup="MM/dd/yyyy" ng-required="!result.sampledate" ng-model="result.sampledate" placeholder="Sample Date*" /> <span class="input-group-btn"> <span class="input-group-addon btn btn-primary" ng-click="open($event)"><i class="glyphicon glyphicon-th"></i></span> </span> </p> <input class="col-xs-6 col-sm-6 col-md-6" type="text" ng-disabled="true" ng-model="result.MinValue" placeholder="Range Min" /> <input class="col-xs-6 col-sm-6 col-md-6" type="text" ng-disabled="true" ng-model="result.MaxValue" placeholder="Range Max" /> <br /> <br /> <input class="col-xs-6 col-sm-6 col-md-6" type="text" ng-disabled="true" ng-model="result.UOM" placeholder="UOM" /> <br /> <br /> <input type="checkbox" class="iCheck" icheck ng-model="result.SensitiveData" ng-true-value="'Y'" ng-false-value="'N'" /> Sensitive Data <br /> <br /> <textarea class="form-control" rows="4" placeholder="comments..." ng-model="result.Comment"></textarea> </form> <div class="modal-footer"> <button type="button" class="btn btn-flat md-close" ng-click="hideModal()">Cancel</button> <button type="button" class="btn btn-flat btn-success" ng-disabled="!labEntry.$valid" ng-click="saveResult()">Save</button> </div> </div> </div> </div>
The digest() method might help: add this function to the end and call it from any function that changes values to the scope after everything is set: function digest() { if ( scope.$$phase !== '$apply' && scope.$$phase !== '$digest' ) { scope.$digest(); } } You can get more Info on the digest function on Angulars Website: Processes all of the watchers of the current scope and its children. Because a watcher's listener can change the model, the $digest() keeps calling the watchers until no more listeners are firing. https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest I hope this helps.
Figured it out. It was an issue with Autocomplete-alt directive I was using. Updated to 1.1.0 and it solved it.
Load data from Database to DropDownList using Knockout
I'm creating editing a variable length list using knockout. When I click Add Button, it will add a DropDownList and a TextBox to the screen. I've successfully load the data from database to DropDownList, but it always populating the data every time I clicked Add Button. Code: <div class="form-horizontal" data-bind="with: purchaseOrder"> <h4>Purchase Order</h4> <hr /> <div class="form-group"> <label class="control-label col-md-2" for="PurchaseOrderDate">PO Date</label> <div class="col-md-10"> <input class="form-control" data-bind="value: PurchaseOrderDate" placeholder="Purchase Order Date" /> </div> </div> <div class="form-group"> <label class="control-label col-md-2" for="InvoiceNo">Invoice No</label> <div class="col-md-10"> <input class="form-control" data-bind="value: InvoiceNo" placeholder="Invoice No" /> </div> </div> <div class="form-group"> <label class="control-label col-md-2" for="Memo">Memo</label> <div class="col-md-10"> <input class="form-control" data-bind="value: Memo" placeholder="Enter Memo" /> </div> </div> </div> <h4>Details</h4> <hr /> <table class="table"> <thead> <tr> <th>Item Name</th> <th>Qty Order</th> <th></th> </tr> </thead> <tbody data-bind="foreach: purchaseOrderDetails"> <tr> <td> <select class="form-control" data-bind="options: AX_INVENTSUMs, optionsText: 'ITEMNAME', optionValue: 'ITEMID'"></select> </td> <td> <input class="form-control" data-bind="value: QuantityOrder" placeholder="Enter Quantity Order"> </td> <td> <a class="btn btn-sm btn-danger" href='#' data-bind=' click: $parent.removeItem'>X</a> </td> </tr> </tbody> </table> <p> <button class="btn btn-sm btn-primary" data-bind='click: addItem'>Add Item</button> </p> #section Scripts { #Scripts.Render("~/bundles/knockout") <script> $(function () { var PurchaseOrder = function (purchaseOrder) { var self = this; self.PurchaseOrderID = ko.observable(purchaseOrder ? purchaseOrder.PurchaseOrderID : 0); self.PurchaseOrderDate = ko.observable(purchaseOrder ? purchaseOrder.PurchaseOrderDate : ''); self.InvoiceNo = ko.observable(purchaseOrder ? purchaseOrder.InvoiceNo : ''); self.Memo = ko.observable(purchaseOrder ? purchaseOrder.Memo : ''); }; var PurchaseOrderDetail = function (purchaseOrderDetail, items) { var self = this; self.PurchaseOrderDetailID = ko.observable(purchaseOrderDetail ? purchaseOrderDetail.PurchaseOrderDetailID : 0); self.PurchaseOrderID = ko.observable(purchaseOrderDetail ? purchaseOrderDetail.PurchaseOrderDetailID : 0); self.ItemID = ko.observable(purchaseOrderDetail ? purchaseOrderDetail.ItemID : 0); self.QuantityOrder = ko.observable(purchaseOrderDetail ? purchaseOrderDetail.QuantityOrder : 0); self.QuantityBonus = ko.observable(purchaseOrderDetail ? purchaseOrderDetail.QuantityBonus : 0); self.AX_INVENTSUMs = ko.observableArray(items); }; var PurchaseOrderCollection = function () { var self = this; self.purchaseOrder = ko.observable(new PurchaseOrder()); self.purchaseOrderDetails = ko.observableArray([new PurchaseOrderDetail()]); self.CashedArray = ko.observableArray([]); $.getJSON("/AX_INVENTSUM/GetAX_INVENTSUMs", null, function (data) { var array = []; $.each(data, function (index, value) { array.push(value); }); self.CashedArray(array); }); self.addItem = function () { self.purchaseOrderDetails.push(new PurchaseOrderDetail(null, self.CashedArray)); }; self.removeItem = function (purchaseOrderDetail) { self.purchaseOrderDetails.remove(purchaseOrderDetail); }; }; ko.applyBindings(new PurchaseOrderCollection()); }); </script> } As you can see in the code above, how to make this occurs only once a time?
You must cache your list somewhere. I prefer to do it in something like parent view model. See bellow. var OrderList = function(){ var self = this; ... self.CashedArray = ko.observableArray(new Array()); $.getJSON("/AX_INVENTSUM/GetAX_INVENTSUMs", null, function (data) { var array = []; $.each(data, function (index, value) { array.push(value); }); self.CashedArray(array); }); self.AddButtonClick = function (){ var orderDetails = new PurchaseOrderDetail(self.CashedArray()); }; }; var PurchaseOrderDetail = function (items) { var self = this; ... self.AX_INVENTSUMs = ko.observableArray(items); };