I have got a task to do knockout.js.But I can't create the textbox value as json object.
I have a model student with fields name and age.For creating new student i can't set the value as json object.
newlist.html.erb
<script>
$(document).ready(function() {
var viewModel = {
firstName: ko.observable(),
_age: ko.observable(),
validationMessage: ko.observable()
};
var self = this;
self.save = function() {
var dataToSave =firstName: ko.observable();
_age: ko.observable();
alert("Could now send this to server: " + JSON.stringify(viewModel));
}
viewModel.Age = ko.dependentObservable({
read: viewModel._age,
write: function (value) {
if (!isNaN(value)) {
this._age(value);
this.validationMessage("");
}
else {
this.validationMessage("Age must be a number!");
}
},
owner: viewModel
});
ko.applyBindings(viewModel);
});
</script>
<h1>Testing</h1>
Name: <input type="text" data-bind="
value: firstName,
valueUpdate : 'afterkeydown'
"/>
<br />
Age: <input type="text" data-bind="value: Age, valueUpdate : 'afterkeydown'" />
<Br />
<span data-bind="text: validationMessage" style="color:Red"></span>
<Br />
<button data-bind='click: save'>Submit</button>
<Br />
But it shows some error. How can I create a json object?
You can convert model to json in next way
var dataToSave = {
firstName: viewModel.firstName(),
_age: viewModel._age()
}
here you task is resolved: Solution
Related
I'm trying to bind focusout event to my knockout js. here is the example:
<div class="form">
<label>
Country:
</label>
<input type="text" id="countryName" name="countryId._autocomplete" data-bind="value: countryName,event: { blur: onBlurCountryEvent }" />
</div>
<div class="form" data-bind="visible: onBlurCountryEvent">
<label>
Time zone:
</label>
<input type="text" id="timeZoneName" name="timeZoneId._autocomplete" data-bind="value: timeZoneName" />
</div>
and this is my knockoutjs:
define(['viewmodels/shell', 'durandal/system', 'durandal/services/logger', 'plugins/router', 'knockout', 'common', 'jqueryform', 'toastr', 'kovalidationconfig'],
function (shell, system, logger, router, ko, common, jqueryform, toastr, kvc) {
var vm = {
activate: activate,
logger: logger,
shell: shell,
countryId: ko.observable(),
countryName: ko.observable(),
timeZoneName: ko.observable(),
timeZoneId: ko.observable(),
timeZoneVisibility: timeZoneVisibility,
bindingComplete: function (view) {
bindFindCountryEvent(view);
bindFindTimeZoneEvent(view);
}
};
vm.onBlurCountryEvent = function () {
var countryVal = $('#countryName').val();
if (countryVal != undefined && countryVal != null && countryVal != '') {
console.log("trueee");
return true;
}
else {
console.log("falseee");
return false;
}
}
function bindFindCountryEvent(view) {
jQuery("#countryName", view).typeahead(
...
}
function bindFindTimeZoneEvent(view) {
jQuery("#timeZoneName", view).typeahead(
...
}
function activate(id) {
shell.isLoading(true);
...
shell.isLoading(false);
});
return true;
}
vm.save = function () {
...
};
});
So, as you can see, I want to have some event and binded function, when I do onBlur from my field country, to check, and to preview timezone field if there any selected country from dropdown search.
Also, if user skips the country, timezone filed should remain visible:false
the event works, and I can see in my console true/false values.
However, my field of timeZone is intact. No matter if this country field is empty or non-empty, the fields is visible always.
If I put visible:false (hardcoded value), it works.
Should I need to bind that function vm.onBlurCountryEvent?
the problem is that the function onBlurCountryEvent is not an observable, so knockout is not checking for changes. I would suggest adding a isTimezoneVisible : ko.observable(false) to your view model then set the isTimeZoneVisible in the onBlurCountryEvent.
In your view set the visible binding to isTimeZoneVisible. Something like the following
var vm = {
countryId: ko.observable(),
countryName: ko.observable(),
timeZoneName: ko.observable(),
timeZoneId: ko.observable(),
isTimeZoneVisible: ko.observable(false), //new property
bindingComplete: function(view) {
bindFindCountryEvent(view);
bindFindTimeZoneEvent(view);
}
};
vm.onBlurCountryEvent = function() {
var countryVal = $('#countryName').val();
if (countryVal != undefined && countryVal != null && countryVal != '') {
console.log("trueee");
vm.isTimeZoneVisible(true); //set property
} else {
console.log("falseee");
vm.isTimeZoneVisible(false); //set property
}
}
function bindFindCountryEvent(view) {
}
function bindFindTimeZoneEvent(view) {
}
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="form">
<label>
Country:
</label>
<input type="text" id="countryName" name="countryId._autocomplete" data-bind="value: countryName,event: { blur: onBlurCountryEvent }" />
</div>
<div class="form" data-bind="visible: isTimeZoneVisible">
<label>
Time zone:
</label>
<input type="text" id="timeZoneName" name="timeZoneId._autocomplete" data-bind="value: timeZoneName" />
</div>
I'm new with Knockout.js, and I would like to check if a field of my form has a specific value. Actually, I only check if it is required or not. What should I do?
Here's what I have in my html page:
<div data-bind="visible: !Vm.isValid()" class="text-danger">Fill each field to send data, otherwise show this message</div>
<input data-bind="enable: Vm.isValid()" type="button" value="Send data!" />
That's what my vm.js file looks like:
window.Vm = ko.validatedObservable({
name : ko.observable().extend({ required: true })
});
I would make something like this, but I don't know how to do it:
var found = "found";
window.Vm = ko.validatedObservable({
name: ko.observable().extend({
required: true,
function: {
if (this.val() == found)
return true; // invalid value, can't submit my form
}
})
});
I would actually recommend against using the Knockout Validation library, as it hasn't been maintained for years. It's an outdated solution to a problem that doesn't really exist anymore. In 2019 you can just use the form validation that is native to every modern browser. Just put a required attribute on your form fields and the form will not submit if not all required fields have been filled out.
If you want it to be a little more dynamic, you could do something like this:
function ViewModel() {
var vm = this;
vm.name = ko.observable();
vm.required = ['name', 'email'];
vm.isRequired = isRequired;
function isRequired(field) {
return vm.required.indexOf(field) > -1;
}
}
And use the attr binding to set the required attribute based on the array of required elements in your viewmodel.
<input type="text" data-bind="textInput: name, attr: { required: isRequired('name') }">
You can use a custom validator like this (Documentation):
var found = "found";
var Vm = ko.validatedObservable({
name: ko.observable().extend({
required: {
message: "This is a required field",
},
validation: {
validator: (val, paramValue) => {
// "val" has the value entered in the field
// "paramValue" has the value set in "params"
return val === paramValue
},
message: "The value is not " + found,
params: found
}
})
});
ko.applyBindings(Vm)
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
<input type="text" data-bind="value: name" />
I have taken data as ["A","B"], and search based upon the same data.
ko.extenders.required = function(target, overrideMessage) {
//add some sub-observables to our observable
target.hasError = ko.observable();
target.validationMessage = ko.observable();
target.data = ko.observableArray(["A","B"]);
target.found = ko.observable();
target.foundMessage = ko.observable();
//define a function to do validation
function validate(newValue) {
target.hasError(newValue ? false : true);
target.validationMessage(newValue ? "" : overrideMessage || "This field is required");
target.found(target.data().find(function(element){ return newValue==element;}));
target.found()?target.foundMessage("element has found"):target.foundMessage("element has not found");
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
function AppViewModel(first) {
this.firstName = ko.observable(first).extend({ required: "" });
}
ko.applyBindings(new AppViewModel("C"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.3.0/knockout-min.js"></script>
<p data-bind="css: { error: firstName.hasError }">
<input data-bind='value: firstName, valueUpdate: "afterkeydown"' />
<span data-bind='visible: firstName.hasError, text: firstName.validationMessage'> </span>
<span data-bind='visible: (!firstName.hasError()), text: firstName.foundMessage'> </span>
</p>
I have the following JSON complex object.
{"User":
{
"$id":"2",
"Id":0,
"FirstName":null,
"LastName":null,
"Email":null,
"EmailConfirmed":false,
"PasswordHash":null,
}
}
How to bind this object in knockout js. So far I have used somethind like this to bind property with input field.
<input required class="form-control" data-bind="value: User.FirstName" type="text" />
Functions bo build model in knockout.
function userModel() {
var self = this;
self.User = ko.observable();
}
function bindData(data) {
userInfo.User(data["User"]);
}
When I call submiting via JS.
var jsonData = ko.toJSON(userInfo);
Object jsonData show that property like FirstName is null, however in formular value has been written.
Object userInfo stores written values in formular, I have checked it.
Should it look like this?
function userModel() {
var self = this;
self.Password = ko.observable();
self.User = ko.observable();
}
function UserViewModel(user) {
this.FirstName = ko.observable(user.FirstName);
this.LastName = ko.observable(user.LastName);
// other properties
}
function bindData(data) {
userInfo.Password(data["Password"]);
userInfo.User(new UserViewModel(data["User"]));
}
$(document).ready(function () {
userInfo = new userModel();
createUser();
ko.applyBindings(userInfo);
});
For two way binding to work, you need to build the same hierarchy of observable values on the view model.
Alternatively, you could use the mapping plugin:
Since User is also a observable, you have to update your binding like so:
<input required class="form-control" data-bind="value: User().FirstName" type="text" />
Since User has a lot of properties, you could benefit from the with binding:
Here's a fiddle with both examples (with and without the parent binding)
var data = {
"User": {
"$id": "2",
"Id": 0,
"FirstName": "Joseph",
"LastName": "Campbell",
"Email": null,
"EmailConfirmed": false,
"PasswordHash": null,
}
}
function UserViewModel(user) {
this.FirstName = ko.observable(user.FirstName);
this.LastName = ko.observable(user.LastName);
// other properties
}
function bindData(data) {
userInfo.User(new UserViewModel(data["User"]));
}
function userModel() {
var self = this;
self.User = ko.observable();
}
var userInfo = new userModel();
bindData(data);
ko.applyBindings(userInfo);
input {
display: block;
margin: 5px 0;
}
input[readonly] {
border: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form data-bind="with: User">
<input type="text" data-bind="value: FirstName" />
<input type="text" data-bind="value: LastName" />
</form>
Current values:
<input type="text" readonly data-bind="value: User().FirstName" />
<input type="text" readonly data-bind="value: User().LastName" />
I've a page with two input fields. I've a JS object (info) containing 'reference' and a 'value' field of each item. For each item, there's a corresponding 'input' field with matched by 'class' attribute. When a user updates a matching input field, I want to add it's 'value' in the info object.
The problem I'm experiencing is that it's putting the value in the last item in the array (location.value) for either input.
Can anyone help me with where I'm going wrong please? (I could see solutions using 'each' where data for all inputs needs to be added to an array/object. I'm stuck on getting the data for matched fields.)
$(document).ready(function() {
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
for (var p in info) {
if (info.hasOwnProperty(p)) {
$('input.' + p).focusout(function() {
var val = $(this).val();
info[p].value = val; // Only setting value on the last item in array!
//console.log(p); /// p = always location!
out();
})
}
}
function out() {
console.log(info);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
Your issue is because p will be location when the loop ends. Therefore all the click handlers will update the location object, regardless of which element they are attached to.
To fix this you can use a closure to retain the scope of the p at the point the event handler logic was created. Also note that hasOwnProperty() is moot when you're looping through the properties of the object; it has to exist for the iteration to happen. Try this:
$(document).ready(function() {
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
for (var p in info) {
(function(p) {
$('input.' + p).focusout(function() {
var val = $(this).val();
info[p].value = val;
out();
})
})(p);
}
function out() {
console.log(info);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
Alternatively, you can avoid the loop and associated closure by using the class on the input to retrieve the required object:
$(document).ready(function() {
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
$('input').focusout(function() {
var className = $(this).attr('class');
if (info.hasOwnProperty(className))
info[className].value = this.value;
out();
});
function out() {
console.log(info);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
You can simply use the class attribute to update the object by key:
$(document).ready(function(){
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
$('input').focusout(function() {
var className = $(this).attr('class');
var inputValue = $(this).val();
if (info.hasOwnProperty(className)) {
info[className].value = inputValue;
} else {
// If you want to add unknown inputs to the object
info[className] = {ref: "", value: inputValue};
}
console.log(info);
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
<input type="text" class="not_exists" value="" />
You need something more like:
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<script>
$(document).ready(function(){
var info = {
name: {
ref: "a2350",
value: ""
},
location: {
ref: "a2351",
value: ""
}
};
for (var p in info)
{
if ( info.hasOwnProperty(p) )
{
doSomethingWith(p);
}
}
function doSomethingWith(p)
{
$('input.' + p).focusout(function()
{
var val = $(this).val();
info[p].value = val;
console.log(p); /// p = the class of the input now.
});
}
});
</script>
<input type="text" class="name" />
<input type="text" class="location" value="" />
I am new to knockout.JS. I am binding a JSON collection to a table.
<tbody data-bind="foreach: Collection">
<tr>
<td>
<span data-bind="text: FirstName" ></span>
</td>
<td>
<span data-bind="text: LastName" ></span>
</td>
<td>
<input type="button" data-bind="click: function(){ obj.RemoveItem($data) }" value="Del" />
</td>
<td>
<input type="button" data-bind="click: function(){ obj.SaveItems($data.Id) }" value="Edit/Save" />
</td>
</tr>
</tbody>
function viewModel(collection) {
var self = this;
this.Collection = ko.observableArray(collection);
this.AddItem = function() {
this.Collection.push({FirstName:"", LastName:""});
};
this.RemoveItem = function(data) {
this.Collection.remove(data);
};
this.SaveItems = function(id) {
alert("New Collection: " + ko.utils.stringifyJson(self.Collection));
}
};
var obj = new viewModel([
{ Id: 1,FirstName: "John", LastName: "Saleth" },
{ Id: 2, FirstName: "John", LastName: "Kennedy" }
]);
ko.applyBindings(obj);
In eachrow, I kept a edit button which on click, inserts a textbox in all TD's with value of
span. And on save click, i am updating the values of span elements with the values of textbox.
The problem is new values of span element is not reflecting in JSON collection. How to update the JSON source with updated span values on click of save button?
You should make each property of the Collection array observable as well.
function ItemModel(item) {
this.Id = ko.observable(item.Id);
this.FirstName = ko.observable(item.FirstName);
this.LastName = ko.observable(item.LastName);
};
function viewModel(collection) {
var self = this;
this.Collection = ko.observableArray();
ko.utils.arrayForEach(collection, function (i) { self.Collection.push(new ItemModel(i)); });
this.AddItem = function() {
this.Collection.push(new ItemModel({
FirstName: "",
LastName: ""
}));
};
...
};