KnockoutJS Required (ifonly) not being honored when observable changes - .net-core

I have 2 fields that I need to required based on another field in my model. The first field is functioning as desired, however the second field (similar logic) doesn't honor the required attribute. I have verified that the onlyif code is firing when the observable is changed. However the form allows submission if the required fields are not filled in.
JS Code
//Works As Expected
self.model.RecentLocations.LastDayOnSite.extend({
required: {
onlyIf: function () {
return ((!self.model.RecentLocations.IsLastDayOnSiteNA()) && (self.model.CaseType() != 'Quarantine'));
}
}
});
//Not Requiring Field as expected.
self.model.ContactTracingStartDate.extend = ko.observable().extend({
required: {
onlyIf: function () {
return (self.model.IsContactTracingRequired() == "Y");
}
}
});
HTML Code
//Works As Expected
<div class="col-md-2 form-group">
<i id="lastDayOnSite-asterisk" class="fas fa-asterisk fa-fw" style="font-size: 7px; color:red; vertical-align:super" data-bind="hidden: (model.RecentLocations.IsLastDayOnSiteNA() || model.CaseType() === 'Quarantine')"></i>
<label for="lastDayOnSite-datepicker_nfd">Last Day on Site</label>
<input type="text" class="form-control datepicker_nfd" id="lastDayOnSite-datepicker_nfd" data-bind="value: model.RecentLocations.LastDayOnSite, preventFutureDate: model.RecentLocations.LastDayOnSite, disable: model.RecentLocations.IsLastDayOnSiteNA()" data-emessage="Last Day on Site" placeholder="Date">
</div>
//Not Requiring Field as expected.
<div class="col-md-2 form-group">
<i id="contactTracingStartDate-asterisk" class="fas fa-asterisk fa-fw" style="font-size: 7px; color:red; vertical-align:super" data-bind="visible: (model.IsContactTracingRequired() === 'Y')"></i>
<label for="contactTracingStartDate-datepicker_nfd">Contact Tracing Start Date</label>
<input type="text" class="form-control datepicker_nfd" id="contactTracingStartDate-datepicker_nfd"
data-bind="value: model.ContactTracingStartDate,
preventFutureDate: model.ContactTracingStartDate, enable: (model.IsContactTracingRequired() === 'Y')" data-emessage="Contract Tracing Start Date" placeholder="Date">
</div>
Not Sure what I am missing here but I am fairly new to KnockoutJS but I can't see where the disconnect is. Any Help or suggestions would be appreciated.

