Uninitialized constant Spree::Api::AramexAddressController::AramexAddressValidator - javascript

I am facing the following issue:
ActionController::RoutingError (uninitialized constant Spree::Api::AramexAddressController::AramexAddressValidator):
app/controllers/spree/api/aramex_address_controller.rb:2:in <class:AramexAddressController>
app/controllers/spree/api/aramex_address_controller.rb:1:in <top (required)>
I included the following in my controllers/spree/api/aramex_address_controller.rb:
class Spree::Api::AramexAddressController < ApplicationController
include AramexAddressValidator
# fetch cities from aramex api
def fetch_cities_from_aramex_address
response = []
zones = Spree::ShippingMethod.where(['LOWER(admin_name) like ?', '%aramex%']).map(&:zones).flatten
if zones.map(&:countries).flatten.map(&:iso).include?(params['country_code'])
response = JSON.parse(fetch_cities(params['country_code']))['Cities']
end
respond_to do |format|
format.json { render :json => response, :status => 200 }
end
end
# Validate address for aramex shipping
def validate_address_with_aramex
begin
zones = Spree::ShippingMethod.where(['LOWER(admin_name) like ?', '%aramex%']).map(&:zones).flatten
final_response = {}
if zones.map(&:countries).flatten.map(&:iso).include?(params[:b_country])
final_response[:b_errors] = confirm_address_validity(params[:b_city], params[:b_zipcode], params[:b_country])
end
if zones.map(&:countries).flatten.map(&:iso).include?(params[:s_country]) && params[:use_bill_address] == "false"
final_response[:s_errors] = confirm_address_validity(params[:s_city], params[:s_zipcode], params[:s_country])
end
rescue
return true
end
respond_to do |format|
format.json { render :json => final_response, :status => 200 }
end
end
# Confirm address validity with Aramex address validatio API
def confirm_address_validity(city, zipcode, country)
response = JSON.parse(validate_address(city, zipcode, country))
if response['HasErrors'] == true
if response['SuggestedAddresses'].present?
response['Notifications'].map{|data| data['Message']}.join(', ') + ', Suggested city name is - ' + response['SuggestedAddresses'].map{|data| data['City']}.join(', ')
else
if response['Notifications'].first['Code'] == 'ERR06'
response['Notifications'].map{|data| data['Message']}.join(', ')
else
cities_response = JSON.parse(fetch_cities(country, city[0..2]))
cities_response['Notifications'].map{|data| data['Message']}.join(', ') + ', Suggested city name is - ' + cities_response['Cities'].join(' ,')
end
end
end
end
end
In my route file I mentioned:
get 'validate_address_with_aramex', to: 'aramex_address#validate_address_with_aramex'
get 'fetch_cities_from_aramex_address', to: 'aramex_address#fetch_cities_from_aramex_address'
I included the following JS call for the submitted Aramex Ajax validation in assets/javascripts/spree/frontend/checkout/address.js:
Spree.ready(function($) {
Spree.onAddress = function() {
var call_aramex = true;
$("#checkout_form_address").on('submit', function(e) {
if ($('#checkout_form_address').valid()) {
var s_country = $("#order_ship_address_attributes_country_id").find('option:selected').attr('iso_code');
var s_zipcode = $("#order_ship_address_attributes_zipcode").val();
var s_city = $("#order_ship_address_attributes_city").val();
var b_country = $("#order_bill_address_attributes_country_id").find('option:selected').attr('iso_code');
var b_zipcode = $("#order_bill_address_attributes_zipcode").val();
var b_city = $("#order_bill_address_attributes_city").val();
if (call_aramex == true && (typeof aramex_countries !== 'undefined') && (aramex_countries.includes(b_country) || aramex_countries.includes(s_country))) {
e.preventDefault();
var error_id = $('#errorExplanation').is(':visible') ? '#errorExplanation' : '#manual_error'
$(error_id).html("").hide()
$.blockUI({
message: '<img src="/assets/ajax-loader.gif" />'
});
$.ajax({
url: "/api/validate_address_with_aramex",
type: 'GET',
dataType: "json",
data: {
s_country: s_country,
s_zipcode: s_zipcode,
s_city: s_city,
b_country: b_country,
b_zipcode: b_zipcode,
b_city: b_city,
use_bill_address: ($("#order_use_billing").is(":checked"))
},
success: function(result) {
$.unblockUI()
if (result.b_errors || result.s_errors) {
if (result.b_errors) {
$(error_id).append('<div>Billing Address ' + result.b_errors + ' </div>')
}
if (result.s_errors) {
$(error_id).append('<div>Shipping Address ' + result.s_errors + ' </div>')
}
if ((result.b_errors && !result.s_errors) && ($("#order_use_billing").is(":unchecked"))) {
$(".js-summary-edit").trigger("click")
}
$(error_id).show();
$('html, body').animate({
scrollTop: '0px'
}, 300);
} else {
call_aramex = false;
$('#checkout_form_address').submit()
}
},
error: function(xhr, status, error) {
$(error_id).append('Oops Something Went Wrong but you can process order')
$(error_id).show();
$.unblockUI()
$('html, body').animate({
scrollTop: '0px'
}, 300);
$('#checkout_form_address').submit()
}
});
}
}
})
var getCountryId, order_use_billing, update_shipping_form_state;
if (($('#checkout_form_address')).is('*')) {
($('#checkout_form_address')).validate({
rules: {
"order[bill_address_attributes][city]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_bill_address_attributes_country_id").val(), 22)
}
},
"order[bill_address_attributes][firstname]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_bill_address_attributes_country_id").val(), 15)
}
},
"order[bill_address_attributes][lastname]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_bill_address_attributes_country_id").val(), 17)
}
},
"order[bill_address_attributes][address1]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_bill_address_attributes_country_id").val(), 32)
}
},
"order[bill_address_attributes][address2]": {
maxlength: function(element) {
return maxCharLimit($("#order_bill_address_attributes_country_id").val(), 32)
}
},
"order[bill_address_attributes][zipcode]": {
maxlength: function(element) {
return maxCharLimit($("#order_bill_address_attributes_country_id").val(), 10)
}
},
"order[ship_address_attributes][city]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_ship_address_attributes_country_id").val(), 22)
}
},
"order[ship_address_attributes][firstname]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_ship_address_attributes_country_id").val(), 15)
}
},
"order[ship_address_attributes][lastname]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_ship_address_attributes_country_id").val(), 17)
}
},
"order[ship_address_attributes][address1]": {
required: true,
maxlength: function(element) {
return maxCharLimit($("#order_ship_address_attributes_country_id").val(), 32)
}
},
"order[ship_address_attributes][address2]": {
maxlength: function(element) {
return maxCharLimit($("#order_ship_address_attributes_country_id").val(), 32)
}
},
"order[ship_address_attributes][zipcode]": {
maxlength: function(element) {
return maxCharLimit($("#order_ship_address_attributes_country_id").val(), 10)
}
}
}
});
getCountryId = function(region) {
return $('#' + region + 'country select').val();
};
isCountryUsOrCa = function(country_id) {
return ["38", "232"].includes(country_id)
}
maxCharLimit = function(country_id, limit) {
if (["38", "232"].includes(country_id)) {
return limit;
} else {
return 255;
}
};
Spree.updateState = function(region) {
var countryId;
var cityId
countryId = getCountryId(region);
if (countryId != null) {
if (region == 'b') {
cityId = '#order_bill_address_attributes_city'
countryInputId = "#order_bill_address_attributes_country_id"
} else {
cityId = '#order_ship_address_attributes_city'
countryInputId = "#order_ship_address_attributes_country_id"
}
fill_cities($(countryInputId).find('option:selected').attr('iso_code'), cityId)
if (Spree.Checkout[countryId] == null) {
return $.get(Spree.routes.states_search, {
country_id: countryId
}, function(data) {
Spree.Checkout[countryId] = {
states: data.states,
states_required: data.states_required
};
return Spree.fillStates(Spree.Checkout[countryId], region);
});
} else {
return Spree.fillStates(Spree.Checkout[countryId], region);
}
}
};
fill_cities = function(country_code, cityId) {
$.ajax({
url: "/api/fetch_cities_from_aramex_address",
type: 'GET',
dataType: "json",
data: {
country_code: country_code
},
success: function(data) {
$(cityId).autocomplete({
source: data,
});
},
error: function() {}
});
}
Spree.fillStates = function(data, region) {
var selected, stateInput, statePara, stateSelect, stateSpanRequired, states, statesRequired, statesWithBlank;
statesRequired = data.states_required;
states = data.states;
statePara = $('#' + region + 'state');
stateSelect = statePara.find('select');
stateInput = statePara.find('input');
stateSpanRequired = statePara.find('[id$="state-required"]');
if (states.length > 0) {
selected = parseInt(stateSelect.val());
stateSelect.html('');
statesWithBlank = [{
name: '',
id: ''
}].concat(states);
$.each(statesWithBlank, function(idx, state) {
var opt;
opt = ($(document.createElement('option'))).attr('value', state.id).html(state.name);
if (selected === state.id) {
opt.prop('selected', true);
}
return stateSelect.append(opt);
});
stateSelect.prop('disabled', false).show();
stateInput.hide().prop('disabled', true);
statePara.show();
stateSpanRequired.show();
if (statesRequired) {
stateSelect.addClass('required');
}
stateSelect.removeClass('hidden');
return stateInput.removeClass('required');
} else {
stateSelect.hide().prop('disabled', true);
stateInput.show();
if (statesRequired) {
stateSpanRequired.show();
stateInput.addClass('required');
} else {
stateInput.val('');
stateSpanRequired.hide();
stateInput.removeClass('required');
}
statePara.toggle(!!statesRequired);
stateInput.prop('disabled', !statesRequired);
stateInput.removeClass('hidden');
return stateSelect.removeClass('required');
}
};
($('#bcountry select')).change(function() {
$('label.error').hide()
if (isCountryUsOrCa($("#order_bill_address_attributes_country_id").val())) {
$('#checkout_form_address').valid();
}
return Spree.updateState('b');
});
($('#scountry select')).change(function() {
$('label.error').hide()
if (isCountryUsOrCa($("#order_bill_address_attributes_country_id").val())) {
$('#checkout_form_address').valid();
}
return Spree.updateState('s');
});
Spree.updateState('b');
order_use_billing = $('input#order_use_billing');
order_use_billing.change(function() {
return update_shipping_form_state(order_use_billing);
});
update_shipping_form_state = function(order_use_billing) {
if (order_use_billing.is(':checked')) {
($('#shipping .inner')).hide();
return ($('#shipping .inner input, #shipping .inner select')).prop('disabled', true);
} else {
($('#shipping .inner')).show();
($('#shipping .inner input, #shipping .inner select')).prop('disabled', false);
return Spree.updateState('s');
}
};
return update_shipping_form_state(order_use_billing);
}
};
return Spree.onAddress();
});
Why am I am facing the issue mentioned at the top?

