Knockout-Validation - data-bind observable array - validation

I'm trying to do some validation for groups of radio buttons, where each group needs to have a value selected. I'm attempting to use Knockout-Validation to do the validation, but I'm having trouble making this dynamic. I can set up the validation, but getting it to validate each group individually is a challenge. Right now it's treating all groups of questions the same. I have an example on JSFiddle which shows the current situation.
I create the questions dynamically in the back end from a DB, but I could potentially add in a unique identifier for each question, but how do I reference an observable who's name I can't know ahead of time for a number of questions I can't predetermine the number of?
Example code form JSFiddle:
$(function() {
var viewModelQuestionnaire = ko.validatedObservable({
'checkScore': ko.observable().extend({
required: true
}),
submit: function () {
if (this.isValid()) {
alert('Thank you.');
} else {
console.log(this);
alert('Please check your submission.');
this.errors.showAllMessages();
}
}
});
ko.applyBindings(viewModelQuestionnaire);
});
<form action="/Questionnaire/Save" method="post"> <table id="ESAS-questions">
<tr class="esas-question-row">
<td class="esas-best-area">
<div class="esas-best-symptom">No Pain</div>
</td>
<td class="esas-score-area">
<span class="esas-score">0<input type="radio" name="question_id-1" value="0" data-bind="checked: checkScore"/></span>
<span class="esas-score">1<input type="radio" name="question_id-1" value="1" data-bind="checked: checkScore"/></span>
<span class="esas-score">2<input type="radio" name="question_id-1" value="2" data-bind="checked: checkScore"/></span></td>
<td class="esas-worst-area">
<span class="esas-worst-symptom">Worst Possible Pain</span>
</td>
</tr>
<tr class="esas-question-row">
<td class="esas-best-area">
<div class="esas-best-symptom">No Tiredness</div>
<div class="esas-best-subtext">(Tiredness = lack of energy)</div>
</td>
<td class="esas-score-area">
<span class="esas-score">0<input type="radio" name="question_id-2" value="0" data-bind="checked: checkScore"/></span>
<span class="esas-score">1<input type="radio" name="question_id-2" value="1" data-bind="checked: checkScore"/></span>
<span class="esas-score">2<input type="radio" name="question_id-2" value="2" data-bind="checked: checkScore"/></span> </td>
<td class="esas-worst-area">
<span class="esas-worst-symptom">Worst Possible Tiredness</span>
</td>
</tr>
</table>
<p><input type="submit" value="Submit" class="finish" data-bind="click:submit" /></p>

You can avoid multiple error message for that you have to customize the display of your objects validation message by using validationMessage binding and stop the default error message insertion with knockout validation options.
data-bind="validationOptions: { insertMessages: false }" //default validation will not insert
// customize the display of your objects validation message
<p class="invalid" data-bind="validationMessage: checkScore"></p>
You have binded same property to both question so whenever one field is selected it select other one also.You can have list of questions as array of objects in observableArray.
Sample structure for list of questions:-
var questions=[
{
question: "No Pain",
checkScore: ko.observable().extend({
required: true
}),
description: "Worst Possible Pain"
},
{
question: "No Tiredness (Tiredness = lack of energy)",
checkScore: ko.observable().extend({
required: true
}),
description: "Worst Possible Tiredness"
}
]
But for questionList validation you have to enable deep(recursive) validation.
Fiddle Demo

Related

alpine js, x-model binds to wrong value when switching tabs

