How to add my own validator with Backbone.validation? - javascript

And I want to create my own validator with Backbone.validation.
I have tried this:
_.extend(Backbone.Validation.validators, {
myValidator: function(value, attr, customValue, model) {
if(value !== customValue){
return 'error';
}
},
});
And in my schema:
profiles: {
editorClass: "form-inline test",
title: "Skills",
type: 'List',
itemType: 'NestedModel',
model: UserProfile,
render: "buttonsstars",
validators: ['myValidator'],
},
But, i couldnt get anything.

From the documentation of backbone.validation, to add a custom validator, you first need to extend the Backbone.Validation.validators (before using it within a model).
_.extend(Backbone.Validation.validators, {
myValidator: function(value, attr, customValue, model) {
if(value !== customValue){
return 'error';
}
},
});
Then use it like this:
var Model = Backbone.Model.extend({
validation: {
age: {
myValidator: 1 // uses your custom validator
}
}
});
If the custom validator is specific to a model, but shared across the validation schema:
var SomeModel = Backbone.Model.extend({
validation: {
name: 'validateName'
},
validateName: function(value, attr, computedState) {/*...snip...*/}
});
If the validator is custom for a specific field of the schema:
var SomeModel = Backbone.Model.extend({
validation: {
name: {
fn: function(value, attr, computedState) {/*...snip...*/}
}
}
});

Related

accessing $(this) value returns undefined jquery

I am trying to access $(this) inside the select2 initialization, but it returns undefined.
$(".tags").each(function() {
var placeholder = "Select Email";
if($(this).attr('name') === 'names[]')
placeholder = "Select Name";
$(this).select2({
tags: true,
placeholder: placeholder,
language: {
noResults: function () {
return 'Type and enter to add new';
},
},
escapeMarkup: function (markup) {
return markup;
},
createTag: function(params) {
console.log($(this).attr('name')); // returns undefined
if (params.term.indexOf('#') === -1)
return null;
return {
id: params.term,
text: params.term
}
}
})
});
select2() is initialized for each .tags. I need to access $(this) inside the initialization here.
How can I do that?
You can hold a reference to $(this) before calling select2()
$(".tags").each(function() {
var placeholder = "Select Email";
var $that = $(this);
if($that.attr('name') === 'names[]')
placeholder = "Select Name";
$that.select2({
tags: true,
placeholder: placeholder,
language: {
noResults: function () {
return 'Type and enter to add new';
},
},
escapeMarkup: function (markup) {
return markup;
},
createTag: function(params) {
console.log($that.attr('name'));
if (params.term.indexOf('#') === -1)
return null;
return {
id: params.term,
text: params.term
}
}
})
});
Hope this helps
The issue is because the createTag function does not run under the scope of the element which select2 was instantiated on.
To fix this you would need to retain a reference to the original element, which can be done by storing it in a variable in the each() handler:
$(".tags").each(function() {
var $tag = $(this);
// other logic...
$tag.select2({
// other logic...
createTag: function(params) {
console.log($tag.prop('name'));
if (params.term.indexOf('#') === -1)
return null;
return {
id: params.term,
text: params.term
}
}
})
});

Add dynamic form validation callback to field

I am using formValidation.io and need to dynamically add a callback type validator within a class so that it can use a class property. The issue is that I initially pass my validator options into a super call that has some form validation procedures. But this means I do not have initial access to class properties.
So to do this I was trying to use updateOption but it definitely does not begin to validate this.
class MyForm extends Form {
var validatorOptions = {
fields: {
phoneNumber: {
validators: {
regexp: {
regexp: Regexp.phone,
message: "Please enter a valid phone number"
}
}
}
}
};
super({
validator: {
options: validatorOptions
}
});
var self = this;
this._cachedPhoneNumbers = [];
var phoneValidatorCallback = {
message: "This number is already in use",
callback: function(value, validator, $field) {
if ($.inArray(value, self._cachedPhoneNumbers) > -1)
return false;
return true;
}
}
// ref to validator is definitely valid!
this.validator.updateOption('phone', 'callback', 'callback', phoneValidatorCallback);
}
Here is the answer. I simply misused the function.
class MyForm extends Form {
var validatorOptions = {
fields: {
phoneNumber: {
validators: {
regexp: {
regexp: Regexp.phone,
message: "Please enter a valid phone number"
},
callback: {
message: 'This number is in use',
callback: function() {
return true;
}
}
}
}
}
};
super({
validator: {
options: validatorOptions
}
});
var self = this;
this._cachedPhoneNumbers = [];
function phoneValidatorCallback(value, validator, $field) {
if ($.inArray(value, self._cachedPhoneNumbers) > -1)
return false;
return true;
}
// ref to validator is definitely valid!
this.validator.updateOption('phone', 'callback', 'callback', phoneValidatorCallback);
}

How do I pass a dynamic page :id to $http.get url in Vue.js

