I am using knockout with bootstrap js.
My requirement :
I am creating an HTML form and I have done data binding using knockout.
After submission of this form, I am showing a message to the user in bootstrap modal.
On closing this modal I need to reset the HTML form. SO on clicking on the close button I and doing a click binding and calling a method. Below is the snippet of that method.
// Re-setting the observable to default value
me.addStudentForClass(null);
me.parents=ko.mapping.fromJS(new parentInfoModel(undefined));
me.localGuardian=ko.mapping.fromJS(new localGuardian(undefined));
me.lastSchoolEducation=ko.mapping.fromJS(new lastSchoolEducation(undefined));
// Closing bootstrap Modal
$('modal-id').modal('hide');
// Cleaning the knockout node , to reflect the reset change
ko.cleanNode($('#add-student')[0]);
// and binding the view model
ko.applyBindings(app.mainViewModel, $('#add-student')[0]);
If we are not using below two lines of code then the modal work fine and modal got closed on clicking the close button
ko.cleanNode($('#add-student')[0]);
ko.applyBindings(app.mainViewModel, $('#add-student')[0]);
But I need to use the above two lines of code because it needs to reset my form.
Please help me with this.
Thank You
It would be helpful to provide the HTML/more context code as well if possible.
I need to use the above two lines of code because it needs to reset my form.
I don't think I have ever used, or needed to use, ko.cleanNode
. In general you should be able to "reset the form" by simply updating the viewmodel the form is bound to - either "resetting" the viewmodel properties that the form fields are bound to, or creating a whole new viewmodel "for the form".
Exactly. I've never encountered a situation that .cleanNode fixed that better application design couldn't. Like fastfasterfastest said: reset the things that need to be reset in the ViewModel and let Knockout reflect that onto the view.
If I had to guess, I would say that the 'modal-id' (without #?) lives somewhere IN the #add-student region and in the middle of the asynchrounous transition to hidden-state the whole region gets re-written by the cleaning and rebinding. Probably calling after hiding would solve your problem:
$('#modal-id').modal('hide').one('hidden.bs.modal', function (e) {
ko.cleanNode($('#add-student')[0]);
ko.applyBindings(app.mainViewModel, $('#add-student')[0]);
})
Except: don't do this. Listen to fastfasterfastest.
Thanks, @karimayachi
Your solution works. Now my problem got solved.
But as per @fastfasterfastest and you , I should not use ko.cleanNode but without this, it is not working.
I have an observable complex object which contains many observable properties.
var presonalModel = function(data){
self=this;
self.fname=ko.observable(data ? data.fname : "");
self.mname=ko.observable(data ? data.mname : "");
self.lname=ko.observable(data ? data.lname : "");
self.dob=ko.observable(data ? data.dob : "");
self.gender=ko.observable(data ? data.gender : "");
self.blood=ko.observable(data ? data.blood : "");
self.phone=ko.observable(data ? data.phone : "");
self.email=ko.observable(data ? data.email : "");
}
var addressModel = function(data){
self=this;
self.line1=ko.observable(data ? data.line1 : "");
self.line2=ko.observable(data ? data.line2 : "");
self.line3=ko.observable(data ? data.line3 : "");
self.pin=ko.observable(data ? data.pin : "");
self.city=ko.observable(data ? data.city : "");
self.state=ko.observable(data ? data.state : "");
}
var addStudentModel = function(data){
var me = {
basic: ko.mapping.fromJS(new presonalModel(data ? data.basic : undefined)),
address: ko.mapping.fromJS(new addressModel(data ? data.address : undefined))
}
}
and in view modal I am doing
app.studentViewModel = (function (ko) {
"use strict";
var _common_= new commonFunctionality();
var me = {
....
student: ko.mapping.fromJS(new addStudentModel(undefined)),
...
//many more other property
}
// this method will be called on form submission
function addStudentApplicationSubmitStatusModelClose() {
// sane the data to database by calling API
$('#add-student-application-submit-status-model').modal('hide');
$("#add-other-info").addClass("d-none");
$("#add-stud-basicInfo").removeClass("d-none");
_resetAddStudent_();
// Resetting the form on event : modal completely hide event
$('#add-student-application-submit-status-model').on('hidden.bs.modal', function (e) {
ko.cleanNode(element[0]);
ko.applyBindings(app.mainViewModel,element[0]);
})
}
function _resetAddStudent_() {
console.log("========OLD==========");
console.log(me.student);//COMMENT 1
// Resetting observable
me.student = ko.mapping.fromJS(new addStudentModel(undefined));
console.log("========NEW==========");
console.log(me.student);//COMMENT 2
me.addStudentForClass(null);
me.parents = ko.mapping.fromJS(new parentInfoModel(undefined));
me.localGuardian = ko.mapping.fromJS(new localGuardian(undefined));
me.lastSchoolEducation = ko.mapping.fromJS(new lastSchoolEducation(undefined));
}
})(ko);
When first time I submit the form then after submission, the form should reset to empty field. But it does not occur but in javascript, in COMMENT 2 I can see that value got reset but it is not reflecting the UI, the UI still hold the old value.
But after using ko.cleanNode, then I got the reset form.
Please let me know if I am doing something wrong . I am new to knockout js. I am doing POC because my coming project is in knockout. I need your help.
Thank You
Can you provide a jsFiddle or something that illustrates your problem? I have to make too many guesses about the binding and views to help you.
Also, general tips: coupling the view and viewmodel by querying and manipulating the DOM from the VM is as bad as design as using .cleanNode()... You should never have to use that. Decouple by providing a basicInfoVisible property (or something like that) and binding to that should take of that. Except maybe for the modal("show"), which you could wrap in a component or custom binding.
If you have a working example (either by jsFiddle or some other method), I can see if I can make it work the clean way...
Hi @karimayachi
I have kept this POC in git.
Its location is: https://github.com/priyank-eschool/eschool-ko-poc
This project will run with node
Command to run : npm start
Project structure .(code is in src folder)
When you will start the project with "npm start" then the index page will open .
index page
Click on main
then it will display the main.html
You need to go to addstudent . Their you will get a multi-page HTML form. After submitting this form the form should reset. For resetting I have used ko.cleanNode($('#add-student')[0]);
I have one more problem in addProfessor tab
It will add a professor. In the second page of the form, their is a question
"Do you have previous experience?" for this we have a radio button. You need to select Yes.
Only after selecting Yes, you will be directed to add experience tab.
You will see a text box and button (This is only for testing purpose , I am testing the length of an observableArray "previousExpe in src\js\viewmodel\professor-view-modal.js")
In text box it is showing the size of "previousExpe " is one but on clicking the button it is showing 2.
I cant able to understand why It is showing two different value. The size is 2 , in text box it is showing wrong value.
In above you can see that I am initializing the value of previousExpe with one object and after that, I am adding a new value in the previousExpe as you can see the below code.
The wrong size of previousExpe is displaying in the text box and I am doing foeach binding on previousExpe to display the form to enter the detail, but I can see only one form.
Thanks in Advance for cleaning my doubt
Hi @priyank-eschool ,
The problem lies in:
me.student = ko.mapping.fromJS(new addStudentModel(undefined));
This doesn't actually _clear_ the observables that the view is bound to. You have a structure which is (semi JSON notation):
viewmodel = {
student: {
basic: {
fname: observable('.....'),
....
}
}
}
Elements in the view are bound to the observables:
<span data-bind="text:studentViewModel.student.basic.fname"></span>
So when you do me.student = ko.mapping.fromJS(new addStudentModel(undefined));
, you are not _resetting_ the fname observable, but you are _replacing_ the whole student property. The original property with it's nested observable (fname) still exists because it (fname) is still being referenced.
If you would in stead explicitly clear the property, it would work:
function _resetAddStudent_() {
me.student.basic.fname('');
...
Of course manually clearing all properties can be suboptimal, but you can do this with .map or ko.mapping, just as long as you are sure you are actually updating observables in stead of replacing object-properties.
Looking at your code I see a lot of problematic things, such as:
All these design decisions lead to needing to use hacks such as cleanNode or re-applying bindings.
As a POC I would have started with a simple example, getting KO's basics right and then extending to add functionality. In stead, you seem to have gone the other way around and start with a full application in which you injected KO. So now you have an application that is build on a fundamentally shaky foundation.
I'm afraid I can't help you further, because I'm not going to rewrite this entire application.. I think you really need to start with the basics and practice with KO until you grasp the concepts in stead of trying to make a full fledged application at once.
I haven't looked into your Professor-problem...
Regards,
Karim
Most helpful comment
I don't think I have ever used, or needed to use,
ko.cleanNode
. In general you should be able to "reset the form" by simply updating the viewmodel the form is bound to - either "resetting" the viewmodel properties that the form fields are bound to, or creating a whole new viewmodel "for the form".