Focus input added dynamically to observableArray in knockout - javascript

In KO 3.0.0.beta (and I am almost sure it'd be the same in 2.3) I am trying to add new row to a dynamically created table:
HTML
<div id="root">
<table>
<tbody data-bind="foreach: events">
<tr>
<td>
<input type="text" data-type="name" data-bind="value: name, css: {hidden: !editing()}, hasFocus: true">
</td>
<td>
<input type="text" data-type="method" data-bind="value: age, css: {hidden: !editing()}">
</td>
</tr>
</tbody>
</table>
</div>
JavaScript
var $root = $('#root');
$root.on('blur', 'table input', function(event) {
var data = ko.dataFor(event.target),
context = ko.contextFor(event.target),
$input = $(event.target),
newData;
data.editing(false);
if($input.closest('td').is(':last-child') && $input.closest('tr').is(':last-child')) {
newData = {
name: '',
age: '',
editing: ko.observable(false)
};
context.$root.events.push(newData);
newData.editing(true);
}
});
var data = {
events: [
{name: 'aaa', age: '22', editing: false},
{name: 'bbb', age: '33', editing: false},
{name: 'ccc', age: '44', editing: false}
]
};
var mapping = {
events: {
key: function(data) {
return ko.unwrap(data.name);
}
}
};
var observable = ko.mapping.fromJS(data, mapping);
ko.applyBindings(observable, $root[0]);
JSFiddle
and it almost works.
I am successful with row creation - which was the siple part, but for the life of me I can't make the first input in the created raw to be focused.
Any ideas (and I went through a ton of suggestions, none of which worked in the above setting)?

I might be missing the point, but why not just set your hasFocus to editing() instead of true?
<div id="root">
<table>
<tbody data-bind="foreach: events">
<tr>
<td>
<input type="text" data-type="name" data-bind="value: name, css: {hidden: !editing()}, hasFocus: editing()">
</td>
<td>
<input type="text" data-type="method" data-bind="value: age, css: {hidden: !editing()}">
</td>
</tr>
</tbody>
</table>
</div>
Fiddle here

It often pays of to avoid mixing jQuery with Knockout like that. For most cases Knockout also has options. In your case, the ones to look at are the event binding and the hasFocus binding.
Looking at your code, I'd update the View to something like this:
<tr>
<td>
<input type="text" data-bind="value: name,
hasFocus: editingName">
</td>
<td>
<input type="text" data-bind="value: age,
hasFocus: editingAge,
event: { blur: $root.onBlurAgeEdit }">
</td>
</tr>
As an aside, if you're after "MS Word Style" creation of new rows when a user presses tab, you can also bind to the "keypress" event and check for the correct keycode (for tab).
Notice that I've split the editing observable in two observables: editingName and editingAge.
The function onBlurAgeEdit will fire whenever the age input blurs. It looks like this:
self.onBlurAgeEdit = function (item) {
// Unwrap the array
var myEvents = self.events();
// Check if this function was called for the *last* item in the array
if (myEvents[myEvents.length-1] === item) {
// Make sure the last item isn't in edit mode anymore
item.editingAge(false);
// Push a new item that has *name* in edit mode
self.events.push({name: ko.observable(''),
age: ko.observable(''),
editingName: ko.observable(true),
editingAge: ko.observable(false)});
}
};
Note that for Firefox you'll need to throttle the observables to make the hasFocus binding play nice.
See all this at work in this fiddle.

Related

In AngularJS how to uncheck a radio button to make all objects become false

I have an array with many "contact" objects inside. Only one contact can be the primary contact (primary: true).
I also have a radio button to select which contact is the primary.
Q: How can I make one contact primary and deactivate all of the others (primary: false)? so only one object have the property (primary: true) and the rest false?
My example: http://plnkr.co/edit/Y3as4SXv2ZGQSF39W8O6?p=preview
.controller('ExampleController', ['$scope',
function($scope) {
$scope.addContList = [
{
email: "q#q.com",
jobTitle: "clerk",
name: "nico2",
phone: "1",
primary: true
},
{
email: "a#a.com",
jobTitle: "director",
name: "david",
phone: "1",
primary: false
}
];
$scope.$watch('addContList', function() {
console.log('changed', JSON.stringify($scope.addContList, null, 2))
}, true)
}
]);
Here is the view
<tr ng-repeat="contact in addContList">
<td>
<label class="radio-inline">
<input type="radio" value="" name="ui_cont" ng-model="contact.primary" ng-value="true">
</label>
</td>
<td>{{ contact.name }} value = {{contact.primary}} </td>
<td>Edit</td>
<td>Delete</td>
</tr>
You would want to add an ngChange event to your input and change all other inputs to false when one gets set to true. I have updated your Plnkr here: http://plnkr.co/edit/7gxI7if9nC7hAMQES1eu?p=preview
<input type="radio" value="" name="ui_cont" ng-change='changeOnPrimary(contact)' ng-model="contact.primary" ng-value="true">
Then in your controller:
$scope.changeOnPrimary = function(selectedContact) {
// iterate over your whole list
angular.forEach($scope.addContList, function(contact) {
// set primary to false for all contacts excepts selected
if (selectedContact.name !== contact.name) {
contact.primary = false;
}
});
}
Please note: the only reason I'm comparing the name field of the object is because there is no unique identifier to compare with. In real code, you would want to compare against an ID rather than a name field.
You can define a new scope property
$scope.primary = null
Then you can define a listener
$scope.$watch("primary", function(value) {
$scope.addContList.forEach(function(contact) {
contact.primary = angular.equals(value, contact);
})
})
and you can define a default value after defining the list
$scope.primary = $scope.addContList[0];
and in the html you change the input line in
<input type="radio" value="" name="ui_cont" ng-model="$parent.primary" ng-value="contact">
You need to use $parent.primary instead of primary, because ng-repeat defines a new child scope.
see http://plnkr.co/edit/5pvatBNwnrJhGzKhOIys?p=preview

Meteor: Editing and saving subdocuments in a collection

I am learning the meteor-framework for it's ability to make realtime inputs and updates easily. Because it's new to me, I'm not quite sure if this is the most efficient way to do things.
I'm trying to create two forms to save some data in the MongoDB. I'm using SimpleSchema.
First there is a simple input field for adding a title to the DB.:
template
<template name="addForm">
<form>
<input type="text" name="text">
<input type="submit" value="add">
</form>
</template>
events.js
Template.addForm.events({
'submit form': function(event){
event.preventDefault();
var title = event.target.text.value;
MongoArticle.insert({
title: title,
slug: title.toLowerCase()
});
event.target.text.value = "";
}
});
collection.js
MongoArticle = new Mongo.Collection('articles');
var subset = new SimpleSchema({
age: {type: String, optional: true},
value: {type: String, optional: true},
info: {type: String, optional: true},
});
MongoArticle.attachSchema(new SimpleSchema({
title: {type: String},
slug: {type: String},
flag: {type: Boolean, optional: true},
elements: {type: [subset], optional: true}
}));
All titles saved in the DB will be displayed in a list:
<template name="list">
<ul>
{{#each art}}
<li>{{title}}</li>
{{/each}}
</ul>
</template>
If you click on the title you will get the detailed form. Here you can add and edit multiple elements, which are saved in a subdocument (elements -> subset).
<template name="Editor">
<table>
<tbody>
{{#each art.elements}}
<tr>
<td><input type="text" value="{{age}}"></td>
<td><input type="text" value="{{value}}"></td>
<td><input type="text" value="{{info}}"></td>
</tr>
{{/each}}
<tr>
<td><input type="text" value=""></td>
<td><input type="text" value=""></td>
<td><input type="text" value=""></td>
</tr>
</tbody>
</table>
</template>
So I'm trying to get all the elements of the selected article to be displayed in a row of input fields. That will allow them to be edited. I want to have realtime saving, so that (if possible) every change is saved without needing to click a button. Is that possible?
The last row should be used to input new data, which should then be added to the Editor.
I tried to do that with a update() on each row and an input() on the last row, but it didn't work for me, because I don't know how to save the row to the elements-field in the DB, which is a subdocument of the articles-collection.
So I read some websites and I found ´aldeed:autoform´ and ´aldeed:collection2´. Would this be helpful for what I am doing?
If you give your inputs names corresponding to your model fields then updating the existing elements boils down to the following snippet:
'change input[name]': function (e, t) {
var articleId = t.data.art._id; // or something similar
var updates = {};
var $target = $(e.target);
var index = $target.closest('tr').index();
var name = $target.attr('name');
updates['elements.' + index + '.' + name] = $target.val();
MongoArticle.update({ _id: articleId }, { $set: updates });
}
You may also consider using different events, e.g. keyup along with some _.debounce().
Inserting a new element is even simpler because you don't need the index. Also you will probably want to hook to different events (maybe button click?) and replace the $set modifier with $push, i.e.
MongoArticle.update({ _id: articleId }, { $push: {
elements: {
age: "",
value: "",
info: ""
}
}});

Get filtered data only on model object as result, angularjs

My code is like this
<body ng-app="myApp" ng-controller="MainCtrl">
<div>Name only
<input ng-model="search.name" />
<br />
<table id="searchObjResults">
<tr>
<th>Name</th>
<th>Phone</th>
</tr>
<tr ng-repeat="friendObj in friends | filter:search:strict | limitTo:1">
<td>{{friendObj.name}}</td>
<td>{{friendObj.phone}}</td>
</tr>
</table>
</div>
<div>
<button type="button" id="btn_submit" ng-click="submitForm()">Get rates</button>
</div>
angular.module('myApp', []).controller('MainCtrl', ['$http', '$scope', function ($http, $scope) {
$scope.friends = [{
name: 'John',
phone: '555-1276'
}, {
name: 'Mary',
phone: '800-BIG-MARY'
}, {
name: 'Mike',
phone: '555-4321'
}, {
name: 'Adam',
phone: '555-5678'
}, {
name: 'Julie',
phone: '555-8765'
}, {
name: 'Juliette',
phone: '555-5678'
}];
$scope.submitForm = function () {
// i want to get the data here
};
}]);
As you can see at a time only one friend will be active on my screen. when I press my submit button, I want that data (filtered single row) to be the only value on my current $scope.friends so that I can send it to an external service as the selected data. Can any one point out what i need to do here
Fiddle
Note: I can't change the position of this button.
Why not make your button part of the table row, since there will only ever be one? Here is a JSFiddle showing it working in that fashion.
The ng-click function handler for the button can then simply take a parameter that is the actual friendObj you are interested in:
<button type="button" ng-click="submitForm( friendObj )">Get rates</button>
EDIT: There is actually a way to do this if you can't move the button; make the ng-repeat operate over a NEW array, which will be accessible outside of the ng-repeat. So your ng-repeat statement becomes:
<tr ng-repeat="friendObj in newArray = (friends | filter:search:strict | limitTo:1)">
And then your button can simply reference the one-element array:
<button type="button" ng-click="submitForm( newArray )">Get rates</button>
Updated Fiddle here :-)
Try this:
$scope.submitForm = function () {
var data = $filter('filter')($scope.friends, $scope.search.name);
};
Fiddle here.
If you put the filter in the controller instead of the view, you could set a variable like $scope.result that the submitForm function could use. For example, in your HTML, you could add an ng-change directive to your search field like so:
<input ng-model="search.name" ng-change="updateResult()" />
Then, instead of using ng-repeat, you'd use ng-show to show the one result, or hide the row if there is no result:
<tr ng-show="result">
<td>{{result.name}}</td>
<td>{{result.phone}}</td>
</tr>
Then in your controller:
$scope.search = {name: ''};
$scope.updateResult = function() {
$scope.result = $filter('filter')($scope.friends, $scope.search.name)[0];
}
$scope.updateResult();
// ...
$scope.submitForm = function() {
// $scope.result can be used here
}
EDIT: The advantage of this approach is it's a bit DRYer because you don't re-filter inside submitForm. MarcoS's approach has the advantage of being a lot shorter!

Manipulating array data to actually save, write, and change the data content

I am new to AngularJS and so far I'm loving it but I am having a hard time manipulating my data with it. I have an array of data with the attributes name:'', description:'', type:'', ... etc ... and I have enough data but not enough yet for me to upload it onto a server. My problem is that I want to be able to change and update my data using a form or input.
Here is my scripts/admin.js where I implement function submitTheForm() that I call with the submit button.
angular.module('myApp')
//filter to get a specific $scope.campaigns using its id
.filter('getById', function() {
return function(input, id) {
var i=0, len=input.length;
// alert(input.length);
for (; i<len; i++) {
if (+input[i].id === +id) {
return input[i];
}
}
return input[0];
};
})
.controller('AdminCtrl', ['$scope', '$filter', function($scope, $filter) {
//<--ARRAY OF DATA with multiple attributes<--
$scope.campaigns = [
{ name:'', description'', etc... etc...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
];
$scope.selectCampaign = function(object) {
$scope.selectedCampaign = object;
};
$scope.submitTheForm = function(item, event) {
if (confirm("Are you sure you want to edit?") == true) {
alert("--> Submitting form");
var dataObject = {
name : $scope.selectedCampaign.name, description: $scope.selectedCampaign.description, type: $scope.selectedCampaign.type, imgSrc: $scope.selectedCampaign.imgSrc, brand: $scope.selectedCampaign.brand, region: $scope.selectedCampaign.region, location: $scope.selectedCampaign.location, contact: $scope.selectedCampaign.contact, url: $scope.selectedCampaign.url, id: $scope.selectedCampaign.id
};
console.log(dataObject);
var campaign = $scope.selectedCampaign;
var id = campaign.id;
var found = $filter('getById')($scope.campaigns, id);
// setTimeout(function(){ $scope.$apply($scope.selectedCampaign = dataObject); });
}
};
}]);
Here is my main.html where I have my input and submit button
<div class="row modalDetail">
<div class="col-xs-12 col-sm-6 col-md-6 detailLeft text-left">
<div class="middle-allign">
<h1 class="detailName">
<input type="text" ng-model="selectedCampaign.name" name="name">
</h1>
<div class="detailDescription">
<textarea rows="5" cols="71" name="description" ng-model="selectedCampaign.description"></textarea>
</div>
<table class="detailTable table">
<tbody>
<tr>
<td class="bolder">Brand</td>
<td>
<input type="text" ng-model="selectedCampaign.brand" name="brand" >
</td>
</tr>
<tr>
<td class="bolder">Campaign Type</td>
<td>
<input type="text" ng-model="selectedCampaign.type" name="type">
</td>
</tr><tr>
<td class="bolder">Region</td>
<td>
<input type="text" ng-model="selectedCampaign.region" name="region">
</td>
</tr>
<tr>
<td class="bolder">Contact</td>
<td>
<input type="text" ng-model="selectedCampaign.contact" name="contact">
</td>
</tr>
<tr>
<td class="bolder">Location</td>
<td>
<input type="text" ng-model="selectedCampaign.location" name="location">
</td>
</tr>
<tr>
<td class="bolder">URL</td>
<td>
<input type="text" ng-model="selectedCampaign.url" name="url">
</td>
</tr>
</tbody>
</table>
<div class="detailCta">
<button class="btn detailButton" ng-click="submitTheForm()">Submit Campaign</button>
</div>
</div>
</div>
I am trying to utilize 'ng-model' to bind the data and it all works fine but it does not actually change the array content within my main.html. When I refresh it all just goes back to how my array content is. This is because I haven't actually over-written my array content. How can I go about making a absolute change/over-write to the actual object within the array content?
I feel as though it is as simple as $scope.campaigns.push(campaign); except instead of 'push' it would be 'update' or 'over-write'
If you want to store the values in the server, you should see ngResource to create entry points to get and save (and delete) data. Alternatively, you can use the lower level service $http.
If you want to store the data in your current script (it will last until the page is refreshed in the browser - F5), you should do some changes:
.controller('AdminCtrl', ['$scope', '$filter', '$rootScope', function($scope, $filter, $rootScope) {
//<--ARRAY OF DATA with multiple attributes<--
if ($rootScope.campaigns === undefined) {
$rootScope.campaigns = [
{ name:'', description'', etc... etc...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
{...Lots of Data...},
];
}
$scope.campaigns = $rootScope.campaigns;
//don't even think this above line is needed, since $scope inherits from $rootScope, but I've put it for clarity.
$scope.selectCampaign = function(object) {
$scope.selectedCampaign = object;
};
$scope.submitTheForm = function(item, event) {
if (confirm("Are you sure you want to edit?") == true) {
alert("--> Submitting form");
var dataObject = {
name : $scope.selectedCampaign.name, description: $scope.selectedCampaign.description, type: $scope.selectedCampaign.type, imgSrc: $scope.selectedCampaign.imgSrc, brand: $scope.selectedCampaign.brand, region: $scope.selectedCampaign.region, location: $scope.selectedCampaign.location, contact: $scope.selectedCampaign.contact, url: $scope.selectedCampaign.url, id: $scope.selectedCampaign.id
};
console.log(dataObject);
var campaign = $scope.selectedCampaign;
var id = campaign.id;
var found = $filter('getById')($scope.campaigns, id);
}
};
}]);
Notice how I bind it to $rootScope.campaigns - so if you navigate to another ng-view and then come back, the data is still here.
However, if you want your data survive the F5, you must either use the server side options I gave you. or use Local Storage.

Update UI after ajax response

I am new to knocokout.js so i have a table in which data is bind using ajax call.When user click on edit button row information is fill to a form which is on same page below the table.
after ajax call which update the data into database successfully , i am not able to show the changed value of particular object which is changed into table. If i refresh then its show new value .
Here is my html and js code .
<div id="body">
<h2>
Knockout CRUD Operations with ASP.Net Form App</h2>
<h3>
List of Products</h3>
<table id="products1">
<thead>
<tr>
<th>
ID
</th>
<th>
Name
</th>
<th>
Category
</th>
<th>
Price
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody data-bind="foreach: Products">
<tr>
<td data-bind="text: Id">
</td>
<td data-bind="text: Name">
</td>
<td data-bind="text: Category">
</td>
<td data-bind="text: formatCurrency(Price)">
</td>
<td>
<button data-bind="click: $root.edit">
Edit</button>
<button data-bind="click: $root.delete">
Delete</button>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
</td>
<td>
</td>
<td>
Total :
</td>
<td data-bind="text: formatCurrency($root.Total())">
</td>
<td>
</td>
</tr>
</tfoot>
</table>
<br />
<div style="border-top: solid 2px #282828; width: 430px; height: 10px">
</div>
<div data-bind="if: Product">
<div>
<h2>
Update Product</h2>
</div>
<div>
<label for="productId" data-bind="visible: false">
ID</label>
<label data-bind="text: Product().Id, visible: false">
</label>
</div>
<div>
<label for="name">
Name</label>
<input data-bind="value: Product().Name" type="text" title="Name" />
</div>
<div>
<label for="category">
Category</label>
<input data-bind="value: Product().Category" type="text" title="Category" />
</div>
<div>
<label for="price">
Price</label>
<input data-bind="value: Product().Price" type="text" title="Price" />
</div>
<br />
<div>
<button data-bind="click: $root.update">
Update</button>
<button data-bind="click: $root.cancel">
Cancel</button>
</div>
</div>
</div>
Code
function formatCurrency(value) {
return "$" + parseFloat(value).toFixed(2);
}
function ProductViewModel() {
//Make the self as 'this' reference
var self = this;
//Declare observable which will be bind with UI
self.Id = ko.observable("");
self.Name = ko.observable("");
self.Price = ko.observable("");
self.Category = ko.observable("");
var Product = {
Id: self.Id,
Name: self.Name,
Price: self.Price,
Category: self.Category
};
self.Product = ko.observable();
self.Products = ko.observableArray(); // Contains the list of products
// Initialize the view-model
$.ajax({
url: 'SProduct.aspx/GetAllProducts',
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
// debugger;
$.each(data.d, function (index, prd) {
self.Products.push(prd);
})
//Put the response in ObservableArray
}
});
// Calculate Total of Price After Initialization
self.Total = ko.computed(function () {
var sum = 0;
var arr = self.Products();
for (var i = 0; i < arr.length; i++) {
sum += arr[i].Price;
}
return sum;
});
// Edit product details
self.edit = function (Product) {
self.Product(Product);
}
// Update product details
self.update = function () {
var Product = self.Product();
$.ajax({
url: 'SProduct.aspx/Update',
cache: false,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: "{Product:" + ko.toJSON(Product) + "}",
success: function (data) {
console.log(data.d);
self.Product(null);
alert("Record Updated Successfully");
},
error: function (data) {
console.log(data);
}
})
}
// Cancel product details
self.cancel = function () {
self.Product(null);
}
}
$(document).ready(function () {
var viewModel = new ProductViewModel();
ko.applyBindings(viewModel);
});
and my webmethod which called by ajax request is as follow :
// to update product
[WebMethod]
public static testModel.Product Update(testModel.Product Product)
{
testEntities db = new testEntities();
var obj = db.Products.First(o => o.Id == Product.Id);
obj.Name = Product.Name;
obj.Price = Product.Price;
obj.Category = Product.Category;
db.SaveChanges();
return obj;
}
and JSON response of ajax call as follow
{"d":{"__type":"testModel.Product","Id":31,"Name":"12","Category":"12","Price":1350,"EntityState":2,"EntityKey":
{"EntitySetName":"Products","EntityContainerName":"testEntities","EntityKeyValues":
[{"Key":"Id","Value":31}],"IsTemporary":false}}}
Here's what's happening. Here: self.Products.push(prd) prd is just a plain javascript object with plain property values, nothing is observable. You're pushing the raw object onto the Products observableArray, which updates the DOM because Products was changed and KO is watching it. When you click 'edit', you set self.Product to that plain object and the KO updates the DOM with this object and its values because Product was changed and KO is watching it. So now your Product form below displays, you see the information, and it looks like you can edit the properties but KO won't update those property changes because KO isn't watching them. They're not observable. Change:
$.each(data.d, function (index, prd) {
//self.Products.push(prd);
self.Products.push({
Id: ko.observable(prd.Id),
Name: ko.observable(prd.Name),
Price: ko.observable(prd.Price),
Category: ko.observable(prd.Category)
});
});
General helpful tips
<div data-bind="if: Product">
This only evaluates once when you bind the viewModel to the DOM with ko.applyBindings. Since self.Product has an initial value of null KO removes this altogether.*Note: I was thinking of #if for some reason.
This works like the visible binding except when the value is false, the element and its children are removed from the DOM. So there is more DOM manipulation going on than necessary. You probably just want to hide this <div>
I would recommend changing this to:
<div data-bind="visible: Product">
Instead of this:
<input type="text" data-bind="text: Product().Name" />
<input type="text" data-bind="text: Product().Category" />
<input type="text" data-bind="text: Product().Price" />
Try this instead:
<div data-bind="with: Product">
<input type="text" data-bind="text: Name" />
<input type="text" data-bind="text: Category" />
<input type="text" data-bind="text: Price" />
</div>
Consider renaming self.Product to self.SelectedProduct to make it more clear what it is for.
I'm not sure what this is doing in the ViewModel:
//Declare observable which will be bind with UI
self.Id = ko.observable("");
self.Name = ko.observable("");
self.Price = ko.observable("");
self.Category = ko.observable("");
var Product = {
Id: self.Id,
Name: self.Name,
Price: self.Price,
Category: self.Category
};
You don't use them in the DOM. You were kind of on the right path with this though. Instead, before the ProductViewModel, create this:
function ProductModel(data) {
var self = this;
data = data || {};
self.Id = ko.observable(data.Id);
self.Name = ko.observable(data.Name);
self.Price = ko.observable(data.Price);
self.Category = ko.observable(data.Category);
}
Now instead of:
$.each(data.d, function (index, prd) {
self.Products.push({
Id: ko.observable(prd.Id),
Name: ko.observable(prd.Name),
Price: ko.observable(prd.Price),
Category: ko.observable(prd.Category)
});
});
We can just do this:
$.each(data.d, function (index, prd) {
self.Products.push(new ProductModel(prd));
});
Hopefully that will get you headed in the right direction.
There is something to change:
Replace
$.each(data.d, function (index, prd) {
self.Products.push(prd);
})
With:
$.each(data.d, function (index, prd) {
self.Products.push({
Id: ko.observable(prd.Id),
Name: ko.observable(prd.Name),
Price: ko.observable(prd.Price),
Category: ko.observable(prd.Category)
});
})
Use ko.observable to make your properties notify the view of its changes so that the view can update accordingly. This should work but not perfect because this is 2 way binding, so whenever you update the values in your div, the view model object is updated immediately and even if your ajax fails to update the data in backend, making the data out of synch between client side and server side.
For better solution. You need to have a look at protectedObservable
$.each(data.d, function (index, prd) {
self.Products.push({
Id: ko.protectedObservable(prd.Id),
Name: ko.protectedObservable(prd.Name),
Price: ko.protectedObservable(prd.Price),
Category: ko.protectedObservable(prd.Category)
});
})
Inside your self.update ajax success function, trigger a change:
success: function (data) {
var product =self.Product();
product.Id.commit();
product.Name.commit();
product.Price.commit();
product.Category.commit();
self.Product(null);
alert("Record Updated Successfully");
}
And revert if there is error:
error: function (data) {
product.Id.reset();
product.Name.reset();
product.Price.reset();
product.Category.reset();
}
Update:
Remember to change all the places with Product.Property to Product.Property() to get its property value . For example: arr[i].Price should be changed to arr[i].Price()
Add self.Products.push(data.d); to the update() functions success handler.
success: function (data) {
console.log(data.d);
self.Product(null);
self.Products.push(data.d);
alert("Record Updated Successfully");
},
You need to update the array so that it reflects in bound html.

Categories

Resources