running into an issue with alpine js.
First of all I have a master component that allows the user to switch between two tabs
#props(['pages', 'blogs'])
<div x-data="init()" class="overview row mb30">
<div class="pageContent__content__languages disFlex mb20 bBottom">
<span
#click.prevent='tab = "pages"'
:class='{ "active": tab === "pages" }'
class="pageContent__content__languages__item disFlex aiCenter pt10 pb10 pr10 pl10 mr10 pointer">
Pagina's
</span>
<span
#click.prevent='tab = "blogs"'
:class='{ "active": tab === "blogs" }'
class="pageContent__content__languages__item disFlex aiCenter pt10 pb10 pr10 pl10 bRadius mr10 pointer">
Blogs
</span>
</div>
<div x-show="tab === 'pages'">
<x-form.edit.navigation.pages :pages="$pages" />
</div>
<div x-show="tab === 'blogs'">
<x-form.edit.navigation.blogs :blogs="$blogs" />
</div>
<button type="button" wire:click="navigationAddToMenu" class="btn blue w100 mt10">
Toevoegen aan menu
</button>
</div>
#push('scripts')
#once
<script type='text/javascript'>
function init() {
return {
selected: #entangle('selected'),
tab: 'pages',
};
}
</script>
#endonce
#endpush
These tabs either display pages or blogs depending on which tab is clicked.
Inside of these blade components is just a foreach loop to display the items,
#props(['pages'])
<div style="grid-area: unset" class="pageContent__settings bRadius--lrg disFlex fdCol">
<table class="overview__wrapper">
<tbody class="bRadius--lrg">
#foreach($pages as $page)
<tr class="overview__row bBottom">
<td class="overview__row__checkbox">
<input x-model='selected' value='{{ $page->id }}' type="checkbox"
id="col-row-{{$loop->index}}">
<label for="col-row-{{$loop->index}}"></label>
</td>
<td class="overview__row__name lh1">
{{ $page->page_name }}
</td>
</tr>
#endforeach
</tbody>
</table>
</div>
The blog blade component is nearly identical.
Now the user is able to check a checkbox to add items to their menu, this is binded using the #entangle directive and the x-model on the checkbox.
So far when the user is on the default tab pages and they select a page the correct ID is retrieved from the checkbox, BUT when the user switches tab to the blogs display, and clicks a checkbox the value is retrieved from the pages tab.
e.g.
1 page and 1 blog, page has id of 1 blog has id of 2. User is on the pages tab and clicks on the checkbox the correct value of 1 is now added to the selected array, user switches tabs to blogs and clicks the checkbox the expected behavior would be to have the id of 2 added to the selected array, but it still adds 1.
Inspecting the HTML and the loops do add unique ids to each of their items.
Fixed it, need to make my ids on the input more unique, instead of doing
<input x-model='selected' value='{{ $blog->id }}' type="checkbox"
id="col-row-{{$loop->index}}">
<label for="col-row-{{$loop->index}}"></label>
I added a extra identifier
<input x-model='selected' value='{{ $blog->id }}' type="checkbox"
id="col-row-blogs-{{$loop->index}}">
<label for="col-row-blogs-{{$loop->index}}"></label>
and pages for the pages.
This fixed the issue

How do I compare and verify user input field to stored data BEFORE to sending form in ColdFusion

