How do I get element related to active input in jQuery UI Autocomplete? - jquery-ui-autocomplete

I'm trying to pass a custom form attribute (category) through jQuery UI Autocomplete to use in a product search. The form looks like <form id="BulkOrderForm" category="samplecategory"><input></input>...</form> and contains inputs that use the autocomplete script. There can be several forms on each page, so I need to be able to get the category value from the form that contains the active input field.
Here's my source:
function autocomplete() {
$("input.wcbulkorderproduct").autocomplete({
element: function(){
var element = $('form#BulkOrderForm').attr('category');
return element;
},
source: function(request, response, element){
$.ajax({
url: WCBulkOrder.url+'?callback=?&action='+acs_action+'&_wpnonce='+WCBulkOrder.search_products_nonce,
dataType: "json",
data: {
term: request.term,
category: element
},
success: function(data) {
response(data);
}
});
}
});
}
Any thoughts on how this can be acheived?

If I'm understanding correctly, you're trying to use the active input's parent form in the ajax request. Here's a way to achieve that:
Html:
<form data-category="form1">
<input type="text" />
<input type="text" />
</form>
<form data-category="formB">
<input type="text" />
<input type="text" />
</form>
JS:
$('form').each(function () {
var category = $(this).data('category');
$(this).find('input').autocomplete({
source: function (request, response) {
response([category]);
}
});
});
Instead of using autocomplete on a catch-all selector that gets inputs from all forms, first select the forms themselves. For each one, extract the category, then select all child inputs and call autocomplete on that result. Then you can use the category variable in the ajax call - in the example I'm simply passing it to the callback to display.
http://jsfiddle.net/Fw2QA/

