How to bind focusOut event to knockoutjs - javascript

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>

Related

Knockout custom validation: How to check if observable is equal to a specific value?

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>

Utility method in Vue.js

I am trying to create a utility method in Vue.js to validate a decimal number from any input field but I'm not sure how to set the value in Vue.js internally.
This is what I did in jQuery before:
$('body').on('blur', '.decimal', function() {
var val = $(this).val();
if ($.isNumeric(val)) {
val = parseFloat(val).toFixed(2);
$(this).val(val);
} else {
$(this).val('');
}
});
This is what I have in Vue but the value is not stored internally and is overwritten.
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
methods: {
validateDecimal: function (e) {
var val = e.target.value;
if (isNumeric(val)) {
e.target.value = parseFloat(val).toFixed(2);
} else {
e.target.value = '';
}
}
}
HTML
<input class="ui-input" :value="some.value" placeholder="0.00" #blur="validateDecimal">
<input class="ui-input" :value="some.othervalue" placeholder="0.00" #blur="validateDecimal">
<input class="ui-input" :value="another.dynamic.input" placeholder="0.00" #blur="validateDecimal">
Apparently you can pass the data object reference to the handler method like so:
(Note you can't just pass the data property, because I don't believe it will be a reference.)
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
inputs:{
'v1':{
value:'1.256'
},
'v2':{
value:'1.521'
}
},
someInput:{value:'1.7125'}
},
methods:{
validateDecimal: function (o,p,e) {
console.log('validateDecimal',o);
var val = e.target.value;
console.log(val);
if (Number(parseFloat(val)) == val) {
val = parseFloat(val).toFixed(2);
this.$set(o, p, val);
} else {
this.$set(o, p, '');
e.target.value = '';
}
},
foo: function(){
console.log(this.inputs.v1.value)
console.log(this.inputs.v2.value)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="i,key in this.inputs">
<input class="ui-input" :value="i.value" placeholder="0.00" #blur="validateDecimal(i, 'value', $event)">
</div>
<div>
<input class="ui-input" :value="this.someInput.value" placeholder="0.00" #blur="validateDecimal(someInput,'value', $event)">
</div>
<button #click="foo">Click</button>
</div>
Edit by OP: Adding an extra parameter for the name of the property and using $set to make the dynamic property reactive. This should make the method more general purpose for any dynamic input fields with any property name.

Conditional validation in VueJS

I am using VueJS with vue-validator and I have been struggling for hours to do simple conditional validation. The example provided in the documentation does not seem to work, at least not in my case.
What I am trying to accomplish is requiring two input groups (observer_firstName and observer_lastName) if a condition (showObserverEntry) is true and requiring another (role) if it is false.
So, if showObserverEntry is false, role should be required/visible. If showObserverEntry is true, role SHOULD NOT be required or visible, observer_firstName and observer_lastName should be required and visible.
Everything works when the page is loaded and showObserverEntry is set to false, it continues to work when switched to true, but when it goes back to false again validation stops working for role. Peeking at the data output, the validation data changes to validation { } where it initially has data.
Vue instance with other methods removed:
var vm = new Vue({
el: "#scheduleContainer",
validator: {
validates: {
requiredIf: function (val, condition){
return val && condition
}
}
},
data: {
loading: true,
stationId: stationId,
date: initialDate,
dateFormatted: initialDateFormatted,
nextDate: null,
prevDate: null,
entries: [],
requestEntries: [],
roles: [],
roleStaff: [],
showObserverEntry: false,
startPickerDatetime: null,
endPickerDatetime: null,
shiftEntry: {
start: null,
end: null,
role: null,
member: "",
observer: {
firstName: "",
lastName: ""
}
}
},
computed: {
validField: function () {
return this.validation.shiftEntry.observer.firstName.valid &&
this.validation.shiftEntry.observer.lastName.valid
}
},
methods: {
getRoleStaff: function () {
if (this.shiftEntry.role != '' && this.shiftEntry.role != 'observer') {
this.$http.post('/members/schedule/manage/json/roles/staff', {id: this.shiftEntry.role})
.success(function (data) {
this.$set('roleStaff', data.members);
vm.shiftEntry.member = "";
vm.showObserverEntry = false;
vm.shiftEntry.observer.firstName = "";
vm.shiftEntry.observer.lastName = "";
});
} else if (this.shiftEntry.role == 'observer') {
this.showObserverEntry = true;
this.resetFields()
}
else {
this.showObserverEntry = false;
this.roleStaff = [];
}
},
resetFields: function () {
this.roleStaff = [];
this.shiftEntry.role = "";
this.shiftEntry.member = "";
this.shiftEntry.observer.firstName = "";
this.shiftEntry.observer.lastName = "";
},
conditionalField: function (response, type) {
return response === type
}
}
});
Form fields:
<div class="form-group"
v-if="conditionalField(showObserverEntry, false)"
v-class="has-error: validation.shiftEntry.member.invalid">
<label for="member">Member:</label>
<select name="member"
id="member"
v-model="shiftEntry.member"
options="roleStaff"
v-attr="disabled: !roleStaff.length"
class="form-control"
v-validate="requiredIf: conditionalField(showObserverEntry, false)">
<option value="">Select Member</option>
</select>
</div>
<div class="form-group"
v-if="conditionalField(showObserverEntry, true)"
v-class="has-error: validation.shiftEntry.observer.firstName.invalid">
<label for="observer_firstName">First Name:</label>
<input type="text"
id="observer_firstName"
class="form-control"
v-model="shiftEntry.observer.firstName"
v-validate="requiredIf: conditionalField(showObserverEntry, true)">
</div>
<div class="form-group"
v-if="conditionalField(showObserverEntry, true)"
v-class="has-error: validation.shiftEntry.observer.lastName.invalid">
<label for="observer_lastName">Last Name:</label>
<input type="text"
id="observer_lastName"
class="form-control"
v-model="shiftEntry.observer.lastName"
v-validate="requiredIf: conditionalField(showObserverEntry, true)">
</div>
It is because a bug in Vue.js. Reason: If we remove one or more v-model based on certain condition(v-if), then it will make all other validation to deactivate.
Refer the issue :https://github.com/vuejs/vue-validator/issues/69

this.content is not updating with values from template

I have the following template
<script type="text/x-handlebars" id="friends/new">
<label>First Name</label>
{{input value=firstName}}<br />
<label>Last Name</label>
{{input value=lastName}}<br />
<label>About</label>
{{textarea value=about}}<br />
<button {{action "create"}} {{bind-attr disabled=isInvalid}}>Create</button>
</script>
I put data into all the fields and click the create button, which goes to the following controllers
App.FriendsNewRoute = Ember.Route.extend({
model: function(){
return { firstName: "", lastName: "", about: ""}
}
});
App.FriendsNewController = Ember.Controller.extend({
needs: "friends",
isInvalid: true,
validForm: function(){
if(this.get('lastName') && this.get('firstName')){
this.set("isInvalid", false);
} else {
this.set("isInvalid", true);
}
}.observes('firstName','lastName'),
actions: {
create: function(){
var newFriend = Ember.copy(this.content);
console.log(newFriend);
}
}
});
When calling this.get('lastName') ect the content that I have entered into the text boxes is correct. But when I log this.content, the value is still the initial value that I set in the FriendsNewRoute. What do I need to do so that this.content correctly update with the current data in my template?
You should change:
Ember.Controller
to
Ember.ObjectController

How to set json value in knockout js?

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

Categories

Resources