I’m updating a site for my brother who teaches training courses. He has a registration form on the site that collects name, age, address, etc. That information is sent to him through cfmail with a copy sent to the registrant. The registrants then mails in a check via snail-mail to complete the registration. (My brother does NOT want to use an online payment method.)
Included in the form is the course name, location and fee. He asked if it was possible to implement some sort of “Promo Code” to offer discounts to select users. I’ve added PromoCode and PromoCode_Fee columns in SQL and am able to make it all work throughout the process.
My problem is on the user end. If the user mistypes the PromoCode in the form, the app will obviously not register the discount, send the registration emails out with the standard fee, and store the registration info in the DB. The only way for the user to fix the PromoCode would be to re-register, which would re-send the emails and add a new registration to the DB.
What I’d like to do is verify that the user entered a valid PromoCode in the input field PRIOR to submitting the form by comparing what they typed to the PromoCode stored in the DB. If the PromoCode doesn’t match, add “Promo Code is invalid” under the input field.
I do this as a hobby, am self-taught and am not sure if it’s even possible (or good idea.) I imagine it’s not possible to do with ColdFusion and would most likely need some sort of JS or jQuery - both of which I’m pretty illiterate in.
I’ve been searching for hours to see if anyone had any similar questions, but have come up short. Any help or pointing me in the right direction would be greatly appreciated.
Here's the code I'm putting together:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="/scripts/jquery.validate.js"></script>
<script>
$(document).ready(function() {
var validator = $("#signupform").validate({
rules: {
firstname: "required",
lastname: "required",
username: {
required: true,
minlength: 2,
remote: "/components/promocodecomponent.cfc?method=validateUserName"
}
}
});
});
</script>
<div class="row justify-content-center">
<div class="col-10">
<form id="signupform" autocomplete="off" method="get" action="">
<table>
<tr>
<td class="label">
<label id="lfirstname" for="firstname">First Name</label>
</td>
<td class="field">
<input id="firstname" name="firstname" type="text" value="" maxlength="100">
</td>
<td class="status"></td>
</tr>
<tr>
<td class="label">
<label id="llastname" for="lastname">Last Name</label>
</td>
<td class="field">
<input id="lastname" name="lastname" type="text" value="" maxlength="100">
</td>
<td class="status"></td>
</tr>
<tr>
<td class="label">
<label id="lusername" for="username">Username</label>
</td>
<td class="field">
<input id="username" name="username" type="text" value="" maxlength="50">
</td>
<td class="status"></td>
</tr>
<tr>
<td class="field" colspan="2">
<input id="signupsubmit" name="signup" type="submit" value="Signup">
</td>
</tr>
</table>
</form>
</div>
</div>
Here's the component code:
component {
remote boolean function validateUserName(string username) returnFormat="json"{
if (arguments.username == "john") {
return true;
}
return "Username already in use";
}
}
Usually, you'd need to post some code that you've tried and isn't working. But you've outlined what you want and are just not sure where to start.
You can test just the value of the discount code before allowing the whole form to be submitted. You don't say how you're doing client-side form validation. I'd suggest using jQuery Validate to handle that, it's very easy to implement.
https://jqueryvalidation.org/documentation/
Go to the demo "The Remember The Milk sign-up form". This form checks the username field via Ajax before the rest of the form can be submitted.
var validator = $("#signupform").validate({
rules: {
firstname: "required",
lastname: "required",
username: {
required: true,
minlength: 2,
remote: "users.action"
}
}
});
If not using this framework, then just make an Ajax request when change is triggered on the discount code field and make sure there's a positive response from that before you allow the form to be submitted.
Also, you need to do server-side validation of the discount code when someone submits the form. If they've entered a discount code that is invalid, then don't allow the form to be processed until they enter a valid code or they clear the value from that field.
I do this as a hobby, am self-taught ... I imagine ... would most likely need some sort of JS or jQuery - both of which I’m pretty illiterate in.
The plugin is easy to use but there may be a slight learning curve depending on your jQuery and javascript skills. Bigger tasks are easier to tackle if you you break them into smaller ones and solve those one at a time. Start with the code snippet #AdrianJMoreno posted and delve into the documentation to understand what the code is doing and how:
validate() - a function that initializes the plugin for a specific form
rules - options that tells the plugin which form fields should be validated and how
remote - remote url to be called via ajax to validate a field's value. The remote endpoint should either return true/false or true/"some custom error message";
1. Build Sample Form
Once you have a sense of how the plugin works, move on to building a scaled down version of the Milk demo form using the code snippet.
JQuery and Validate libraries
<!-- modify js paths as needed -->
<script src="scripts/jquery-3.1.1.js"></script>
<script src="scripts/jquery.validate.min.js"></script>
Javascript initialization (so plugin validates the form)
<script>
$(document).ready(function() {
var validator = $("#signupform").validate({
rules: {
firstname: "required",
lastname: "required",
username: {
required: true,
minlength: 2,
remote: "users.action"
}
}
});
});
</script>
HTML form (only the fields in code snippet)
<form id="signupform" autocomplete="off" method="get" action="">
<table>
<tr>
<td class="label">
<label id="lfirstname" for="firstname">First Name</label>
</td>
<td class="field">
<input id="firstname" name="firstname" type="text" value="" maxlength="100">
</td>
<td class="status"></td>
</tr>
<tr>
<td class="label">
<label id="llastname" for="lastname">Last Name</label>
</td>
<td class="field">
<input id="lastname" name="lastname" type="text" value="" maxlength="100">
</td>
<td class="status"></td>
</tr>
<tr>
<td class="label">
<label id="lusername" for="username">Username</label>
</td>
<td class="field">
<input id="username" name="username" type="text" value="" maxlength="50">
</td>
<td class="status"></td>
</tr>
<tr>
<td class="label">
<label id="lsignupsubmit" for="signupsubmit">Signup</label>
</td>
<td class="field" colspan="2">
<input id="signupsubmit" name="signup" type="submit" value="Signup">
</td>
</tr>
</table>
</form>
2. Test (Sample Form)
Test out the form in a browser. Since all fields are required, leaving the fields blank and submitting should trigger error messages on submit
3. Create Remote URL
The live demo form uses a mocking library to simulate a remote ajax call. The mock url "users.action" must be replaced with a real url on your server. That url will point to a new component (or "CFC") you create. The component should contain a remote accessible method that validates a given username.
Keep things simple for the initial test. Have the method return true if the input equals a test value like "John" and false for everything else. Ultimately you'll replace that with real business logic, like a database lookup, but one step at a time.
YourComponentName.cfc
component {
remote boolean function validateUserName(string username) returnFormat="json"{
if (arguments.username == "john") {
return true;
}
return false;
}
}
4. Test (Remote URL)
To test a remote method in a browser, specify the path, name of the method to invoke, and any parameters that method expects. Verify the component returns the expected results. The result should be true for username=John and false for any other value.
Example url:
https://localhost/path/YourComponentName.cfc?method=validateUsername&username=john
Valid: UserName=John:
InValid: UserName=Bob:
5. Fix Remote URL
With the component working, update the javascript code to point to the new cfc. Omit the username parameter because the plugin will pass it to the ajax call automatically.
For example, if the component location on disk is:
c:\path\webroot\path\YourComponentName.cfc
Use the url:
/path/YourComponentName.cfc
JS Snippet:
...,
username: {
...,
remote: "/path/YourComponentName.cfc?method=validateUserName"
}
6. Test Form (.. are you sensing a theme?)
Test the final form again with both a valid and invalid usernames to confirm it works as expected.
Invalid usernames "Mike" or "Bob" should trigger an error
Valid username "John" should not trigger an error
Next Steps ...
Continue to expand the working example and learn more about the plugin features by adding some customization. The next steps are left as an exercise for the reader ...
Replace hard coded test logic in the cfc with a database lookup
Replace default error message "Please fix this field" with a more user friendly message
Change the appearance of valid and invalid fields using CSS