I'll give you another solution, you can lookup the parent form of the active input, and extract the attribute from it. Because I don't know if this category in your form is dynamic or no, or either if you can control all of the process involved in your code, I'll give you a more generic solution, although if that attribute is dynamic "Turch" solution is way better than mine, by letting the data functionality of jquery handle the attribute changes, if it's static, than you can just do it like this:
function autocomplete() {
var element = $("input.wcbulkorderproduct").autocomplete({
source: function(request, response){
$.ajax({
url: WCBulkOrder.url+'?callback=?&action='+acs_action+'&_wpnonce='+WCBulkOrder.search_products_nonce,
dataType: "json",
data: {
term: request.term,
category: element
},
success: function(data) {
response(data);
}
});
}
}).parents('form').first().attr('category');
//chained call, sets autocomplete, grabs the parent form and the attribute
//which is saved on the variable element, and is used on every call through
//javascript context inheritance.
}
UPDATE
A little example illustrating my suggestion (provided by #Turch > thanks), can be found here.

Related

React with server side variables

I'm rendering out components that have properties with liquid strings. These components are being rendered on the server and picked back up again in the client. Essentially I'm using the DOM as a data store. I'm debating on methods of where to store the data. I need the component to render out valid markup to the server for SEO. But I don't need to pick back up the variable like I am here with this.refs.variantId.getDOMNode(). I could for instance set the variantId to a global client side javascript variable somewhere higher then this code in essence something like var variantId = "{{ product.variants[0].id }}";.
This component will render to a string and be placed within a template file on a server, the server will process that HTML come across the {{ product.variants[0].id }} variable and turn it into something like 1058477584. My component needs to reach into the existing DOM for itself and pull the value out.
var React = require("react");
var $ = require("jquery");
module.exports = React.createClass({
handleSubmit: function(e){
e.preventDefault();
var variantId = this.refs.variantId.getDOMNode().value.trim();
$.ajax({
url: "/cart/add.js",
method: "post",
dataType: "json",
data: {
"id": variantId,
"quantity": this.props.quantity,
},
success: function(data) {
// emit cart added event
}.bind(this),
error: function(xhr, status, err) {
// emit error event (cart added)
}.bind(this)
});
},
getDefaultProps: function(){
return {
quantity: 1,
variantId: "{{ product.variants[0].id }}",
buttonText: "Add to cart"
}
},
render: function() {
return (
<div className="buyButton">
<form action="/cart/add" method="post" encType="multipart/form-data" onSubmit={this.handleSubmit}>
<input type="hidden" name="quantity" value={ this.props.quantity } />
<input type="hidden" name="id" ref="variantId" value={ this.props.variantId } />
<button type="submit" className="btn btn-holstee">{this.props.buttonText}</button>
</form>
</div>
);
}
});
I'm wondering what people think about rendering components with another templating language as a string property. Does it make sense to store that data anywhere else? I don't have access to a server that can store individual pages. It's all templates so multiple data sources need to be handled by one route.
Is there a better way to abstract the liquid out of the component?
Is there a better way to call DOMNodes / update all the props to DOMNodes?
I do something similar in my app. I serialize with JSON and put it in a script tag in the DOM
<script type="application/json" id="preload-notifications">{{json_encode($preload_notifications)}}</script>
Then,
var raw = document.getElementById('preload-messaging');
if (raw === null) {
return ;
}
var data = JSON.parse(raw.text);
I use the flux architecture so it's really simple
this.dispatch('messaging', {
messages: data
});
But you could can inject it as a prop.
React.render(<MessagingContainer messages={data} />, messageDomNode);
Whatever you do, I suggest you don't query the DOM inside a React component. Try to pass stuff as props as much as possible.

AngularJs Directive: Using TemplateURL. Replace element. Add form input. Getting form.input.$error object

Not sure if this is possible but I'm trying, and keep coming up short.
http://plnkr.co/edit/Gcvm0X?p=info
I want a 'E' (element) directive that is replaced with a more complex nested HTML node using the 'templateUrl' feature of directives.
HTML defining the directive (form tag included for complete mental image):
<form id="frm" name="frm">
<ds-frm-input-container
class="col-md-1"
frm-Name="frm"
frm-obj="frm"
input-name="txtFName"
ds-model="user.firstName"></ds-frm-input-container>
</form>
TemplateUrl contents which 'replaces' the above directive 'ds-frm-input-container' HTML element:
<div>
<input
required
ng-minlength=0
ng-maxlength=50
class="form-control"
ng-model="dsModel"
placeholder="{{dsPlaceHolder}}" />
<span ng-if="showErrs" class="label label-danger">FFFFF: {{dsModel}}</span>
</div>
Controller and Directive:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = "Nacho";
$scope.user = {};
$scope.user.firstName = "";
})
.directive('dsFrmInputContainer', function(){
var ddo = {
priority: 0,
restrict: 'AE',
scope:
{
frmName: '#',
inputName: '#',
dsPlaceHolder: '#',
dsModel: '=',
frmObj: '='
},
templateUrl: 'template1.html',
replace: true,
controller: function($scope)
{
$scope.showErrs = true;
},
compile: function compile(ele, attr) {
return {
pre: function preLink(scope, ele, attr, controller)
{
},
post: function postLink(scope, ele, attr, controller)
{
var txt = ele.find('input');
txt.attr('id', scope.inputName);
txt.attr('name', scope.inputName);
//BLUR
txt.bind('blur', function () {
console.log("BLUR BLUR BLUR");
angular.forEach(scope.frmObj.$error, function(value, key){
var type = scope.frmObj.$error[key];
for(var x=0; x < type.length; x++){
console.log(type[x]);
}
});
event.stopPropagation();
event.preventDefault();
});
}
};
},
};
return ddo;
});
The directive replaces just fine and the input element is named just fine. The form object however doesn't include the input element name in the error information. This makes it impossible for me to single out the input element during a 'blur' event that is setup in the directive.
I am doing this trying to reduce the show/hide logic 'noise' in the html for error messages (spans) and it should be reusable.
UPDATE (2014.01.28):
2014.01.28:
Added promises. There is a service that allows validation on button clicks. NOT USING built in angular validation anymore found some compatibility issues with another library (or viceversa).
ORIGINAL:
Here is my form validation directive vision completed (plnkr link below). Completed in concert with the help of the stack overflow community. It may not be perfect but neither are butterfingers but they taste good.
http://plnkr.co/edit/bek8WR?p=info
So here is a link that has the name variables set as expected on the given input form error object. http://plnkr.co/edit/MruulPncY8Nja1BUfohp?p=preview
The only difference is that the inputName is read from the attrs object and is not part of the scope. This is then read before the link function is returned, in the compile phase, to set the template DOM correctly.
I have just spent quite a while trying to sort this problem out, and while this is not exactly what you were looking for, his is my attempt. It uses bootstrap for all the styling, and allows for required and blur validation, but its definitely not finished yet. Any thoughts or advice much appreciated.
https://github.com/mylescc/angular-super-input

