decoupling $scope.form from controller - angularjs

In my angular controller, I have to reset some form fields to pristine at a certain point (when some other field changes). The code looks something like this ...
$scope.fieldCallback = function() {
$scope.data.Field = undefined;
$scope.form.field.$setPristine();
}
this of course causes problem once I run my unit tests, because form.field will only be there if you actually parse the document (which of course in the case of unit tests is not happening). I know it is possible to do something along the lines of
var element = angular.element(...[some html]...);
$compile(element)($scope);
but this does not really seem like a good solution to me.
Is there any way to decouple the controller from the html so I don't have to mock this like described above?
Thx in advance!

If I am correctly getting your question, you could try implementing this while focus out of form
<form name="testForm" ... ... ng-blur="func()">
<input type="text" name="Field" />
</form
and in js file
$scope.func = function(){
$scope.testForm.Field.$setPristine();
}
you can drirectly use expresion in ngBlur
ng-blur="focus=false"

Related

Angular directive making required field readonly

I'm trying to make a input field required and readonly.
The html standard does not allow this, so I tried to make my own directive for it, but the keydown/keypress bind is never made. Is there a way around this and why this does not work?
ngApp.directive('required', function() {
return {
require: '?input',
restrict: 'A',
link: function($scope,$element,$attr) {
if ($attr.hasOwnProperty('readonly')) {
$element.removeAttr('readonly');
$element.bind('keydown keypress', function(e){
e.preventDefault();
});
}
}
};
});
plnkr.co/edit/XhTxPpqnKwSFKJflSHZW?p=preview .... second text field, required is never validated by the system!
cheers
c_bb
EDIT: I just re-read your question and thought about it again, and my original answer is crap. You really do want novalidate on your form, and then you show errors in an angular type way rather than trying to use html form validation, which isn't going to work for you for the reasons you mention in the comments.
You want your form to looks something like this:
<form name="testForm" ng-submit="submitForm(testForm.$valid)" novalidate >
<input type="text" name="value" ng-model="test1" required><br>
<input type="text" name="value2" ng-model="test2" required readonly><br>
<p ng-show="testForm.value2.$error.required" class="help-block">Please select a whatever</p>
<input type="submit" ng-click='testForm.$valid && mySubmitFunction()'>
</form>
This will give you an error message based on the stock angular validation for required.
and you need some kind of a submit handler in your controller, like this:
$scope.submitForm = function(isValid) {
// check to make sure the form is completely valid
if (isValid) {
alert('our form is amazing');
} else {
console.log('fail')
}
};
Do whatever sensible things you need in the submit function.
//OLD, TERRIBLE ANSWER BELOW
If the readonly attribute does indeed override the required attribute the 'proper' solution would probably to write some custom validation (see article at http://ng-learn.org/2014/02/Writing_Custom_Validitions/).
However, if you just want a cheap way out, you could put a ng-minlength=1 requirement on the input. If the user can't put anything in the field manually, and there has to be something in the field for it to be valid, I think you've basically got what you want.

AngularJs watch angular.element

I'm trying to integrate AngularJs into a legacy Spring MVC application because there is lots of spaghetti javascript code (a huge mess!) to hide / show html elements based on conditions etc.
It uses jsp and lots of custom jsp tags and because the way it is written I'm hesitant to mess with the jsp tags themselves.
I'm trying to do is read the values of the spring input into angular scope and once I have it then can use angular to hide / show stuff.
Assume that my html is something like this
<div ng-app ng-controller="FooCtrl">
Backbone <input type="radio" name="yes" value="Backbone"/>
Angular <input type="radio" name="yes" value="Angular" />
</div>
I'm able to read these elements into Angular's scope like this
$scope.elements = angular.element("input[name='yes']");
But change to the value of these elements are not getting triggered or watched by Angular.
Ideally when the radio button gets checked I would like the model to change. How can I do this?
Thank you in advance.
Here is plnkr with the basic setup.
http://plnkr.co/edit/CRPBFF9FaGivBRa8OSdZ?p=preview
One thing is that in your controller you had:
$scope.$watch('$scope.elements',function(newValue){
console.log(newValue);
},true);
It should be 'elements' rather than '$scope.elements'. I'm not quite sure if a $watch or $watchCollection is going to be your best bet here. I tried it but was having issues.
Here is another idea:
var app = angular.module('myApp',[]);
app.controller('FooCtrl', function($scope){
$scope.message = "hi";
$scope.elements = angular.element("input[name='yes']");
angular.element("input[name='yes']").bind("input change", function(e) {
console.log(e);
});
});

Why I cannot access form angular's properites in JS such as $error?

I cannot access angular's Form members. I don't have a clue why. I'm using angular 1.4. The full code is at: http://jsbin.com/lujojiduru/edit?html,js,console,output
angular.module('test',[]).controller('testController', function($scope){
$scope.sendInvitations = function(){
var error = myForm.NewInvitations.$error;
console.log('sent');
console.log('value: ' + error );
};
});
the value of $error is always undefined. Any idea?
var error = myForm.NewInvitations.$error;
should be:
var error = $scope.myForm.NewInvitations.$error;
Notice the $scope
This is assuming you have the name="myForm" on your <form> tag
So something like:
<div ng-controller="testController">
<form name="myForm" novalidate>
...
</form>
</div>
you can also, if you prefer, send in the validity of your form, to your method on the controller.
So:
<button class="btn btn-success" ng-click="sendInvitations(myForm.$valid)">Send Invitations</button>
And in your controller:
$scope.sendInvitations = function(isValid){
if(!isValid)
return;
};
Update
You also don't have any errors.
Add required to your input.
So your controller is now:
angular.module('test',[]).controller('testController', function($scope){
$scope.sendInvitations = function(){
debugger;//
var error = $scope.myForm.NewInvitations.$error;
console.log('sent');
console.log(error );
};
});
and your input
<input style="width: 95%" name="NewInvitations" type="email" ng-model="newInvitations" required />
This is an update to your bin
http://jsbin.com/hebikucanu/1/edit?html,js,console,output
myForm is not accessible in the global scope. You can pass it in as an argument to sendInvitations.
ng-click="sendInvitations(myForm)
$scope.sendInvitations = function(myForm){
It's unlikely that you'd need to do this, though. You can do use the myForm properties in the view.
In angular, you should never touch the DOM. Meaning, you should never invoke HTML elements directly as you would in traditional HTML/JS settings. (read more)
Angular encourages the use of ng-model to employ two-way binding as a means to communicate between the controller and the view. (angular two-way data binding tutorial)
Thus, the first change you should attempt is to replace
var error = myForm.NewInvitations.$error;
with:
var error = $scope.NewInvitations;
This will cause the code to run.
But it appears that you want to retrieve the email input validation error and display it through angular.
Here is an excellent explanation with a tutorial that I think achieves what you're trying to do. If you just want to see the code in action, try this link. (Be sure to hit the Run button.)
Hope this helps!

Two-way bind does not work if the model is something like ng-model="record.name" for Kendo UI controls

I have a problem with Kendo UI controls
My HTML
<input type="text" ng-model="record.name" kendo-numeric-text-box />
<input type="text" ng-model="record.name"> </input>
<button ng-click="resetRecord()" >test bind</button>
My test.js
$scope.record ={};
$scope.resetRecord= function(){
$scope.record = {};
}
Well, when I execute the function resetRecord just the standard input clear, the kendo ui input does not clear, I've tried $scope.record =null and it does not work either.
if I change to code below, it works, but I need it works like above
$scope.resetRecord= function(){
$scope.record. name = null;
}
it happens with all input Kendo UI, not just kendo-numeric-text-box
if there is a way to iterate with record object, discovering all the "properties", such as name it will work for me.
My intention is just have one controller for all CRUD screen of the system, I wouldn't like to write one controller with model definition for each entity of the system.
is it a bug?
UPDATED - SOLVED
Solution 1
As runTarm said in solution 2.
for (prop in $scope.model.record) {
$scope.model.record[prop]='';
}
Solution 2
Just add the attribute k-rebind to the Kendo UI controls and it will update it's content according ng-model.
input type="text" ng-model="record.name" k-rebind="record.name" kendo-numeric-text-box />
This is not a bug, but a common issue due to the Prototypal Inheritance characteristic of JavaScript Object. I would recommend reading Understanding-Scopes for more detail.
You could avoid the problem by storing the record in another object, instead of in $scope directly. For example:
$scope.model = {};
$scope.model.record = {};
$scope.resetRecord = function() {
$scope.model.record = {};
}
Also change your ng-model:
<input type="text" ng-model="model.record.name" kendo-numeric-text-box />
<input type="text" ng-model="model.record.name"> </input>
<button ng-click="resetRecord()" >test bind</button>
Or if you wouldn't want to add the extra model., an alternative solution is what you have mentioned.
You could iterate over object's properties and delete them all like this:
for (prop in $scope.record) {
delete $scope.record[prop];
}
Hope this helps.

Angular - Form Validation Custom Directive, how to pass result to another element for the error display?

So basically I'm building myself a new directive for form validation and I got it all working perfectly except for 1 thing which I believe is not properly coded. So as you know form validation with a directive is to validate an <input ngx-validation="alpha"> how do I pass the error text, which occurs inside my directive to the other element (a Bootstrap <span class="text-danger">{{ validation_errors["input1"] }}</span> that is right below my input? As for the moment, I created a scope variable which exist inside my controller and it does work...until the user forgets to create that actual variable... So how am I suppose to share the information for 1 element to another? Which by the way, my variable is an array holding all input error messages... Here is my code at the moment:
<!-- html form -->
<input type="text" class="form-control" name="input1" ng-model="form1.input1" ngx-validation="alpha|min_len:3|required" />
<span class="validation text-danger">{{ validation_errors["input1"] }}</span>
JS Code
// my Controller
myApp.controller('Ctrl', ['$scope', '$translate', function ($scope, $translate) {
$scope.form1 = {};
$scope.validation_errors = []; // the scope variable
}]);
// creation of my directive
angular.module('ghiscoding.validation', ['pascalprecht.translate'])
.directive('ngxValidation', function($translate){
return{
require: "ngModel",
link: function(scope, elm, attrs, ctrl) {
// get the ngx-validation attribute
var validationAttr = attrs.ngxValidation;
// rest of the validation code...
// ...
if(!isFieldValid && ctrl.$dirty) {
scope.validation_errors[ctrl.$name] = message;
}else {
scope.validation_errors[ctrl.$name] = "";
}
return value;
};
ctrl.$parsers.unshift(validator);
ctrl.$formatters.unshift(validator);
}
};
});
If you look at my code, the scope variable in question which I use is called $scope.validation_errors = []; which is created in the Controller, without creating it, it will of course fail. You can also see my github, I made it available and also wish that lot of people could use my Angular-Validation directive because it's just so easy the way I've done it :) See my Github Angular-Validation
EDIT
Just to make it clear, the validation part of my directive is working fine. My real problem is simply, how do I pass the error message from the directive (which the directive is connected to the <input>) and pass that error message (a simple string) to the <span> they are 2 different elements, how can I talk to another element within a directive, how can I bind? At the moment I'm using a global variable, which needs to exist in the controller, this isn't good... I am very new to Angular and I'm struggling with directives, so please provide code. Thanks a lot for help.
ANSWER
So to have a full Angular Directive, the last piece of code for the solution was answered here... Since the <span> for displaying my error message is always after my input, I can simply update the next element text with native Angular jqLite...that's it, as simple as that... Here is my new HTML code
<!-- HTML -->
<input type="text" name="input1" validation="alpha|min_len:3|required" />
<span class="validation text-danger"></span>
// Previous piece of code to replace
if(!isFieldValid && ctrl.$dirty) {
scope.validation_errors[ctrl.$name] = message;
}
// REPLACED by this
if(!isFieldValid && ctrl.$dirty) {
elm.next().text(message);
}
You should do the same thing as all the other built-in form validation directives do: use the ngModelController's $setValidity method, using your own key. Your span will then be able to check if an error exists for your validation key using
theFormName.theInputName.$error.yourValidationKey
And the validity of the field, as well as the validity of the enclosing form, will automatically be handled by angular.
See http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController.

Resources