maybe you can try this:
namespace :spree do
namespace :api do
resources :aramex_address, only: [] do
get :validate_address_with_aramex
get :fetch_cities_from_aramex_address
end
end
end
words of advice I think it's better if you rename the fetch_cities_from_aramex_address with show method, so it still follow rails convenience

well here is the issue of constant lookup. Your constant that is AramexAddressValidator is missing because of the way you wrote class Spree::Api::AramexAddressController < ApplicationController
So if your module AramexAddressValidator is inside some scope use that scope also while including this module
For Ex. if its inside spree/aramex_address_validator use
include Spree::AramexAddressValidator

Related

Select2 group with Infinite scroll

I am using select2 group option with infinite scroll and data are coming by Ajax calling per page 10. Here is some problem arises, suppose user 1 has 15 data and user 2 has 15 data, at first 10 data are coming from user 1 and in next page 10 (5 data for user1 and 5 data for user2). no problem for data getting but the problem is user 1 group showing double. How can I prevent double display to my select2 options group? Has there any way to make an option group again?
HTML CODE
<div class="container">
<form id="frm">
<h1>Solution 1</h1>
<div class="row">
<div class="col-4">
<div class="form-group">
<label for="tagInput">Get data by ajax calling</label>
<select class="form-control" id="pictures_tag_input">
</select>
<small class="form-text text-muted"><p class="text-info">Infinite Scroll</p></small>
</div>
</div>
</div>
</form>
</div>
JS CODE
$(document).ready(function() {
// solution 1
//example.com/articles?page[number]=3&page[size]=1
http: $("#pictures_tag_input").select2({
placeholder: "Search for options",
ajax: {
url: "https://jsonplaceholder.typicode.com/users/1/todos",
dataType: "json",
global: false,
cache: true,
delay: 250,
minimumInputLength: 2,
data: function(params) {
// console.log(params.page || 1);
return {
q: params.term, // search term
_page: params.page || 1,
_limit: 10 // page size
};
},
processResults: function(data, params) {
params.page = params.page || 1;
var datx = getNestedChildren(data);
// console.log(datx);
return {
results: datx,
pagination: {
more: true
}
};
} //end of process results
} // end of ajax
});
function getNestedChildren(list) {
var roots = [];
for (i = 0; i < list.length; i += 1) {
node = list[i];
if (roots.length === 0) {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
roots.push(obj);
} else {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
var rootArray = $.map(roots, function(val, i) {
var vl = "User " + node.userId;
if (val.text === vl) return val;
else return undefined;
});
if (rootArray.length > 0) {
var obj1 = {
id: node.id,
text: node.title
};
rootArray[0].children.push(obj1);
} else {
roots.push(obj);
}
}
}
return roots;
}
});
Demo
https://codepen.io/mdshohelrana/pen/MLVZEG
Just try to use the following code
templateResult: function(data) {
if (typeof data.children != 'undefined') {
$(".select2-results__group").each(function() {
if (data.text == $(this).text()) {
return data.text = '';
}
});
}
return data.text;
}
NOTE: Need to group from server side, Other wise you have to make master details from client side.
the accepted answer didn't work for me and I don't see why should that work. A return in the $.each will not return from the templateResult() function.
Here is an approach that worked for me.
It is not necessary to build a nested list by getNestedChildren(list) on javascript side. It is much easier to build it on server side instead.
The appearance of search results in the dropdown (options and the optgroups) can be customized by using the templateResult option. I removed the duplicated optgroups and labels by this option.
check the templateResult: formatOptions, part of the code
$(document).ready(function() {
$("#pictures_tag_input").select2({
placeholder: "Search for options",
templateResult: formatOptions,
ajax: {
url: "https://jsonplaceholder.typicode.com/users/1/todos",
dataType: "json",
global: false,
cache: true,
delay: 250,
minimumInputLength: 2,
data: function(params) {
return {
q: params.term,
_page: params.page || 1,
_limit: 10
};
},
processResults: function(data, params) {
params.page = params.page || 1;
return {
results: data,
pagination: {
more: true
}
};
} //end of process results
} // end of ajax
});
function formatOptions(item, container, $el) {
// optgroups section
if (item.children && item.children.length > 0) {
// don't format the repeated optgroups!
if ($(".select2-results__group").text() === item.text) {
return;
}
if ($('[aria-label="' + item.text + '"]').length > 0) {
return;
}
// the first occasion of the given optgroup
return $el;
}
// options section
// here you can implement your own logic
// if you want to customise the output of the options
$el.addClass('something-special-result result');
return $el;
}
});
maybe the problem is a source of a data
You call user 1 .... server return a 1
You call user 2 .... server return a 1
You call user 3 .... server return a 2
You call user 4 .... server return a 2
You call user 5 .... server return a 3
You call user 6 .... server return a 3
curent_user = 1;
$(document).ready(function() {
http: $("#pictures_tag_input").select2({
placeholder: "Search for options",
ajax: {
url: "https://jsonplaceholder.typicode.com/users/1/todos",
dataType: "json",
global: false,
cache: false,
minimumInputLength: 2,
data: function(params) {
console.log("params",params || 1);
return {
q: params.term, // search term
_page: curent_user,
_limit: 10 // page size
};
},
processResults: function(data, params) {
curent_user += 2;
var datx = getNestedChildren(data);
console.log("data: ", data);
return {
results: datx,
pagination: {
more: true
}
};
} //end of process results
} // end of ajax
});
function getNestedChildren(list) {
var roots = [];
for (i = 0; i < list.length; i += 1) {
node = list[i];
if (roots.length === 0) {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
roots.push(obj);
} else {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
var rootArray = $.map(roots, function(val, i) {
var vl = "User " + node.userId;
if (val.text === vl) return val;
else return undefined;
});
if (rootArray.length > 0) {
var obj1 = {
id: node.id,
text: node.title
};
rootArray[0].children.push(obj1);
} else {
roots.push(obj);
}
}
}
return roots;
}
});
so if you skip a one step
You call user 1 .... server return a 1
You call user 3 .... server return a 2
You call user 5 .... server return a 3
I just found a better solution which does not result in a (duplicated) optgroup being rendered as an empty option:
processResults: function( json, params ){
setTimeout( function() {
var $prevOptions = false;
var $prevGroup = false;
// loop
$('.select2-results__option[role="group"]').each(function(){
// vars
var $options = $(this).children('ul');
var $group = $(this).children('strong');
// compare to previous
if( $prevGroup && $prevGroup.text() === $group.text() ) {
$prevOptions.append( $options.children() );
$(this).remove();
return;
}
// update vars
$prevOptions = $options;
$prevGroup = $group;
});
}, 1 );
return json;
}
Advanced Custom Fields uses the exact same code for their WordPress plugin in order to fix this issue, ajax-load and group posts from different post-types.