jQuery on("submit") does not bind to elements loaded with ajax because it is inside another form

I have a table of items.
The first column of each row contains a checkbox input as part of a form.
The user can click on these checkboxes and then click submit to do bulk actions such as delete items.
I have setup (using jquery) a situation whereby on clicking the 'Add data' link in a row column, a DIFFERENT form is loaded into a third column in which a user can enter item data.
What i then want to do is use ajax to submit this second form. To do this I am using the following code:
$(document).on('submit',".add_form",function(event)
{
event.preventDefault();
var serial=$(this).serialize();
var domain=$('[name=domain]').val();
$.ajax({
url:"portfolio/transactions/"+domain+"/",
type:"post",
data: {data:serial},
success: function(dat){
$('#transactions_div').html(dat);
}
});
});
This however does NOT work, and I believe this is because html does not allow forms within forms. My assumption is that jQuery follows such standards and is getting confused when a second form is loaded into a div which is contained within another set of tags.
Given this, is what I want to do simply not possible?
THanks
Try this:
$(document).on('submit', ".add_form", function (event) {
event.preventDefault();
var serial = $(this).serializeArray();
var domain = $('[name=domain]').val();
$.ajax({
url: "portfolio/transactions/" + domain + "/",
type: "POST",
data: serial,
success: function (data) {
$('#transactions_div').html(data);
}
});
});
HTML
<form class="add_form">
<form class="secondForm">
<input type="checkbox" />
</form>
<input name="domain" type="text" />
<!-- Replace the submit-Button with a normal button -->
<input type="button" value="Send" id="send"/>
</form>
JS
$(document).on('click', "#send", function () {
var serial = $(".add_form").serialize();
var domain = $('[name=domain]').val();
$.ajax({
url: "portfolio/transactions/" + domain + "/",
type: "post",
data: serial, // serial instead of {data : serial}
success: function (dat) {
$('#transactions_div').html(dat);
}
});
});

Validate fields after user has left a field