I have have view router set up:
router.map({
'/tracks/:id': {
component: SingleTrack
}
})
And this is my component (which works with a hard coded URL):
var SingleTrack = Vue.component('track', {
template: '#track-template',
data: function() {
return {
track: ''
}
},
ready: function() {
this.$http.get('//api.trax.dev/tracks/1', function (data) {
this.$set('track', data.track)
})
}
});
How do I pass the url/:id to the end of the $http.get string so i can grab the correct data dynamically when that route in loaded, something like:
ready: function(id) {
this.$http.get('//api.trax.dev/tracks/' + id, function (data) {
this.$set('track', data.track)
})
}
You should be able to get the route parameter from the component $route property :
var itemId = this.$route.params.id;
this.$http.get('//api.trax.dev/tracks/' + itemId, function (data) {
this.$set('track', data.track)
})
See more details in vue.js router documentation
For Best Practises:
index.js(router)
{
path: '/tracks/:id',
name: 'SingleTrack',
component: SingleTrack,
props: (route) => {
const id = Number.parseInt(route.params.id);
return { id }
},
}
SingleTrack.vue
props: {
id: {
type: Number,
required: true,
},
},
mounted(){
this.$http.get('//api.trax.dev/tracks/' +this.id, function (data) {
this.$set('track', data.track)
})
}

JavaScript JTable - Null value with multiple cascaded lists/dropdowns

I am using the latest version of JTable from http://jtable.org/ (downloaded it yesterday). I setup my JTable as shown below (I also included the server-side code below, which is written in C#). The List function works (the data shows up in the table), the Add function works, and the Delete function works. However, when I go to Edit a row, there is an error when trying to populate the data for the "ElevationsMulti" field. I get an error that simply says, "Cannot load options for field ElevationsMulti."
JTable Code:
$('#ReportsContainer').jtable({
title: 'Reports',
actions: {
listAction: '/Report_SingleEstimate/GetReportNames?customerId=' + customerId,
createAction: '/Report_SingleEstimate/AddReport',
updateAction: '/Report_SingleEstimate/EditReport',
deleteAction: '/Report_SingleEstimate/DeleteReport'
},
fields: {
ReportID: {
key: true,
list: false
},
ReportName: {
title: 'Report Name'
},
CustomerID: {
title: 'Customer',
list: false,
options: '/Estimates/GetCustomers',
defaultValue: customerId
},
PlanNameID: {
title: 'Plan Name',
dependsOn: 'CustomerID',
options: function (data) {
if (data.source == 'list') {
return '/Estimates/GetListOfPlanNames?customerId=0';
}
//data.source == 'edit' || data.source == 'create'
return '/Estimates/GetListOfPlanNames?customerId=' + data.dependedValues.CustomerID;
}
},
ProductID: {
title: 'Product',
options: '/Estimates/GetProducts'
},
HeaderFieldsMulti: {
title: 'Fields',
options: '/Report_SingleEstimate/GetHeaderFields',
type: 'multiselectddl',
list: false
},
ElevationsMulti: {
title: 'Elevations',
type: 'multiselectddl',
dependsOn: ['PlanNameID', 'ProductID'],
options: function (data) {
if (data.source == 'list') {
return '/Elevation/GetAllElevations';
}
return '/Report_SingleEstimate/GetElevations?PlanNameID=' + data.dependedValues.PlanNameID +
'&ProductID=' + data.dependedValues.ProductID;
},
list: false
}
}
});
$('#ReportsContainer').jtable('load');
Not sure if it makes a difference in JTable, but the ElevationsMulti depends on the PlanNameID and ProductID fields, and the PlanNameID field depends on the CustomerID fields. In other words, the ElevationsMulti field depends on a field that depends on another field (multiple nested dropdowns).
C# server-side code:
[HttpPost]
public JsonResult GetElevations(int PlanNameID, int ProductID)
{
try
{
int estimateId = Estimates.getEstimateId(PlanNameID, ProductID);
List<MyDropdownList> elevations = Estimate_ElevationList.listElevationsByEstimateForDropdown(estimateId);
return Json(new { Result = "OK", Options = elevations });
}
catch (Exception ex)
{
return Json(new { Result = "ERROR", Message = ex.Message });
}
}
Error here:
Further debugging has given me a more specific error.
The parameters dictionary contains a null entry for parameter 'PlanNameID' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.JsonResult GetElevations(Int32, Int32)' in 's84.Controllers.Report_SingleEstimateController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Basically, JTable sends PlanNameID to the server as a null value. Which seems to indicate that JTable has not loaded the options for the PlanNameID field yet when it makes the server call for the ElevationsMulti field.
How do I make JTable wait to load the ElevationsMulti field until after the PlanNameID field has been loaded?
Problem solved.
The problem came from using the Type "multiselectddl" in JTable. I changed the code in JTable that creates the multiselectddl to the same code as the regular dropdown. Here is the code:
_createInputForRecordField: function (funcParams) {
...
//Create input according to field type
if (field.type == 'date') {
return this._createDateInputForField(field, fieldName, value);
} else if (field.type == 'textarea') {
return this._createTextAreaForField(field, fieldName, value);
} else if (field.type == 'password') {
return this._createPasswordInputForField(field, fieldName, value);
} else if (field.type == 'checkbox') {
return this._createCheckboxForField(field, fieldName, value);
} else if (field.options) {
if (field.type == 'multiselectddl') {
return this._createDropDownListMultiForField(field, fieldName, value, record, formType, form);
} else if (field.type == 'radiobutton') {
return this._createRadioButtonListForField(field, fieldName, value, record, formType);
} else {
return this._createDropDownListForField(field, fieldName, value, record, formType, form);
}
} else {
return this._createTextInputForField(field, fieldName, value);
}
},
_createDropDownListMultiForField: function (field, fieldName, value, record, source, form) {
//Create a container div
var $containerDiv = $('<div class="jtable-input jtable-multi-dropdown-input"></div>');
//Create multi-select element
var $select = $('<select multiple="multiple" class="' + field.inputClass + '" id="Edit-' + fieldName + '" name=' + fieldName + '></select>')
.appendTo($containerDiv);
var options = this._getOptionsForField(fieldName, {
record: record,
source: source,
form: form,
dependedValues: this._createDependedValuesUsingForm(form, field.dependsOn)
});
this._fillDropDownListWithOptions($select, options, value);
return $containerDiv;
}