The answer is change the line of code
self.model.ContactTracingStartDate.extend = ko.observable().extend({
TO:
self.model.ContactTracingStartDate.extend({
The problem was the observerable was reset by the ko.observable().extend instead of just extending the existing observable.

Related

$(...).jqBootstrapValidation is not a function at HTMLDocument.<anonymous>

i am trying to implement a contact us page using bootstrap template. I am a really beginner in asp.net and its my first time using a template.
I cant really get it where is the problem that is causing the above error.
I want to when the user click the send Message button to send emails to a current business email.
Can anyone help me?
contact.cshtml
<div class="row">
<div class="col-lg-8 mb-4">
<h3>Send us a Message</h3>
<form name="sentMessage" id="contactForm" novalidate>
<div class="control-group form-group">
<div class="controls">
<label>Full Name:</label>
<input type="text" class="form-control" id="name" required data-validation-required-message="Please enter your name.">
<p class="help-block"></p>
</div>
</div>
<div class="control-group form-group">
<div class="controls">
<label>Phone Number:</label>
<input type="tel" class="form-control" id="phone" required data-validation-required-message="Please enter your phone number.">
</div>
</div>
<div class="control-group form-group">
<div class="controls">
<label>Email Address:</label>
<input type="email" class="form-control" id="email" required data-validation-required-message="Please enter your email address.">
</div>
</div>
<div class="control-group form-group">
<div class="controls">
<label>Message:</label>
<textarea rows="10" cols="100" class="form-control" id="message" required data-validation-required-message="Please enter your message" maxlength="999" style="resize:none"></textarea>
</div>
</div>
<div id="success"></div>
<!-- For success/fail messages -->
<button type="submit" class="btn btn-primary" id="sendMessageButton">Send Message</button>
</form>
</div>
</div>
</div>
<script src="~/Content/vendor/jquery/jquery.min.js"></script>
<script src="~/Scripts/contact_me.js"></script>
<script src="~/Scripts/jqBootstrapValidation.js"></script>
<script src="~/Content/vendor/bootstrap/js/bootstrap.min.js"></script>
contact_me.js
$(function () {
$('#contactForm input,#contactForm textarea').jqBootstrapValidation({
preventSubmit: true,
submitError: function($form, event, errors) {
// additional error messages or events
},
submitSuccess: function($form, event) {
event.preventDefault(); // prevent default submit behaviour
// get values from FORM
var name = $("input#name").val();
var email = $("input#email").val();
var phone = $("input#phone").val();
var message = $("textarea#message").val();
var firstName = name; // For Success/Failure Message
// Check for white space in name for Success/Fail message
if (firstName.indexOf(' ') >= 0) {
firstName = name.split(' ').slice(0, -1).join(' ');
}
$this = $("#sendMessageButton");
$this.prop("disabled", true); // Disable submit button until AJAX call is complete to prevent duplicate messages
$.ajax({
url: "mail/contact_me.php",
type: "POST",
data: {
name: name,
phone: phone,
email: email,
message: message
},
cache: false,
success: function() {
// Success message
$('#success').html("<div class='alert alert-success'>");
$('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
.append("</button>");
$('#success > .alert-success')
.append("<strong>Your message has been sent. </strong>");
$('#success > .alert-success')
.append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
error: function() {
// Fail message
$('#success').html("<div class='alert alert-danger'>");
$('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×")
.append("</button>");
$('#success > .alert-danger').append($("<strong>").text("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!"));
$('#success > .alert-danger').append('</div>');
//clear all fields
$('#contactForm').trigger("reset");
},
complete: function() {
setTimeout(function() {
$this.prop("disabled", false); // Re-enable submit button when AJAX call is complete
}, 1000);
}
});
},
filter: function() {
return $(this).is(":visible");
},
});
$("a[data-toggle=\"tab\"]").click(function(e) {
e.preventDefault();
$(this).tab("show");
});
});
/*When clicking on Full hide fail/success boxes */
$('#name').focus(function() {
$('#success').html('');
});
As my answer morphed through the comments:
the answer was that the OP added Jquery twice making a conflict in
file jqBootstrapValidation.js
Also, to help others based on the OP's post, this next part could be helpful as well:
Javascript files need to be loaded in the order of their dependence. Since your custom javascript file, contact_me.js depends on functions contained in the javascript file, jqBootstrapValidation.js, you need to load jqBootstrapValidation.js before contact_me.js, to fix this switch the order in which these files are loaded:
<script src="~/Content/vendor/bootstrap/js/bootstrap.min.js"></script>
<script src="~/Scripts/jqBootstrapValidation.js"></script>
<script src="~/Content/vendor/jquery/jquery.min.js"></script>
<script src="~/Scripts/contact_me.js"></script>

update ng-invalid when editing double entry fields

i have a password and password confirm field, which is connected using a directive. beside that i have css that set border color when ng-invalid. the issue is that when i for instance enter the confirm_password first and then same in password it does not remove the 'ng-invalid'. is there a way to tell angular to update other fields classes when editing password?
html
<div class="form-group">
<label>Adgangskode</label>
<input type="password" class="form-control" name="password"
ng-model="vm.password" ng-minlength="6" ng-maxlength="24"
placeholder="Din adgangskode"
equals="vm.confirm_password" required>
<p ng-show="SignUp.password.$invalid
&& (SignUp.password.$dirty || vm.submitted)"
class="help-block ng-binding" style="">Adgangskode er invalid.</p>
</div>
<div class="form-group">
<label>Adgangskode bekræftelse</label>
<input type="password" class="form-control" name="confirm_password"
ng-model="vm.confirm_password"
ng-minlength="6" ng-maxlength="24"
ng-model="vm.confirm_password"
placeholder="Bekræft din adgangskode"
required nx-equal="vm.password">
<p ng-show="SignUp.confirm_password.$error.nxEqual
&& (SignUp.confirm_password.$dirty || vm.submitted)"
class="help-block ng-binding">Adgangskoderne er ikke ens.</p>
</div>
css
input.ng-dirty.ng-invalid {
border-color: #a94442;
}
.ng-submitted input.ng-invalid {
border-color: #a94442;
}
directive funciton
function ComparePassword() {
return {
require: 'ngModel',
link: function (scope, elem, attrs, model) {
if (!attrs.nxEqual) {
console.error('nxEqual expects a model as an argument!');
return;
}
scope.$watch(attrs.nxEqual, function (value) {
model.$setValidity('nxEqual', value === model.$viewValue);
});
model.$parsers.push(function (value) {
var isValid = value === scope.$eval(attrs.nxEqual);
model.$setValidity('nxEqual', isValid);
return isValid ? value : undefined;
});
}
}
}
Have the compare directive watch the other field:
app.directve("compareTo", compareTo);
function compareTo() {
return {
require: "ngModel",
link: function(scope, elem, attrs, ngModel) {
ngModel.$validators.compareTo = function(modelValue) {
return modelValue == scope.$eval(attrs.compareTo);
};
scope.$watch(attrs.compareTo, function() {
ngModel.$validate();
});
}
};
}
Usage:
<form name="form1">
<input type="password" name="password" required
ng-model="user.password" />
<input type="password" name="confirmPassword" required
ng-model="user.confirmPassword" compare-to="user.password" />
</form>
<div ng-show="form1.comfirmPassword.$error.compareTo">
Error: Password entries must match
</div>
Think carefully about double entry
Double entry:
increases the workload for every single user;
can be bypassed by copying and pasting, or automatic form-filling tools;
only ensures the two fields match, not that they contain the valid information;
and
may be seen as belittling the user;
Alternatives to double entry are worth serious consideration. These alternatives include authentication and/or simple methods of reset or recovery.
— Formulate Information Design Blog - Double entry of form fields

Meteor: No context for #each loop event handlers

I'm using an #each loop with an unmanaged local collection to generate a sequence of input fields for a form. However, when I try to use this._id in the event handler it is undefined. In fact, the context being passed to the event handler is for the window. Any help to find what is going wrong and how I should be getting the proper context as this within my event handler is much appreciated.
The code is:
<h4 class="page-header">Children on this account</h4>
{{#each children}}
<div id={{_id}} class="form-group col-md-12 child-form-instance">
<div class="form-group col-sm-5 col-xs-12">
<input type="text" name="childFirstName" class="form-control" placeholder="Child's First Name">
</div>
<div class="form-group col-sm-5 col-xs-10">
<input type="text" name="childLastName" class="form-control" placeholder="Child's Last Name" value="{{_id}}">
</div>
<div class="form-group col-xs-2">
<button type="button" class="btn btn-danger remove-child" aria-label="remove child">
<span class="glyphicon glyphicon-trash"></span>
</button>
</div>
</div>
{{/each}}
<div class="form-group">
<input type="button" id="addChild" class="btn btn-success" value="Add child">
</div>
and the js:
var children = new Mongo.Collection(null);
Template.signup.onRendered( () => {
Modules.client.signupValidateSubmit({
form: "#signup",
template: Template.instance()
});
children.remove({});
children.insert({}); //create one empty child
});
Template.signup.events({
'submit form': ( event ) => event.preventDefault(),
'click #addChild': () => {
children.insert({});
console.log(children.find().fetch());
},
'click .remove-child': () => {
console.log(this);
}
});
Template.signup.helpers({
children () {
return children.find();
}
});
Everything is working fine with the addChild button, and the _ids are getting properly assigned in the DOM, but the remove-child class is logging the window context.
You are using ES6 arrow functions which will lexically bind this with the parent scope. As a consequence, you don't get the scope of children.
In order to solve the issue, just change the template event to:
Template.signup.events({
// ...
'click .remove-child': function (event, template) {
console.log(this);
}
// ...
});

Adding errors to form validation doesn't work?

According to the Semantic UI docs on form validation, I can add errors manually:
add errors(errors) | Adds errors to form, given an array errors
(I want to use this feature because I submit my form via AJAX, do server-side validation, then want to display the results.)
I tried the following code:
$('#my-form').form("add errors", [ 'error' ]);
$('#my-form').form("validate form");
I get this contradictory output from the console when calling the above methods, and the form validates as successful when it obviously shouldn't.
Any idea?
To perform server-side validation via AJAX, use a custom rule:
$myForm = $('.ui.form');
var settings = {
rules: {
custom: function () {
// Perform AJAX validation here
return false;
}
}
};
var rules = {
ajaxField: {
identifier: 'ajaxField',
rules: [{
type: 'custom',
prompt: 'Custom error!'
}]
}
};
$myForm.form(rules, settings);
Here it is in action: http://jsbin.com/sufiwafe/1/edit
For how to use callbacks and form validation in general, there is an important discussion on the Semantic-UI issues page on GitHub. The author mentions:
... the documentation was ambiguous but validation + settings is passed
like $(".form').form(rules, settings);
It appears like you are trying to recreate the wheel when using semantic ui.
Assuming you have included the complete versions of semantic.css in the head and semantic.js just above the body closing tag, here is an abbreviated version of working code for a simple contact form with some of the error work done by semantic and some by html5. For completeness I have included a user side captcha.
HTML
<form class="ui form" name="contact_sdta" action="" method="post">
<div class="field">
<label>Your Email </label>
<div class="ui left labeled icon input">
<input name="email" id="email" placeholder="name#mail.com" type="email">
<i class="mail icon"></i>
<div class="ui corner label">
<i class="asterisk red icon"></i>
</div>
</div>
</div>
<div class="field">
<label>Subject</label>
<div class="ui left labeled icon input">
<input name="subject" id="subject" placeholder="I am interested in more information about" type="text">
<i class="text file outline icon"></i>
<div class="ui corner label">
<i class="asterisk red icon"></i>
</div>
</div>
</div>
<div class="field">
<label>Message</label>
<div class="ui left labeled icon input">
<textarea name="message"></textarea>
<i class="text file outline icon"></i>
<div class="ui corner label">
<i class="asterisk red icon"></i>
</div>
</div>
</div>
<div class="ui buttons">
<input type="reset" value="Cancel" class="ui button">
<div class="or"></div>
<input type="submit" value="Submit" class="ui blue submit button">
</div>
<div class="ui error message"></div>
</form>
mainjs
$(function(){
$('form input[type=reset]')
.before('<div>Are you a human? <input type="checkbox" name="captcha" /><i class="asterisk red icon"></i></div><br />');
$('.ui.form').form({
email: {
identifier: 'email',
rules: [
{
type: 'empty',
prompt: 'Please enter your email'
}
]
},
subject: {
identifier: 'subject',
rules: [
{
type: 'empty',
prompt: 'Please enter a subject'
}
]
},
message: {
identifier: 'message',
rules: [
{
type: 'empty',
prompt: 'Please enter a message'
}
]
},
human: {
identifier: 'captcha',
rules: [
{
type: 'checked',
prompt: 'You must behuman'
}
]
}
});
});
I hope this helps to clear things up.
The developer confirmed this was a bug on GitHub:
https://github.com/Semantic-Org/Semantic-UI/issues/959
MVC5: Try Add this in the lowermost part of your View
#section Scripts {
#Scripts.Render("~/bundles/jqueryval") }

Form validation in meteorjs

I am doing simple validation of inputs in meteorjs, after first tour it works, and every next time it doesn't work (until I reload the page) – it means error messages are not displayed.
//main.js//
Template.addMealForm.events({
'click #submitNewMeal': function (ev) {
ev.preventDefault();
var query = {
name: $("#name").val().trim(),
price: $("#price").val(),
calories: $("#calories").val(),
category: $("#category").val()
};
areInputsValid(query);
}
});
var areInputsValid = function (query) {
if ((query.name.length === 0) || (query.price.length === 0) || (query.calories.length === 0)) {
$("#warningLabel").addClass("di")
$(".warningLabel").text("All fields are required");
}
else if ((isNaN(query.price) === true) || (isNaN(query.calories) === true)) {
$("#warningLabel").addClass("di")
$(".warningLabel").text("To Price and Calories fields please enter a number");
}
else {
console.log('it works');
$('.dn').hide();
}
};
//main.html//
<template name="addMealForm">
<form role="form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control input_form" id="name" placeholder="Name of the meal">
</div>
<div class="form-group">
<label for="price">Price</label>
<input class="form-control input_form" id="price" placeholder="Price">
</div>
<div class="form-group">
<label for="calories">Calories</label>
<input class="form-control input_form" id="calories" placeholder="Calories">
</div>
<div id="warningLabel" class="form-group has-error dn">
<label class="control-label warningLabel"></label>
</div>
<button id="submitNewMeal" type="submit" class="btn btn-rimary">Add</button>
</form>
</template>
The problem is that you are calling $('.dn').hide() in the success case. Because #warningLabel has a class of dn it will not be displayed again on subsequent errors.
One solution is to add $('.dn').show() to the top of areInputsValid.
You already have Tracker as part of Meteor, so I put a little tutorial and JSfiddle together on how to use it to implement a typical form validation scenario.
http://bit.ly/meteor-form-validation-video
http://bit.ly/meteor-form-validation-fiddle

Resources