With AngularJS, I can use ng-pristine or ng-dirty to detect if the user has entered the field. However, I want to do client-side validation only after the user has left the field area. This is because when a user enters a field like e-mail or phone, they will always get an error thrown until they've completed typing out their full e-mail, and this is not an optimal user experience.
Example
UPDATE:
Angular now ships with a custom blur event:
https://docs.angularjs.org/api/ng/directive/ngBlur
From version 1.3.0 this can easily be done with $touched, which is true after the user has left the field.
<p ng-show="form.field.$touched && form.field.$invalid">Error</p>
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Angular 1.3 now has ng-model-options, and you can set the option to { 'updateOn': 'blur'} for example, and you can even debounce, when the use is either typing too fast, or you want to save a few expensive DOM operations (like a model writing to multiple DOM places and you don't want a $digest cycle happening on every key down)
https://docs.angularjs.org/guide/forms#custom-triggers and https://docs.angularjs.org/guide/forms#non-immediate-debounced-model-updates
By default, any change to the content will trigger a model update and
form validation. You can override this behavior using the
ngModelOptions directive to bind only to specified list of events.
I.e. ng-model-options="{ updateOn: 'blur' }" will update and validate
only after the control loses focus. You can set several events using a
space delimited list. I.e. ng-model-options="{ updateOn: 'mousedown
blur' }"
And debounce
You can delay the model update/validation by using the debounce key
with the ngModelOptions directive. This delay will also apply to
parsers, validators and model flags like $dirty or $pristine.
I.e. ng-model-options="{ debounce: 500 }" will wait for half a second
since the last content change before triggering the model update and
form validation.
I solved this by expanding on what #jlmcdonald suggested. I created a directive that would automatically be applied to all input and select elements:
var blurFocusDirective = function () {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, elm, attr, ctrl) {
if (!ctrl) {
return;
}
elm.on('focus', function () {
elm.addClass('has-focus');
scope.$apply(function () {
ctrl.hasFocus = true;
});
});
elm.on('blur', function () {
elm.removeClass('has-focus');
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
elm.closest('form').on('submit', function () {
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
}
};
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
This will add has-focus and has-visited classes to various elements as the user focuses/visits the elements. You can then add these classes to your CSS rules to show validation errors:
input.has-visited.ng-invalid:not(.has-focus) {
background-color: #ffeeee;
}
This works well in that elements still get $invalid properties etc, but the CSS can be used to give the user a better experience.
I managed to do this with a pretty simple bit of CSS. This does require that the error messages be siblings of the input they relate to, and that they have a class of error.
:focus ~ .error {
display:none;
}
After meeting those two requirements, this will hide any error message related to a focused input field, something that I think angularjs should be doing anyway. Seems like an oversight.
This seems to be implemented as standard in newer versions of angular.
The classes ng-untouched and ng-touched are set respectively before and after the user has had focus on an validated element.
CSS
input.ng-touched.ng-invalid {
border-color: red;
}
Regarding #lambinator's solution... I was getting the following error in angular.js 1.2.4:
Error: [$rootScope:inprog] $digest already in progress
I'm not sure if I did something wrong or if this is a change in Angular, but removing the scope.$apply statements resolved the problem and the classes/states are still getting updated.
If you are also seeing this error, give the following a try:
var blurFocusDirective = function () {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, elm, attr, ctrl) {
if (!ctrl) {
return;
}
elm.on('focus', function () {
elm.addClass('has-focus');
ctrl.$hasFocus = true;
});
elm.on('blur', function () {
elm.removeClass('has-focus');
elm.addClass('has-visited');
ctrl.$hasFocus = false;
ctrl.$hasVisited = true;
});
elm.closest('form').on('submit', function () {
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
}
};
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
It might work for you to write a custom directive that wraps the javascript blur() method (and runs a validation function when triggered); there's an Angular issue that has a sample one (as well as a generic directive that can bind to other events not natively supported by Angular):
https://github.com/angular/angular.js/issues/1277
If you don't want to go that route, your other option would be to set up $watch on the field, again triggering validation when the field is filled out.
To pick up further on the given answers, you can simplify input tagging by using CSS3 pseudo-classes and only marking visited fields with a class to delay displaying validation errors until the user lost focus on the field:
(Example requires jQuery)
JavaScript
module = angular.module('app.directives', []);
module.directive('lateValidateForm', function () {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
$inputs = element.find('input, select, textarea');
$inputs.on('blur', function () {
$(this).addClass('has-visited');
});
element.on('submit', function () {
$inputs.addClass('has-visited');
});
}
};
});
CSS
input.has-visited:not(:focus):required:invalid,
textarea.has-visited:not(:focus):required:invalid,
select.has-visited:not(:focus):required:invalid {
color: #b94a48;
border-color: #ee5f5b;
}
HTML
<form late-validate-form name="userForm">
<input type="email" name="email" required />
</form>
based on #nicolas answer.. Pure CSS should the trick, it will only show the error message on blur
<input type="email" id="input-email" required
placeholder="Email address" class="form-control" name="email"
ng-model="userData.email">
<p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>
CSS
.ng-invalid:focus ~ .bg-danger {
display:none;
}
Here is an example using ng-messages (available in angular 1.3) and a custom directive.
Validation message is displayed on blur for the first time user leaves the input field, but when he corrects the value, validation message is removed immediately (not on blur anymore).
JavaScript
myApp.directive("validateOnBlur", [function() {
var ddo = {
restrict: "A",
require: "ngModel",
scope: {},
link: function(scope, element, attrs, modelCtrl) {
element.on('blur', function () {
modelCtrl.$showValidationMessage = modelCtrl.$dirty;
scope.$apply();
});
}
};
return ddo;
}]);
HTML
<form name="person">
<input type="text" ng-model="item.firstName" name="firstName"
ng-minlength="3" ng-maxlength="20" validate-on-blur required />
<div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
<span ng-message="required">name is required</span>
<span ng-message="minlength">name is too short</span>
<span ng-message="maxlength">name is too long</span>
</div>
</form>
PS. Don't forget to download and include ngMessages in your module:
var myApp = angular.module('myApp', ['ngMessages']);
ng-model-options in AngularJS 1.3 (beta as of this writing) is documented to support {updateOn: 'blur'}. For earlier versions, something like the following worked for me:
myApp.directive('myForm', function() {
return {
require: 'form',
link: function(scope, element, attrs, formController) {
scope.validate = function(name) {
formController[name].isInvalid
= formController[name].$invalid;
};
}
};
});
With a template like this:
<form name="myForm" novalidate="novalidate" data-my-form="">
<input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
<span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
<button type="submit">Submit Form</button>
</form>
Use field state $touched The field has been touched for this as shown in below example.
<div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
You must enter a value
</div>
You can dynamically set the has-error css class (assuming you're using bootstrap) using ng-class and a property on the scope of the associated controller:
plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info
HTML:
<div ng-class="{'has-error': badEmailAddress}">
<input type="email" class="form-control" id="email" name="email"
ng-model="email"
ng-blur="emailBlurred(email.$valid)">
</div>
Controller:
$scope.badEmailAddress = false;
$scope.emailBlurred = function (isValid) {
$scope.badEmailAddress = !isValid;
};
If you use bootstrap 3 and lesscss you can enable on blur validation with the following less snippet:
:focus ~ .form-control-feedback.glyphicon-ok {
display:none;
}
:focus ~ .form-control-feedback.glyphicon-remove {
display:none;
}
.has-feedback > :focus {
& {
.form-control-focus();
}
}
outI used a directive. Here is the code:
app.directive('onBlurVal', function () {
return {
restrict: 'A',
link: function (scope, element, attrs, controller) {
element.on('focus', function () {
element.next().removeClass('has-visited');
element.next().addClass('has-focus');
});
element.on('blur', function () {
element.next().removeClass('has-focus');
element.next().addClass('has-visited');
});
}
}
})
All my input control has a span element as the next element, which is where my validation message is displayed and so the directive as an attribute is added to each input control.
I also have (optional).has-focus and has-visited css class in my css file which you see being referenced in the directive.
NOTE: remember to add 'on-blur-val' exactly this way to your input control without the apostrophes
By using ng-focus you can achieve your goal. you need to provide ng-focus in your input field. And while writing your ng-show derivatives you have to write a logic not equal too. Like the below code:
<input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus>
<div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>
We can use onfocus and onblur functions. Would be simple and best.
<body ng-app="formExample">
<div ng-controller="ExampleController">
<form novalidate class="css-form">
Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
</form>
</div>
<style type="text/css">
.css-form input.ng-invalid.ng-touched {
border: 1px solid #FF0000;
background:#FF0000;
}
.css-form input.focusOn.ng-invalid {
border: 1px solid #000000;
background:#FFFFFF;
}
</style>
Try here:
http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

Enforcing Case Insensitive uniqueness on fields at a given nesting level using angularjs

<FORM>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<INPUT type="submit"/>
</FORM>
My question is: How do I ensure that for the form to be valid, the nested toValidates have a unique value but only within the same outer div?
I am guessing this logic should go in an OuterClassDirective, but I can't seem to figure out what the logic should look like?
Any advice would be appreciated.
Thanks!
What about this. Your outerClassDirective should have a controller, which will store used values in an array. It will transclude the input fields in its body. Your toValidate directive requires outerClassDirective and adds the model value to the array, making it invalid if exists.
Here is a try (untested):
app.directive('outerClass', function() {
var values = [];
var valid = true;
return {
template: '<div ng-transclude></div>',
transclude: true,
replace: true,
require: 'ngModel',
controller: function() {
this.addValue: function(value) {
valid = values.indexOf(value) > -1;
values.push(value);
};
},
link: function(scope, elm, attrs, ctrl) {
ctrl.$setValidity('toValidate', valid)
}
};
});
app.directive('toValidate', function() {
return {
require: '^outerClass',
link: function(scope, elm, attrs, ctrl) {
ctrl.addValue(attrs.value);
}
}
};
});
The 'tabs' and 'pane' directives on the Angular home page solve a similar issue -- the child 'pane' directives need to communicate with the parent 'tabs' directive.
Define a controller on the outerclass directive, and then define a method on the controller (use this not $scope). Then require: '^outerclass' in the toValidate directive. In the toValidate link function, you can $watch for value changes and call the method on the outerclass controller to pass the value up. Do the validation in the outerclass directive.
See also 'this' vs $scope in AngularJS controllers.