Backbone View: function is undefined

I want to build a simple backbone app for depositing and withdrawing funds via Stripe. Since a lot of the functionality is common, I placed that in a Stripe view, and extend the Deposit and Withdraw views from it, like so:
var App = {
Models: {},
Collections: {},
Views: {},
Router: {}
}
App.Views.Home = Backbone.View.extend({
el: $('#main-content'),
template: Handlebars.compile($('#home-template').html()),
render: function() {
this.$el.html(this.template())
return this
},
events: {
'click #deposit-button': 'navigateToDeposit',
'click #withdraw-button': 'navigateToWithdraw'
},
navigateToDeposit: function(e) {
Backbone.history.navigate('/deposit', true)
},
navigateToWithdraw: function(e) {
Backbone.history.navigate('/withdraw', true)
}
})
App.Views.Stripe = Backbone.View.extend({
el: $('#main-content'),
initialize: function() {
Stripe.setPublishableKey('pk_test_0QvQdPBkXAjB4EBsT4mf226x')
},
render: function() {
this.$el.html(this.template())
return this
},
events: {
'click #submit': 'submitForm'
},
submitForm: function(e) {
e.preventDefault()
$('#submit').prop('disabled', true)
var that = this
Stripe.card.createToken($('#form'), that.stripeResponseHandler)
},
stripeResponseHandler: function(status, response) {
var $form = $('#form')
if(response.error) {
$form.find('.payment-errors').text(response.error.message)
$('submit').prop('disabled', false)
} else {
console.log(this)
var form_data = this.getFormData(response.id),
that = this
$.post(that.transaction_endpoint, form_data, function(data, textStatus, jqXHR) {
Backbone.history.navigate('/home', true)
})
}
}
})
App.Views.Deposit = App.Views.Stripe.extend({
template: Handlebars.compile($('#deposit-template').html()),
getFormData: function(token) {
return {
amount: $('#form input[name=amount]').val(),
token: token
}
},
transaction_endpoint: 'handledeposit'
})
App.Views.Withdraw = App.Views.Stripe.extend({
template: Handlebars.compile($('#withdraw-template').html()),
getFormData: function(token) {
return {
name: $('#form input[name=name]').val(),
email: $('#form input[name=email]').val(),
token: token
}
},
transaction_endpoint: 'handlewithdraw'
})
App.Router = Backbone.Router.extend({
routes: {
'deposit' : 'showDepositView',
'withdraw' : 'showWithdrawView',
'*path' : 'showHomeView'
},
showDepositView: function() {
var depositView = new App.Views.Deposit()
depositView.render()
},
showWithdrawView: function() {
var withdrawView = new App.Views.Withdraw()
withdrawView.render()
},
showHomeView: function() {
var homeView = new App.Views.Home()
homeView.render()
}
})
var router = new App.Router()
Backbone.history.start()
The call to the getFormData method gives me an error saying the function is undefined, even though I have defined it in both Deposit and Withdraw views. Also, I added a console.log(this) right above it, and it logs the Window object to the console instead of the View. What am I doing wrong here?
I have a feeling it's to do with this call:
Stripe.card.createToken($('#form'), that.stripeResponseHandler)
Try binding this to the calling scope using .bind():
Stripe.card.createToken($('#form'), that.stripeResponseHandler.bind(this))
You don't really need to do var that = this but I'll leave it in for simplicity's sake.

Categories