When we have a controller or ng-model-controller we can do
ctrl.$parsers.push(function(viewValue){
ctrl.$setValidity('valid', true);
});
and in the end $digest is called automatically and validation renders.
What if I want to validate a field on blur. and I do
element.blur(function(){
[validations]
ctrl.$setValidity('valid', false);
})
and the result don't change on html with elements ng-binded, how to render this change?
You have to call ctrl.$setValidity('valid', false); within scope.$apply
scope.$apply(function(){
ctrl.$setValidity('valid', false);
})
Or better yet, in order to avoid problems further on, use CSS to solve this problem. Let's assume the following code:
<textarea ng-model="description" ng-minlength="50"></textarea>
<span class="error" ng-show="myform.$error.minlength">Too short!</span>
<span class="error" ng-show="myform.$error.someOtherValidation">Err!</span>
When you start typing, Angular starts validation, and errors appear (which is sometimes desirable, but more often, it's not).
What you can do in order not to mess up Angular's internals, is some CSS magic:
textarea:focus ~ .error {
display:none;
}
The tilde is a general sibling selector, so as long as you have your fields focused all your errors will stay hidden.
Related
I have an application where I am using ui-bootstrap-tabs. documentation here. With the ng-bs-daterangepicker.
The behavior I am observing is that whenever I put the daterangepicker inside the ui-tab. It is not able to catch the events attached to it.
But when I move that input tag outside the ui-tabs, it's able to catch the events associated with it.
I have created a working plunker to highlight my issue.
<body ng-app="myApp">
<div ng-controller="testController">
<uib-tabset>
<uib-tab index="0" heading="Drivers"></uib-tab>
<uib-tab index="1" heading="Charts">
<input class="btn btn-danger" type="daterange" id="daterange1" ng-model="dates" format="DD MMM" ranges="ranges" />
</uib-tab>
</uib-tabset>
<!-- <input class="btn btn-danger" type="daterange" id="daterange1" ng-model="dates" format="DD MMM" ranges="ranges" /> -->
</div>
</body>
Here there are two input tags. One inside the uib-tab and other outside it.
$scope.dates = {
startDate: moment().startOf('day'),
endDate: moment().endOf('day')
};
$scope.ranges = {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract('days', 1), moment().subtract('days', 1)],
'Last 7 days': [moment().subtract('days', 7), moment()],
'Last 30 days': [moment().subtract('days', 30), moment()],
'This month': [moment().startOf('month'), moment().endOf('month')]
};
$('#daterange1').on('apply.daterangepicker', function(ev, picker) {
console.log(picker.startDate);
console.log(picker.endDate);
});
The event apply.daterangepicker is not called when I activate the inside input button but is called when I activate the outside one.
My approach
I am guessing it's not a scope issue as highlighted in some posts. Because if it was that, then how come even the date is being populated.
Another thing could be the Known issue column which says
To use clickable elements within the tab, you have override the tab
template to use div elements instead of anchor elements, and replicate
the desired styles from Bootstrap's CSS. This is due to browsers
interpreting anchor elements as the target of any click event, which
triggers routing when certain elements such as buttons are nested
inside the anchor element.
maybe somehow this is stopping the event propagation. I am stuck at this point and can't think of a solution on how to fix it. Hoping the community would help here...
In case of $('#daterange1').on the object to which the event if getting attached must exist at the moment when .on() is invoked.
When daterangepicker is initialized inside Tabs component you could attach event like this:
$("body").on("apply.daterangepicker", "#daterange1", function(e,picker) {
console.log(picker.startDate);
console.log(picker.endDate);
});
Modified plunker
I have called scripts on _Layout.cshtml page and my Index.cshtml page has partial view into it. So on page load, SignalR scripts working perfect on partial view, on page end I make another ajax request and load the partial view with another data filled in that and embed under already displayed data, and then the SignalR does not work on the newly embedded record.
This is my index page code:
<div class="col-md-6">
<div class="profile-body">
<div class="row infinite-scroll">
#Html.Partial("_AlbumRow", Model)
</div>
</div>
</div>
This is my partial View Code:
#model IEnumerable<SmartKids.Lib.Core.ViewModels.FileMediaAlbumsVM>
#foreach (var item in Model)
{
<div class="widget">
<div class="block rounded">
<img src="#Url.Content(item.ImageUrl)" alt="#item.Title">
<input type="button" data-image-id="#item.imageId" class="btn btn-sm btn-default">Like</input>
</div>
</div>
}
Kindly help me how to resolve this issue that after making an ajax request I am not able to get those SignalR working. Here is more to say when I put the SignalR scripts on PartialView that works but it also sucks that on each ajax request there is again SignalR loaded on the page and when I click on LIke button it makes many calls to the function behind it.
Kindly help me to resolve this issue, I am stuck at this point since 1 week.
Here is signalR Code:
$(".btn.btn-sm.btn-default").on("click", function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(imageId);
});
Problem: You are binding event to elements directly, So when you remove this element and replace it with a different one the events are also removed along with that element, This is something like strongly coupled.
Solution: Use Jquery event delegation. This will make sure the events will be triggered on the current elements and also all the elements that can come in future.
syntax is as below.
$(document).on("click", ".btn.btn-sm.btn-default",function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(iamgeId);
});
NOTE: This was never a singlaR issue, it was Jquery issue.
Efficient Way: The problem in using $(document).on("click"... is that when ever there is a click happening on the entire page the Jquery framework will bubble the events from the clicked element upwards(its parent, and its parent and so on..) unless the element specified in the selector arrives, So its kind of performance hit as we don't want this check's to run if we are clicking outside the required area ( button .btn.btn-sm.btn-default in this example).
So best practice is to bind this event delegation to the closest parent possible which will not be removed, <div class="row infinite-scroll"> in this question. So that only when the click happens within this element the event bubbling will happen and also will be stopped once it reaches the parent element,it acts kind of a boundary for event bubbling.
$('.row.infinite-scroll').on("click", ".btn.btn-sm.btn-default",function () {
var imageId = $(this).attr("data-image-id");
albumClient.server.like(iamgeId);
});
Today I'm learning Vue.js, and I have a few ideas of where it might be really useful in a new project that's an off-shoot of an existing, live project.
I like the idea of trying to replace some of my existing functionality with Vue, and I see that Components may be quite handy as quite a lot of functionality is re-used (e.g. Postcode lookups).
Once of the pieces of functionality I've used for an age is for invalid form elements - currently in jQuery when a form input or textarea is blurred I add a class of form__blurred, and that is coupled with some Sass such as:
.form__blurred {
&:not(:focus):invalid {
border-color:$danger;
}
}
This is to avoid styling all required inputs as errors immediately on page load.
I'm totally fine with doing this in jQuery, but I figured maybe it could be done in Vue.
I have an idea of how I might do it with components thanks to the laracasts series, but my form inputs are all rendered by Blade based on data received from Laravel and it doesn't seem like a neat solution to have some of the inputs rendered in Javascript, for a number of reasons (no JS, confusion about where to find input templates, etc).
I figured something like the following simplified example would be handy
<input type="text" class="form__text" v-on:blur="blurred" v-bind:class="{ form__blurred : isBlurred }" />
<script>
var form = new Vue({
el : '.form__input',
data : {
isBlurred : false
},
methods : {
blurred : function() {
this.isBlurred = true;
}
}
});
</script>
That actually works great but, as expected, it seems like using a class selector only selects the first instance, and even if it didn't, I'm guessing changing the properties would apply to all elements, not each one individually.
So the question is - is this possible with pre-rendered HTML, in a way that's smarter than just looping through a selector?
If it is, is there a way to create the Vue on a .form element and apply this function to both .form__input and .form__textarea?
Or, as is probably the case, is this just not a good use-case for Vue (since this is actually a lot more code than the jQuery solution).
Sounds like a great use case for a Custom Directive.
Vue allows you to register your own custom directives. Note that in Vue 2.0, the primary form of code reuse and abstraction is components - however there may be cases where you just need some low-level DOM access on plain elements, and this is where custom directives would still be useful.
<div id="app">
<input type="text" name="myforminput" v-my-directive>
</div>
<script>
Vue.directive('my-directive', {
bind: function (el) {
el.onblur = function () {
el.classList.add('form__blurred');
}
}
});
var app = new Vue({
el: '#app'
});
</script>
You can also add the directive locally to a parent component, if it makes sense for your application.
I'm hijaxing an existing form and POSTing to the server. jQuery validate does most of the validation but if validation fails on the server we return the errors to the client as JSON.
Below is the code that does that:
<script type="text/javascript">
$(function () {
$("form").submit(function (e) {
var $form = $(this);
var validator = $form.data("validator");
if (!validator || !$form.valid())
return;
e.preventDefault();
$.ajax({
url: "#Url.Action("index")",
type: "POST",
data: $form.serialize(),
statusCode: {
400: function(xhr, status, err) {
var errors = $.parseJSON(err);
validator.showErrors(errors);
}
},
success: function() {
// clear errors
// validator.resetForm();
// just reload the page for now
location.reload(true);
}
});
});
});
</script>
The problem is I can't seem to clear the validation errors if the POST is successful. I've tried calling validator.resetForm() but this makes no difference, the error messages added by the showError() call, are still displayed.
Note I'm also using the jQuery.validate.unobtrusive plugin.
You posted this a while ago, I don't know if you managed to solve it? I had the same problem with jQuery validate and the jQuery.validate.unobtrusive plugin.
After examining the source code and some debugging, I came to the conclusion that the problem comes from the way the unobtrusive plugin handles error messages. It removes the errorClass that the jQuery.validate plugin sets, and so when the form is reset, jQuery validate cannot find the error labels to remove.
I did not want to modify the code of the plugins, so I was able to overcome this in the following way:
// get the form inside we are working - change selector to your form as needed
var $form = $("form");
// get validator object
var $validator = $form.validate();
// get errors that were created using jQuery.validate.unobtrusive
var $errors = $form.find(".field-validation-error span");
// trick unobtrusive to think the elements were succesfully validated
// this removes the validation messages
$errors.each(function(){ $validator.settings.success($(this)); })
// clear errors from validation
$validator.resetForm();
note: I use the $ prefix for variables to denote variables that contain jQuery objects.
$("#form").find('.field-validation-error span').html('')
In .NET Core I have the form inside a builtin Bootstrap modal.
For now I'm manually removing the error message spans from their containers, once the modal is starting to show, by using the additional .text-danger class of the error message container like so:
$('#my-form').find('.text-danger').empty();
so that I don't rely on container .field-validation-error that might have been already toggled to .field-validation-valid.
The min.js versions of the libraries jquery.validate and jquery.validate.unobtrusive are loaded via the partial view _ValidateScriptsPartial.cshtml, so I played with them to see what resetForm() / valid() and native html form reset() do.
So in my case $('#my-form').data("validator").resetForm() only resets some validator internals, not the form and it doesn't trigger the onReset() function in the unobtrusive library. The $('#my-form').valid() indeed removes the errors in the modal, but only if the modal is fully shown and valid. The native html form reset() is the only one that triggers both onReset() of unobtrusive library, and then the resetForm() of the validator. So it seems like we need to trigger the native html form document.querySelector('#my-form').reset() to activate the reset functions of both libraries/plugins.
The interesting thing is that the unobtrusive library runs the simple jQuery empty() on the .field-validation-error class (the container of the error span message) only in its onSuccess() function, and not onReset(). This is probably why valid() is able to remove error messages. The unobtrusive onReset() looks like it's responsible only for toggling .field-validation-error class to .field-validation-valid. Hense we are left with a <span id="___-error">The error message</span> inside the container <span class="text-danger field-validation-error">...</span>.
May be I am wrong to clear the errors like this:
function clearError(form) {
$(form + ' .validation-summary-errors').each(function () {
$(this).html("<ul><li style='display:none'></li></ul>");
})
$(form + ' .validation-summary-errors').addClass('validation-summary-valid');
$(form + ' .validation-summary-errors').removeClass('validation-summary-errors');
$(form).removeData("validator");
$(form).removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse($(form));
};
I tried answer given in the comment by AaronLS but not got the solution so I just do it like this.
Maybe helpful to someone.
Here's the code I ended up using to clear/reset all errors. It's possible there's some redundancy in there, but it's working for me.
function removeValidationErrors(frmId) {
var myform = $('#' + frmId);
myform.get(0).reset();
var myValidator = myform.validate();
$(myform).removeData('validator');
$(myform).removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse(myform);
myValidator.resetForm();
$('#' + frmId + ' input, select').removeClass('input-validation-error');
}
The reason this is still an issue (even 6 years on) is that jQuery Validation doesn't have an event handler for when your form is valid; only for when it's invalid.
Unobtrusive Validation taps into the Invalid handler to add your errors to your Validation Summary elements. (Specifically, any element with data-valmsg-summary=true.) But because there's no Valid handler, there's no way for Unobtrusive Validation to know when they can be cleared.
However, jQuery Validation does allow you to supply your own showErrors method, which is called after every validation check, whether the result is valid or invalid. Thus, you can write a custom function that will clear those validation summary boxes if your form is valid.
Here's a sample that will apply it globally. (You could apply it to specific instances of your validators by using settings, but since I always want this functionality, I just put it in the defaults object.)
$.validator.defaults.showErrors = function () {
if (!this.errorList.length) {
var container = $(this.currentForm).find("[data-valmsg-summary=true]");
container.find("ul").empty();
container.addClass("validation-summary-valid").removeClass("validation-summary-errors");
}
// Call jQuery Validation's default showErrors method.
this.defaultShowErrors();
};
This also has the benefit of clearing the validation summary box the moment your form is valid, instead of having to wait for the user to request a form submission.
I couldn't find this documented anywhere, but you should be able to reset the form by triggering a specific event, reset.unobtrusiveValidation, to which unobtrusive listens.
Example here:
.validation-summary-valid, .field-validation-valid { display: none; }
.field-validation-error { display: block; color: #dc3545 }
.input-validation-error { border: 1px solid #dc3545 }
.validation-summary-errors { background-color: #dc3545; color: #fff; margin-bottom: .5rem; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.5/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/4.0.0/jquery.validate.unobtrusive.js"></script>
<form id="testForm">
<div class="validation-summary-valid" data-valmsg-summary="true">
Validation Summary:
<ul><li style="display:none"></li></ul>
</div>
<div>
<label for="first_name">first name:</label>
<input data-val="true" data-val-required="the 'first name' field is required" name="first_name" id="first_name" />
<span class="field-validation-valid" data-valmsg-for="first_name" data-valmsg-replace="true"></span>
</div>
<div>
<label for="last_name">last name:</label>
<input data-val="true" data-val-required="the 'last name' field is required" name="last_name" id="last_name" />
<span class="field-validation-valid" data-valmsg-for="last_name" data-valmsg-replace="true"></span>
</div>
<button type="submit">Submit form (click first)</button>
<button type="button" onclick="$('#testForm').trigger('reset.unobtrusiveValidation')">Reset form (click second)</button>
</form>
EDIT: The problem is not related to Boxy, I've run into the same issue when I've used JQuery 's load method.
EDIT 2: When I take out link.remove() from inside the ajax callback and place it before ajax load, the problem is no more. Are there restrictions for manipulating elements inside an ajax callback function.
I am using JQuery with Boxy plugin.
When the 'Flag' link on the page is clicked, a Boxy modal pops-up and loads a form via ajax. When the user submits the form, the link (<a> tag) is removed and a new one is created from the ajax response. This mechanism works for, well, 3 times! After the 3rd, the callback function just does not remove/replace/append (tested several variations of manipulation) the element.
The only hint I have is that after the 3rd call, the parent of the link becomes non-selectable. However I can't make anything of this.
Sorry if this is a very trivial issue, I have no experience in client-side programming.
The relevant html is below:
<div class="flag-link">
<img class="flag-img" style="width: 16px; visibility: hidden;" src="/static/images/flag.png" alt=""/>
<a class="unflagged" href="/i/flag/showform/9/1/?next=/users/1/ozgurisil">Flag</a>
</div>
Here is the relevant js code:
$(document).ready(function() {
$('div.flag-link a.unflagged').live('click', function(e){
doFlag(e);
return false;
});
...
});
function doFlag(e) {
var link = $(e.target);
var url = link.attr('href');
Boxy.load(url, {title:'Inappropriate Content', unloadOnHide:true, cache:false, behaviours: function(r) {
$("#flag-form").live("submit", function(){
var post_url = $("#flag-form").attr('action');
boxy = Boxy.get(this);
boxy.hideAndUnload();
$.post(post_url, $("#flag-form").serialize(), function(data){
par = link.parent();
par.append(data);
alert (par.attr('class')); //BECOMES UNDEFINED AT THE 3RD CALL!!
par.children('img.flag-img').css('visibility', 'visible');
link.remove();
});
return false;
});
}});
}
Old and late reply, but.. I found this while googling for my answer, so.. :)
I think this is a problem with the "notmodified" error being thrown, because you return the same Ajax data.
It seems that this is happening even if the "ifModified" option is set to false (which is also the default).
Returning the same Ajax data three times will cause issues for me (jQuery 1.4). Making the data unique (just adding time/random number in the response) removes the problem.
I don't know if this is a browser (Firefox), jQuery or server (Apache) issue though..
I have had the same problem, I could not run javascript after I call boxy. So I put all my javascript code in afterShow:function one of boxy attributes. I can run almost except submit my form. My be my way can give you something.