Integrating Braintree Client token into braintree.client.create

I have limited experience working with JavaScript and am attempting to pass a client token generated on my server into the braintree client create of Braintree's javascript code. I do not know how to pass the ClientToken out of the jQuery post and into the authorization section of braintree.client.create. A portion of my code is below:
<script src="https://js.braintreegateway.com/web/3.34.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.34.0/js/hosted-fields.min.js"></script>
<script src="https://www.paypalobjects.com/api/checkout.js" data-version-4></script>
<script src="https://js.braintreegateway.com/web/3.34.0/js/paypal-checkout.min.js"></script>
<script>
var form = document.querySelector('#register-form');
var submit = document.querySelector('input[type="submit"]');
var ClientToken;
var authPosting = jQuery.post('/path/to/php_scripts/getClientToken.php');
authPosting.done(function( data ) {
ClientToken = data;
console.log(ClientToken);
});
braintree.client.create({
authorization: 'ClientToken'
}, function (clientErr, clientInstance) {
if (clientErr) {
console.error(clientErr);
return;
}
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '15px',
'font-family': 'roboto, verdana, sans-serif',
'font-weight': 'lighter',
'color': 'black'
},
':focus': {
'color': 'black'
},
'.valid': {
'color': 'black'
},
'.invalid': {
'color': 'black'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: ''
},
cvv: {
selector: '#cvv',
placeholder: ''
},
expirationDate: {
selector: '#expiration-date',
placeholder: 'MM/YY'
},
postalCode: {
selector: '#postal-code',
placeholder: ''
}
}
}, function(err, hostedFieldsInstance) {
if (err) {
console.error(err);
return;
}
function findLabel(field) {
return jQuery('.hosted-field--label[for="' + field.container.id + '"]');
}
hostedFieldsInstance.on('focus', function (event) {
var field = event.fields[event.emittedBy];
findLabel(field).addClass('label-float').removeClass('filled');
jQuery(".error-msg").hide();
});
// Emulates floating label pattern
hostedFieldsInstance.on('blur', function (event) {
var field = event.fields[event.emittedBy];
var label = findLabel(field);
if (field.isEmpty) {
label.removeClass('label-float');
} else if (field.isValid) {
label.addClass('filled');
} else {
label.addClass('invalid');
}
});
hostedFieldsInstance.on('empty', function (event) {
var field = event.fields[event.emittedBy];
findLabel(field).removeClass('filled').removeClass('invalid');
});
hostedFieldsInstance.on('validityChange', function (event) {
var field = event.fields[event.emittedBy];
var label = findLabel(field);
if (field.isPotentiallyValid) {
label.removeClass('invalid');
} else {
label.addClass('invalid');
}
});
//Submit function
jQuery("#register-form").validate({
submitHandler: function(form) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (err, payload) {
if (err) {
if (err.code === 'HOSTED_FIELDS_FIELDS_EMPTY') {
var msg = "All credit card fields are required.";
jQuery(".error-msg").text(msg);
jQuery(".error-msg").show();
} else if (err.code === 'HOSTED_FIELDS_FIELDS_INVALID') {
var invalidFields = err.details.invalidFieldKeys;
invalidFields.forEach(function (field) {
if (field == "number") {
var myid = "card-number";
var myobj = "credit card number";
} else if (field == "expirationDate") {
var myid = "expiration-date";
var myobj = "expiration date";
}
jQuery("#" + myid).append("<div class='error-msg'>Please enter a valid " + myobj + ".</div>");
});
} else {
var msg = "There was an error on the form."
alert (msg);
}
return;
}
vfirst = jQuery( "input#firstname" ).val(),
vlast = jQuery( "input#lastname" ).val(),
vemail = jQuery( "input#email" ).val(),
vproof = jQuery( "input[name='proof']" ).val(),
vprice = jQuery( "tr#total td.price" ).data('price'),
vcourse = 1950,
vnonce = 'fake-valid-nonce',
url = '/path/to/php_scripts/placeOrder.php';
// This is where you would submit payload.nonce to your server
// alert('Submit your cc nonce to your server here!');
});
//form.submit();
}
});
});
....more code below....
</script>
Any suggestions would be greatly appreciated.
Answering my own question:
CLIENT_AUTHORIZATION = jQuery.parseJSON(jQuery.ajax({
url: "/path/to/php_scripts/TokenScript.php",
dataType: "json",
async: false
}).responseText);
braintree.client.create({
authorization: CLIENT_AUTHORIZATION.token
}, function (clientErr, clientInstance) {
if (clientErr) {
console.error(clientErr);
return;
} code continues.....
The CLIENT_AUTHORIZATION.token is whatever the token was named on the client authorization script.

Calling toastr makes the webpage jump to top of page

I'm seeking a solution for the toastr.js "error" that causes the webpage, if scrolled down, to jump up again when a new toastr is displayed
GitHub page containing the script
I've tried to change the top to auto, but that wasn't an accepted parameter, because nothing showed up then.
Isn't there any way to make it appear where the mouse is at the moment?
.toast-top-center {
top: 12px;
margin: 0 auto;
left: 43%;
}
this has the calling code:
<p><span style="font-family:'Roboto','One Open Sans', 'Helvetica Neue', Helvetica, sans-serif;color:rgb(253,253,255); font-size:16px ">
xxxxxxxxxxxxx
</span></p>
This is the function code:
<script type='text/javascript'> function playclip() {
toastr.options = {
"debug": false,
"positionClass": "toast-top-center",
"onclick": null,
"fadeIn": 800,
"fadeOut": 1000,
"timeOut": 5000,
"extendedTimeOut": 1000
}
toastr["error"]("This link is pointing to a page that hasn't been written yet,</ br> sorry for the inconvenience?"); } </script>
And this is the script itself:
/*
* Toastr
* Copyright 2012-2015
* Authors: John Papa, Hans Fjällemark, and Tim Ferrell.
* All Rights Reserved.
* Use, reproduction, distribution, and modification of this code is subject to the terms and
* conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
*
* ARIA Support: Greta Krafsig
*
* Project: https://github.com/CodeSeven/toastr
*/
/* global define */
(function (define) {
define(['jquery'], function ($) {
return (function () {
var $container;
var listener;
var toastId = 0;
var toastType = {
error: 'error',
info: 'info',
success: 'success',
warning: 'warning'
};
var toastr = {
clear: clear,
remove: remove,
error: error,
getContainer: getContainer,
info: info,
options: {},
subscribe: subscribe,
success: success,
version: '2.1.3',
warning: warning
};
var previousToast;
return toastr;
////////////////
function error(message, title, optionsOverride) {
return notify({
type: toastType.error,
iconClass: getOptions().iconClasses.error,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function getContainer(options, create) {
if (!options) { options = getOptions(); }
$container = $('#' + options.containerId);
if ($container.length) {
return $container;
}
if (create) {
$container = createContainer(options);
}
return $container;
}
function info(message, title, optionsOverride) {
return notify({
type: toastType.info,
iconClass: getOptions().iconClasses.info,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function subscribe(callback) {
listener = callback;
}
function success(message, title, optionsOverride) {
return notify({
type: toastType.success,
iconClass: getOptions().iconClasses.success,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function warning(message, title, optionsOverride) {
return notify({
type: toastType.warning,
iconClass: getOptions().iconClasses.warning,
message: message,
optionsOverride: optionsOverride,
title: title
});
}
function clear($toastElement, clearOptions) {
var options = getOptions();
if (!$container) { getContainer(options); }
if (!clearToast($toastElement, options, clearOptions)) {
clearContainer(options);
}
}
function remove($toastElement) {
var options = getOptions();
if (!$container) { getContainer(options); }
if ($toastElement && $(':focus', $toastElement).length === 0) {
removeToast($toastElement);
return;
}
if ($container.children().length) {
$container.remove();
}
}
// internal functions
function clearContainer (options) {
var toastsToClear = $container.children();
for (var i = toastsToClear.length - 1; i >= 0; i--) {
clearToast($(toastsToClear[i]), options);
}
}
function clearToast ($toastElement, options, clearOptions) {
var force = clearOptions && clearOptions.force ? clearOptions.force : false;
if ($toastElement && (force || $(':focus', $toastElement).length === 0)) {
$toastElement[options.hideMethod]({
duration: options.hideDuration,
easing: options.hideEasing,
complete: function () { removeToast($toastElement); }
});
return true;
}
return false;
}
function createContainer(options) {
$container = $('<div/>')
.attr('id', options.containerId)
.addClass(options.positionClass);
$container.appendTo($(options.target));
return $container;
}
function getDefaults() {
return {
tapToDismiss: true,
toastClass: 'toast',
containerId: 'toast-container',
debug: false,
showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery
showDuration: 300,
showEasing: 'swing', //swing and linear are built into jQuery
onShown: undefined,
hideMethod: 'fadeOut',
hideDuration: 1000,
hideEasing: 'swing',
onHidden: undefined,
closeMethod: false,
closeDuration: false,
closeEasing: false,
closeOnHover: true,
extendedTimeOut: 1000,
iconClasses: {
error: 'toast-error',
info: 'toast-info',
success: 'toast-success',
warning: 'toast-warning'
},
iconClass: 'toast-info',
positionClass: 'toast-top-right',
timeOut: 5000, // Set timeOut and extendedTimeOut to 0 to make it sticky
titleClass: 'toast-title',
messageClass: 'toast-message',
escapeHtml: false,
target: 'body',
closeHtml: '<button type="button">×</button>',
closeClass: 'toast-close-button',
newestOnTop: true,
preventDuplicates: false,
progressBar: false,
progressClass: 'toast-progress',
rtl: false
};
}
function publish(args) {
if (!listener) { return; }
listener(args);
}
function notify(map) {
var options = getOptions();
var iconClass = map.iconClass || options.iconClass;
if (typeof (map.optionsOverride) !== 'undefined') {
options = $.extend(options, map.optionsOverride);
iconClass = map.optionsOverride.iconClass || iconClass;
}
if (shouldExit(options, map)) { return; }
toastId++;
$container = getContainer(options, true);
var intervalId = null;
var $toastElement = $('<div/>');
var $titleElement = $('<div/>');
var $messageElement = $('<div/>');
var $progressElement = $('<div/>');
var $closeElement = $(options.closeHtml);
var progressBar = {
intervalId: null,
hideEta: null,
maxHideTime: null
};
var response = {
toastId: toastId,
state: 'visible',
startTime: new Date(),
options: options,
map: map
};
personalizeToast();
displayToast();
handleEvents();
publish(response);
if (options.debug && console) {
console.log(response);
}
return $toastElement;
function escapeHtml(source) {
if (source == null) {
source = '';
}
return source
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
}
function personalizeToast() {
setIcon();
setTitle();
setMessage();
setCloseButton();
setProgressBar();
setRTL();
setSequence();
setAria();
}
function setAria() {
var ariaValue = '';
switch (map.iconClass) {
case 'toast-success':
case 'toast-info':
ariaValue = 'polite';
break;
default:
ariaValue = 'assertive';
}
$toastElement.attr('aria-live', ariaValue);
}
function handleEvents() {
if (options.closeOnHover) {
$toastElement.hover(stickAround, delayedHideToast);
}
if (!options.onclick && options.tapToDismiss) {
$toastElement.click(hideToast);
}
if (options.closeButton && $closeElement) {
$closeElement.click(function (event) {
if (event.stopPropagation) {
event.stopPropagation();
} else if (event.cancelBubble !== undefined && event.cancelBubble !== true) {
event.cancelBubble = true;
}
if (options.onCloseClick) {
options.onCloseClick(event);
}
hideToast(true);
});
}
if (options.onclick) {
$toastElement.click(function (event) {
options.onclick(event);
hideToast();
});
}
}
function displayToast() {
$toastElement.hide();
$toastElement[options.showMethod](
{duration: options.showDuration, easing: options.showEasing, complete: options.onShown}
);
if (options.timeOut > 0) {
intervalId = setTimeout(hideToast, options.timeOut);
progressBar.maxHideTime = parseFloat(options.timeOut);
progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime;
if (options.progressBar) {
progressBar.intervalId = setInterval(updateProgress, 10);
}
}
}
function setIcon() {
if (map.iconClass) {
$toastElement.addClass(options.toastClass).addClass(iconClass);
}
}
function setSequence() {
if (options.newestOnTop) {
$container.prepend($toastElement);
} else {
$container.append($toastElement);
}
}
function setTitle() {
if (map.title) {
var suffix = map.title;
if (options.escapeHtml) {
suffix = escapeHtml(map.title);
}
$titleElement.append(suffix).addClass(options.titleClass);
$toastElement.append($titleElement);
}
}
function setMessage() {
if (map.message) {
var suffix = map.message;
if (options.escapeHtml) {
suffix = escapeHtml(map.message);
}
$messageElement.append(suffix).addClass(options.messageClass);
$toastElement.append($messageElement);
}
}
function setCloseButton() {
if (options.closeButton) {
$closeElement.addClass(options.closeClass).attr('role', 'button');
$toastElement.prepend($closeElement);
}
}
function setProgressBar() {
if (options.progressBar) {
$progressElement.addClass(options.progressClass);
$toastElement.prepend($progressElement);
}
}
function setRTL() {
if (options.rtl) {
$toastElement.addClass('rtl');
}
}
function shouldExit(options, map) {
if (options.preventDuplicates) {
if (map.message === previousToast) {
return true;
} else {
previousToast = map.message;
}
}
return false;
}
function hideToast(override) {
var method = override && options.closeMethod !== false ? options.closeMethod : options.hideMethod;
var duration = override && options.closeDuration !== false ?
options.closeDuration : options.hideDuration;
var easing = override && options.closeEasing !== false ? options.closeEasing : options.hideEasing;
if ($(':focus', $toastElement).length && !override) {
return;
}
clearTimeout(progressBar.intervalId);
return $toastElement[method]({
duration: duration,
easing: easing,
complete: function () {
removeToast($toastElement);
clearTimeout(intervalId);
if (options.onHidden && response.state !== 'hidden') {
options.onHidden();
}
response.state = 'hidden';
response.endTime = new Date();
publish(response);
}
});
}
function delayedHideToast() {
if (options.timeOut > 0 || options.extendedTimeOut > 0) {
intervalId = setTimeout(hideToast, options.extendedTimeOut);
progressBar.maxHideTime = parseFloat(options.extendedTimeOut);
progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime;
}
}
function stickAround() {
clearTimeout(intervalId);
progressBar.hideEta = 0;
$toastElement.stop(true, true)[options.showMethod](
{duration: options.showDuration, easing: options.showEasing}
);
}
function updateProgress() {
var percentage = ((progressBar.hideEta - (new Date().getTime())) / progressBar.maxHideTime) * 100;
$progressElement.width(percentage + '%');
}
}
function getOptions() {
return $.extend({}, getDefaults(), toastr.options);
}
function removeToast($toastElement) {
if (!$container) { $container = getContainer(); }
if ($toastElement.is(':visible')) {
return;
}
$toastElement.remove();
$toastElement = null;
if ($container.children().length === 0) {
$container.remove();
previousToast = undefined;
}
}
})();
});
}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
if (typeof module !== 'undefined' && module.exports) { //Node
module.exports = factory(require('jquery'));
} else {
window.toastr = factory(window.jQuery);
}
}));
Change your code to be like this:
<a href="#" style="color: rgb(255,0,0,0)" onclick="playclip(); return false;" >xxxxxxxxxxxxx </a>
However, I would reconsider using this type of javascript invokation. Take a look at this "javascript:void(0);" vs "return false" vs "preventDefault()"

Error with Knockout-validation validation

I have a collection of text editors in a form, validating them with knockout-validation. When I submit the form, only a few of them show validation errors, although all of them should pass. This issue only occurs with these two fields shown in error, however I have checked and double checked and all of the wiring for all of these is set up correctly, e.g. there is no reason I can see that these two fields should be different from the others that do not exhibit this issue.
I'm hoping that someone with more experience than I in the internals of knockout-validation could point me to something that may be alternately tripping the validation of these. I have it set up to validate the text when a user clicks outside of the editor, and that works fine. The issue below only appears when a user clicks submit, and other fields on the page (besides the editors shown here) have an issue. In other words, if every single field is valid, the form is submitted. If any fields OTHER than those shown below have issues, the false positive is shown.
Adding Code examples:
Creating the validator and extending the observables. Note the ones depicted in the image are configured identically to those not being marked invalid:
ko.validation.rules['notIn'] = {
validator: function (value, params) {
var thisValidator = baseCompare.slice();
if (Array.isArray(params))
params.forEach(function (element, index, array) {
thisValidator.push(element);
});
else
thisValidator.push(params);
return thisValidator.indexOf(value) == -1;
},
message: 'Please enter a unique value'
};
ko.validation.registerExtenders();
self.setupValidation = function () {
self.title.extend({
required: true,
notIn: {
message: 'Please enter a unique title',
params: self.froalaBasify('New Change Request')
}
});
self.domain.extend({ required: true, notEqual: '0' }).extend({ notify: 'always' });
self.priority.extend({ required: true, notEqual: '0' }).extend({ notify: 'always' });
self.securityClassification.extend({ required: true, notEqual: '0' }).extend({ notify: 'always' });
self.impact.extend({ required: true, notEqual: '0' }).extend({ notify: 'always' });
self.customersAffected.extend({
required: true,
notIn: {
message: 'Please describe the customers affected',
params: self.froalaBasify(CRHelper.ValidationPlaceholders.customersAffected)
}
});
//False Positive
self.justification.extend({
required: true,
notIn: {
message: 'Please provide a justification for the request',
params: self.froalaBasify(CRHelper.ValidationPlaceholders.justification)
}
});
self.scope.extend({
required: true,
notIn: {
message: 'Please describe the systems affected by this change',
params: self.froalaBasify(CRHelper.ValidationPlaceholders.scope)
}
});
//False Positive
self.validationProcess.extend({
required: true,
notIn: {
message: 'Please enter a validation plan for this change',
params: self.froalaBasify(CRHelper.ValidationPlaceholders.validationProcess)
}
});
self.description.extend({
required: true,
notIn: {
message: 'Please enter a description',
params: self.froalaBasify(CRHelper.ValidationPlaceholders.description)
}
});
self.deadlineRequested.extend({
required: true,
dateGreaterThanToday: {
onlyIf: function () {
//var isTrue = $('#ddTransition option:selected').text() == "Delete Request";
//return !(isTrue);
return self.id === 0;
}
}
});
//self.deadlineRequested.extend({
// required: true}).extend({ notify: 'always' });
};
And then here is the submit handler:
self.submitChangeRequest = function () {
var validated = ko.validatedObservable(self.ChangeRequest());
var transition = "";
transition = $('#ddTransition option:selected').text();
var disableValidation = false;
if (transition == "Delete Request")
disableValidation = true;
var formIsValid = validated.isValid();
if (CRHelper.ConfirmSubmitIfDeleteChosen() && (disableValidation || formIsValid)) {
$('.submitter').prop('disabled', true);
var formUsage = $("[id*='hdnFormUsage']")[0].value;
var ajax = new utils.AjaxFormatter(
{
type: "POST",
data: ko.toJSON(self.ChangeRequest()),
url: DomainUtil.baseUrl + "api/ChangeRequests/",
contentType: "application/json",
headers: { 'formUsage': formUsage }
},
function (updatedModel, status, request) {
var updateMessage = formUsage == 'execute' ? 'executed' : (formUsage == 'new' ? 'saved new' : transition == 'Delete Request' ? 'deleted' : 'updated');
var $msg = $("[id*='StatusMessage']");
$msg.html('Successfully ' + updateMessage + ' Change Request ' + updatedModel.id);
$msg.addClass('alertGo').show().fadeOut({ duration: 8000, easing: 'easeInQuint', complete: function () { $msg.removeClass('alertGo'); } });
if (formUsage == 'new') {
changeRequestViewModel.SetChangeRequest(new ChangeRequestModel());
CRHelper.SetEditors('new');
} else {
changeRequestViewModel.SetChangeRequest(new ChangeRequestModel(updatedModel));
CRHelper.SetEditors('edit');
}
if ((formUsage == 'editState' || formUsage == 'edit') && updatedModel.activeStateId === 8)
$('#approvedByItems').toggle();
if (formUsage === 'execute' || (formUsage === 'editState' && updatedModel.activeStateName == 'Approved')) {
$('#divExecuted').toggle();
$('#divChangeState').hide();
}
try {
$('#dtpDeadlineRequested').datepicker();
$('#dtpDeadlineRequested').datepicker('refresh');
} catch (e) {
$('#dtpDeadlineRequested').datepicker('destroy');
$('#dtpDeadlineRequested').datepicker();
}
if (transition == "Delete Request") {
$('.submitter').prop('disabled', true);
$('#divSubmitButtons #buttonSet').toggle();
$('#divSubmitButtons #returnLink').toggle();
$('#formFields').prop('disabled', true);
}
if (updatedModel.activeStateName == 'Executed' || updatedModel.activeStateName == 'Deleted' || updatedModel.activeStateName == 'Approved') {
$('#formFields').prop('disabled', true);
$('.editor').unbind('dblclick');
$('#divSubmitButtons #buttonSet').toggle();
$('#divSubmitButtons #returnLink').toggle();
}
},
function (request, status, error) {
//update message text
var $msg = $("[id*='StatusMessage']");
var updateMessage = formUsage == 'execute' ? 'executing' : (formUsage == 'new' ? 'saving' : 'updating');
$msg.html('Failure ' + updateMessage + ': ' + error);
$msg.addClass('alertStop').show().fadeOut({ duration: 8000, easing: 'easeInQuint', complete: function () { $msg.removeClass('alertStop'); } });
},
function () {
if (pwChooser)
pwChooser.selectedPW([]);
//hide spinny
}
);
utils.tokenizedAjax(ajax);
} else {
ko.validation.group(self.ChangeRequest()).showAllMessages();
var $msg = $("[id*='StatusMessage']");
$msg.html('Check your entries and try again')
.addClass('alertCaution')
.show()
.fadeOut({
duration: 2000,
easing: 'easeInQuint',
complete: function () {
$msg.removeClass('alertCaution');
}
});
}
}
And then here is where I declare the observables on the model - all pretty straightforward:
self.description = ko.observable(crData.description || '');
self.domain = ko.observable(crData.domain || '');
self.id = crData.id || 0;
self.impact = ko.observable(crData.impact || '');
self.includesPolicyWaiver = ko.observable(crData.includesPolicyWaiver || false);
self.justification = ko.observable(crData.justification || '');
self.priority = ko.observable(crData.priority || '');
self.scope = ko.observable(crData.scope || '');
self.securityClassification = ko.observable(crData.securityClassification || '');
self.title = ko.observable(crData.title || 'New Change Request');
self.validationProcess = ko.observable(crData.validationProcess || '');
I've triple-checked the code and there is nowhere else that the values behind these two form fields are being explicitly changed. This looks to me like a global-type value is getting toggled then not reset or something.
This is interesting: The only change I made was the order of the elements in the html. Doing the same test/submission, now the other fields that are in the 2nd and 4th position are showing the false positive. The ones that showed it originally validate as expected.

Vuejs + typeahead this - undefined

I am trying to do a form with autocomplete. When a user type a part of a name typeahead suggests people that are in our database. If the user selects one of the suggestion I want to the full form to be filled from the autosuggestion.
Everything works but all my attempts to assign to the data object the value from the suggestion. When I log the this.newReviewer I get this is undefined... here is my code:
var Vue = require('vue')
var validator = require('vue-validator')
var resource = require('vue-resource')
Vue.use(validator)
Vue.use(resource)
var token = $('#token').attr('value');
Vue.http.headers.common['X-CSRF-TOKEN'] = token;
new Vue({
el:'#invite',
data: {
newReviewer: {
title:'',
last_name:'',
first_name:'',
email:'',
ers_id:'null'
},
reviewers: {},
quantity: {},
submitted: false,
},
ready: function(){
this.fetchInvitedReviewers();
console.log(this.newReviewer);
var ersContacts = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: 'api/search/%QUERY',
wildcard: '%QUERY'
}
});
$('#last_name')
.typeahead({
minLength: 2,
highlight: true,
hint: true
},
{
displayKey: 'last_name',
templates: {
suggestion: function(ersContacts) {
return (
'<div>' +
'<h3>'
+ ersContacts.title + ' '
+ ersContacts.first_name + ' '
+ ersContacts.last_name
+ '</h3>'
+ '<p class="uk-text-muted">'
+ ersContacts.city + ' '
+ ersContacts.country
+ '</p>' +
'</div>'
)
}
},
source: ersContacts
})
.on('typeahead:select', function(e, suggestion){
console.log(this.newReviewer);
this.newReviewer.last_name.push(suggestion.last_name);
this.newReviewer.email.push(suggestion.email);
})
.bind(this);
},
computed:{
//errors: function() {
// for(var key in this.newReviewer) {
// if (! this.newReviewer[key]) return true;
// }
// return false;
//}
},
methods: {
fetchInvitedReviewers: function() {
this.$http.get('api', function(response) {
this.$set('reviewers', response.reviewers);
this.$set('quantity', response.quantity);
});
},
onSubmitForm: function(e){
e.preventDefault();
var reviewer = this.newReviewer;
//add new reviewer to reviewers array
this.reviewers.push(reviewer);
//reset input values
this.newReviewer = {title:'', last_name: '', first_name: '', email:'', ers_id:''}
//substract one to the quantity
this.quantity -= 1 ;
//sent post ajax request
this.$http.post('api/store', reviewer)
//show success and count how many left
if(this.quantity <= 0) {
this.submitted = true;
}
}
},
validator: {
validates: {
email: function (val) {
return /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(val)
}
}
}
});
found...
Sorry guys. this
})
.bind(this);
should have been
}
.bind(this));

Categories

Resources