(Vue + Laravel) v-if / v-else inside <td> throws no matching end tag error

I apologize in advance if my question is silly-- Vue newbie here but very eager to learn!
In order to create an interface to manage user privileges in a web app, I've made a component in which I want to create a table with nested v-fors.
For each row, I want 5 cells (): the first one includes text depending on the current iteration of object permisos and the other 4 should be created from the object tipos_permisos (which is an object with 'fixed' values).
The problem:
When I try to compile, I get multiple errors claiming that some tags have no matching end tag. I assume it is due to the v-for nested inside another v-for and/or the v-if inside the innermost v-for... or something like this. The claim is that has no matching tag or that the element uses v-else without corresponding v-if :/
I've tried writing out the last 4 cells manually (without using the tipos_permisos object) but even then, I get errors. In this case, claiming that , and have no matching end tag.
The desired result:
Please note that for some of the resources listed, some of the privileges might not apply (i.e., the log viewer is read-only always so it doesn't have the C (create), U (update) or D (delete) privileges, hence the conditional to show either a checkbox or an icon)
My component:
<template>
<form :action="usr.url.savepermisos" method="post" class="">
<div class="card">
<div class="card-header bg-gd-primary">
<h3 class="card-title">Privilegios de {{ usr.nombre }}</h3>
</div>
<div class="card-content">
<p class="mb-3">
El usuario
<span class="font-w700">{{ usr.nombre }}</span>
tiene los siguientes privilegios:
</p>
<table class="table">
<thead>
<tr>
<th>Recurso</th>
<th>Alta / Creación</th>
<th>Visualización</th>
<th>Edición</th>
<th>Eliminación</th>
</tr>
</thead>
<tbody>
<tr v-for="(recurso, idxrecurso) in permisos">
<td :data-order="recurso.id">{{ recurso.etiqueta }}</td>
<td class="align-middle" v-for="(color,tp) in tipos_permisos">
<label :class="'checkbox checkbox-' + color + ' mycheckbox'" v-if="(typeof recurso[tp] !== "undefined")">
<input type="checkbox" class="checkbox-input check_permiso" />
<span class="checkbox-indicator"></span>
</label>
<i class="fas fa-minus-square fa-lg text-muted" data-toggle="tooltip" title="N/A" v-else></i>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</template>
<script>
export default {
data() {
return {
tipos_permisos: {
'C': 'success',
'R': 'info',
'U': 'warning',
'D': 'danger'
}
}
},
props: [
'usr',
'permisos',
'perm_log'
]
}
</script>
If something is unclear please let me know so that I can provide further info.
There is a missing "=" after v-for:
<td class="align-middle" v-for"(color,tp) in tipos_permisos">
I didn't understand this part:
v-if="(typeof recurso[tp] !== "undefined")"
If undefined in your code is a string, your condition should be
v-if="recurso[tp] !== 'undefined'"
If it's a real undefined, it should be like this:
v-if="recurso[tp] !== undefiened"

when hit enter go to next element vuejs2

i have function on click vuejs2
this is the function
#change='units(chosed_item.id,$event)'
and this is the code for this function to send the id and put result inside array
units:function(item_id,$event){
var unit_id = event.target.value;
$.ajax({
type:'POST',
url: path+'unit_search',
data: {unit_id,item_id},
success:(data) => {
this.voucher_items.push({
id:data['id'],
item_desc_ar:data['item_desc_ar'],
item_unit_name:data['item_unit_name'],
item_smallest_unit_cost:data['item_smallest_unit_cost'],
item_smallest_unit:data['item_smallest_unit'],
item_smallest_unit_selling_price:data['item_smallest_unit_selling_price'],
item_discount_value:data['item_discount_value'],
item_tax_value:data['item_tax_value'],
});
this.chosed_items = [];
}
});
},
and i loop the voucher_items in loop
like this
<tr v-for="voucher_item , key in voucher_items">
<td>
<input name='items_quantity_coast[]' type='text' class='form-control' />
</td>
<td>
<input type='number' name='items_quantity_quantity[]' min='1' class='form-control' v-model='voucher_item.quantity' required />
</td>
<td>
<input type='number' name='items_quantity_discount[]' min='1' class='form-control' v-model='voucher_item.item_discount_value_input' min='1' max=''/>
</td>
</tr>
how can i make the input focus go to last input in voucher_items like when i click unit function do the code above and focus the input in
<input type='number' name='items_quantity_discount[]' min='1' class='form-control' v-model='voucher_item.item_discount_value_input' min='1' max=''/>
thanks
You can use a custom directive ..
In your component, add a directives option ..
directives: {
focus: {
inserted(el) {
el.focus()
}
}
}
Use your new v-focusdirective to focus on the desired element ..
This will focus on the last input on your last row. If you want finer control on which element to focus you can create a custom directive and use it on an upper level element (the table element for example), then use normal DOM traversing techniques(firtElementChild, lastElementChild et al.) to target specific elements. The process will be the same.

VueJs and Laravel - multiple select fields

Using Laravel 5.4 and Vuejs 2.1
In code I have a select field with two input fields (quantity and stock) in tr (Table Row). Table Row with the fields can be dynamically added as much as user is needed, this is the code:
<tbody id="loads-content">
<tr v-for="(item, index) in items">
<td>
<select v-model="item.load_id" name="load_id[]" class="loads" v-on:change="getLoadStock()">
<option v-for="load in loads" :value="load.id" :id="load.id">{{load.name}}</option>
</select>
</td>
<td><input type="text" v-model="item.quantity" name="quantity[]" class="input is-hovered qty"></td>
<td><input type="text" v-model="item.stock" class="input is-hovered stock" disabled readonly="true"></td>
<td>
<button type="button" class="button is-danger remove" #click="items.splice(index, 1)"><i class="fa fa-minus-square-o" aria-hidden="true"></i></button>
</td>
</tr>
<a #click="addLine" class="button is-success"><i class="fa fa-plus-square-o" aria-hidden="true"></i></a>
</tbody>
When you choose some value from select field I need to populate the STOCK input field with stock data from the database. For that I used an API call. I made something like this:
methods: {
addLine() {
this.items.push({
load_id: '',
quantity: '',
stock: ''
})
},
getLoadStock(){
var loadsContent = $('#loads-content');
var tr = loadsContent.parent().parent();
var id = tr.find('.loads').val();
axios.get('/api/load/' + id)
.then(function(response) {
tr.find('.stock').val(response.data.stock);
})
.catch(function(error) {
console.log(error)
});
},
This code is not working as expected.
The goal is to fetch actual stock for current choosen load, to see how much quantity can you enter in the input field for quantity.
I am open for any suggestions, if anyone has a better approach and solution please help.
Thanks in advance! :)
You are mixing two different and incompatible ways of building a web app: Vue.js and jQuery.
When using Vue, you should not be manipulating the DOM directly. You should instead be binding the DOM elements to Vue model attributes. Then, if you need a DOM element to reflect a change, you change the model – not the DOM.
So for instance, you would want something like this (note adding index as an argument to getLoadStock):
<select v-model="item.load_id" name="load_id[]" class="loads" v-on:change="getLoadStock(index)">
<option v-for="load in loads" :value="load.id" :id="load.id">{{load.name}}</option>
</select>
and
getLoadStock(index){
axios.get('/api/load/' + this.items[index].load_id)
.then(function(response) {
this.items[index].stock = response.data.stock;
})
.catch(function(error) {
console.log(error)
});
},

Resources