AngularJS does not send hidden field value - javascript

For a specific use case I have to submit a single form the "old way". Means, I use a form with action="". The response is streamed, so I am not reloading the page. I am completely aware that a typical AngularJS app would not submit a form that way, but so far I have no other choice.
That said, i tried to populate some hidden fields from Angular:
<input type="hidden" name="someData" ng-model="data" /> {{data}}
Please note, the correct value in data is shown.
The form looks like a standard form:
<form id="aaa" name="aaa" action="/reports/aaa.html" method="post">
...
<input type="submit" value="Export" />
</form>
If I hit submit, no value is sent to the server. If I change the input field to type "text" it works as expected. My assumption is the hidden field is not really populated, while the text field actually is shown due two-way-binding.
Any ideas how I can submit a hidden field populated by AngularJS?

You cannot use double binding with hidden field.
The solution is to use brackets :
<input type="hidden" name="someData" value="{{data}}" /> {{data}}
EDIT : See this thread on github : https://github.com/angular/angular.js/pull/2574
EDIT:
Since Angular 1.2, you can use 'ng-value' directive to bind an expression to the value attribute of input. This directive should be used with input radio or checkbox but works well with hidden input.
Here is the solution using ng-value:
<input type="hidden" name="someData" ng-value="data" />
Here is a fiddle using ng-value with an hidden input: http://jsfiddle.net/6SD9N

You can always use a type=text and display:none; since Angular ignores hidden elements. As OP says, normally you wouldn't do this, but this seems like a special case.
<input type="text" name="someData" ng-model="data" style="display: none;"/>

In the controller:
$scope.entityId = $routeParams.entityId;
In the view:
<input type="hidden" name="entityId" ng-model="entity.entityId" ng-init="entity.entityId = entityId" />

I've found a nice solution written by Mike on sapiensworks. It is as simple as using a directive that watches for changes on your model:
.directive('ngUpdateHidden',function() {
return function(scope, el, attr) {
var model = attr['ngModel'];
scope.$watch(model, function(nv) {
el.val(nv);
});
};
})
and then bind your input:
<input type="hidden" name="item.Name" ng-model="item.Name" ng-update-hidden />
But the solution provided by tymeJV could be better as input hidden doesn't fire change event in javascript as yycorman told on this post, so when changing the value through a jQuery plugin will still work.
Edit
I've changed the directive to apply the a new value back to the model when change event is triggered, so it will work as an input text.
.directive('ngUpdateHidden', function () {
return {
restrict: 'AE', //attribute or element
scope: {},
replace: true,
require: 'ngModel',
link: function ($scope, elem, attr, ngModel) {
$scope.$watch(ngModel, function (nv) {
elem.val(nv);
});
elem.change(function () { //bind the change event to hidden input
$scope.$apply(function () {
ngModel.$setViewValue( elem.val());
});
});
}
};
})
so when you trigger $("#yourInputHidden").trigger('change') event with jQuery, it will update the binded model as well.

Found a strange behaviour about this hidden value () and we can't make it to work.
After playing around we found the best way is just defined the value in controller itself after the form scope.
.controller('AddController', [$scope, $http, $state, $stateParams, function($scope, $http, $state, $stateParams) {
$scope.routineForm = {};
$scope.routineForm.hiddenfield1 = "whatever_value_you_pass_on";
$scope.sendData = function {
// JSON http post action to API
}
}])

I achieved this via -
<p style="display:none">{{user.role="store_user"}}</p>

update #tymeJV 's answer
eg:
<div style="display: none">
<input type="text" name='price' ng-model="price" ng-init="price = <%= #product.price.to_s %>" >
</div>

I had facing the same problem,
I really need to send a key from my jsp to java script,
It spend around 4h or more of my day to solve it.
I include this tag on my JavaScript/JSP:
$scope.sucessMessage = function (){
var message = ($scope.messages.sucess).format($scope.portfolio.name,$scope.portfolio.id);
$scope.inforMessage = message;
alert(message);
}
String.prototype.format = function() {
var formatted = this;
for( var arg in arguments ) {
formatted = formatted.replace("{" + arg + "}", arguments[arg]);
}
return formatted;
};
<!-- Messages definition -->
<input type="hidden" name="sucess" ng-init="messages.sucess='<fmt:message key='portfolio.create.sucessMessage' />'" >
<!-- Message showed affter insert -->
<div class="alert alert-info" ng-show="(inforMessage.length > 0)">
{{inforMessage}}
</div>
<!-- properties
portfolio.create.sucessMessage=Portf\u00f3lio {0} criado com sucesso! ID={1}. -->
The result was:
Portfólio 1 criado com sucesso! ID=3.
Best Regards

Just in case someone still struggles with this, I had similar problem when trying to keep track of user session/userid on multipage form
Ive fixed that by adding
.when("/q2/:uid" in the routing:
.when("/q2/:uid", {
templateUrl: "partials/q2.html",
controller: 'formController',
paramExample: uid
})
And added this as a hidden field to pass params between webform pages
<< input type="hidden" required ng-model="formData.userid" ng-init="formData.userid=uid" />
Im new to Angular so not sure its the best possible solution but it seems to work ok for me now

Directly assign the value to model in data-ng-value attribute.
Since Angular interpreter doesn't recognize hidden fields as part of ngModel.
<input type="hidden" name="pfuserid" data-ng-value="newPortfolio.UserId = data.Id"/>

I use a classical javascript to set value to hidden input
$scope.SetPersonValue = function (PersonValue)
{
document.getElementById('TypeOfPerson').value = PersonValue;
if (PersonValue != 'person')
{
document.getElementById('Discount').checked = false;
$scope.isCollapsed = true;
}
else
{
$scope.isCollapsed = false;
}
}

Below Code will work for this IFF it in the same order as its mentionened
make sure you order is type then name, ng-model ng-init, value. thats It.

Here I would like to share my working code :
<input type="text" name="someData" ng-model="data" ng-init="data=2" style="display: none;"/>
OR
<input type="hidden" name="someData" ng-model="data" ng-init="data=2"/>
OR
<input type="hidden" name="someData" ng-init="data=2"/>

Related

Undefined value from text area in AngularJS

I am creating one form using Bootstrap & AngularJS. I am using CK editor in my page as textarea. But I am not able to retrieve the value of the textarea while the value of the input text field is easily captured in my AngularJS controller. Following is the code snippet:
HTML page:
<div class="container">
<div ng-controller="controller">
<form role="form">
<label for="sd"><b>Short Description: </b></label>
<input ng-model="sdesc" class = "form-control input-xxlarge" type = "text" placeholder ="Provide a short description here."/>
<br/>
<label for="dt"><b>Details: </b></label>
<textarea ng-model="details" class="form-control" name="details_editor" id="details_editor"></textarea>
<br/>
<button class = "btn btn-primary" ng-click="submitted()">Ask It!</button>
<script>
CKEDITOR.replace('details_editor');
</script>
</form>
</div>
<br/>
<hr>
</div>
JS
app.controller('controller', ['$scope', function($scope){
$scope.submitted = function(){
var sdesc = $scope.sdesc;
var details = $scope.details;
alert($scope.details);
};
}]);
The alert shows undefined for the text area value.
Please help me solve the issue.
You are using the plain Javascript version of CK editor and hence Angular is not getting notified to update the ng-model value of that textarea.
Basically, Angular runs a digest cycle to update all views and models but since in this case the values being changed in the CK editor is happening outside the Angular.s context which is not updating the ng-model value.
To fix this, we added a small directive and notifying the change in the ng-model to the Angular by using the $timeout. (We can also use the $apply directive, but it may fail sometimes if the digest cycle is already in progress)
Example directive:
var app = angular.module("your-app-name", []);
app.directive("ckEditor", ["$timeout", function($timeout) {
return {
require: '?ngModel',
link: function ($scope, element, attr, ngModelCtrl) {
var editor = CKEDITOR.replace(element[0]);
console.log(element[0], editor);
editor.on("change", function() {
$timeout(function() {
ngModelCtrl.$setViewValue(editor.getData());
});
});
ngModelCtrl.$render = function (value) {
editor.setData(ngModelCtrl.$modelValue);
};
}
};
}]);
Remove, your following code:
<script>
CKEDITOR.replace('details_editor');
</script>
And, modify your text-editor like:
<textarea ng-model="details" class="form-control" ck-editor name="details_editor" id="details_editor"></textarea>
I found ng-ckeditor to implement ckeditor in angularjs.
Please refer this :https://github.com/esvit/ng-ckeditor. I tried it, It is easy to implement and working as expected

Angularjs ng-required call function

It is possible make the required value dependet of some funcion?
Something like this? I want to do this because I want to change the required attribute to some form inputs...
HTML:
Name: <input type="text" ng-model="user.name" ng-required="isRequired('name')" />
Age: <input type="text" ng-model="user.age" ng-required="isRequired('age')" />
JS:
$scope.isRequired(fieldName){
$scope.requiredFields = [];
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
for (i in requiredFields) {
if (requiredFields[i] == fieldName){
return true;
}
}
return false;
}
Updated Answer:
So based on your updated OP, what you want is certainly doable. The problem with what you were trying to do is that ng-required has no ability to execute a function, it only reads a boolean. But we can dynamically create variables based on data from the server to automatically set fields to required:
Updated Plunker
<form>
Name: <input type="text" ng-model="user.test" ng-required="name" /><br/>
<input type="text" ng-model="user.name" ng-required="age" />
<br/>
<button type="submit">Submit</button>
</form>
Note that I put a $scope property for each input in the ng-required attribute. Now we can dynamically create that $scope property and set it to true if our data says we need to:
$scope.isRequired = function(){
$scope.requiredFields = [];
$http.get('fields.json')
.success(function(data){
$scope.requiredFields = angular.fromJson(data);
console.log($scope.requiredFields.required)
for (i = 0; i < $scope.requiredFields.required.length; i++) {
$scope[$scope.requiredFields.required[i]] = true
}
console.log($scope[$scope.requiredFields.required[0]]);
})
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
}
$scope.isRequired()
So it is iterating over an array of required fields received from the server, and then dynamically creating a $scope property for each one that is required, and setting it to true. Any field that has that $scope property in it's ng-required will be required now. Anything not dynamically created will just return false, and ng-required doesn't trigger.
Original answer:
Plunker
As Pratik mentioned, ng-required only accepts a Boolean value, but we can toggle the value of that with a function.
HTML
<form>
Name: <input type="text" ng-model="user.name" ng-required="isRequired" />
<br/><button ng-click="toggle()">Required: {{isRequired}}</button>
<button type="submit">Submit</button>
</form>
code:
$scope.isRequired = true;
$scope.toggle = function() {
$scope.isRequired = !$scope.isRequired;
}
I know this is a couple of years old and so AngularJS may have changed, but the accepted answer as it stands today isn't correct. You can very easily execute a function within ng-required, as it takes an expression, which can be a function. For example:
index.html
<div ng-controller="ExampleController" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ X ]
<code>{{expr}}</code> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
script.js
angular.module('expressionExample', [])
.controller('ExampleController', ['$scope', function($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}]);
In script.js, a function addExp is defined and added to the scope, and then it's called in the ng-click directive of the a tag, which also takes an expression as its argument.
This code is taken directly from the AngularJS documentation on expressions. It doesn't use ng-require directly, but any directive that takes an expression will work the same. I have used the same syntax to use a function for ng-require.

Angularjs - assigning logic between two models

I am new to Angular. It is a very simple question -
in my index.html I am defining two models on two text boxes :-
<html><head><script...></head><body ng-app="myApp"ng-controller="MainController" >
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb2" type="text"></input>
</body></html>
And in my app.js I am defining like this
var app = angular.module('myApp', []);
app.controller('MainController', ['$scope', function($scope){
$scope.tb1 = $scope.tb2;
}]);
Now, what I want is that whatever I type in first text box (tb1) automatically typed to second text box (tb2) and vise-versa, but that is not happening.
Any guess ?
Your code in controller $scope.tb1 = $scope.tb2; would only be executed once (when controller initializes), that's why it doesn't work.
You need to bind input elements to the same model then Angular will handle two-way binding for you automatically.
<input ng-model="tb1" type="text" name="numberofusers"/>
<input ng-model="tb1" type="text"></input>
Or if you want to use two different models for different elements, you can add a hook to input's ng-change event listener like
<input ng-model="tb1" type="text" name="numberofusers" ng-change="tb2 = tb1"/>
<input ng-model="tb2" type="text" ng-change="tb1 = tb2"></input>
Then these two elements would sync automatically. But you know what, ng-change can only monitor user input change, that means, if you change tb1 or tb2 programmably, ng-change will not be triggered.
In this case, you should monitor model's change using $scope.$watch
$scope.$watch('tb1', function(newValue) {
$scope.tb2 = newValue;
}));
Currently it's beyond your requirement.
This is because controller will only execute once and if there is any value in $scope.tb2 will assign to $scope.tb1 but intially both of them are blank .
So you need to $watch the changes and assign value to each other like :-
$scope.$watch('tb1',function(newVal){
$scope.tb2=newVal;
})
$scope.$watch('tb2',function(newVal){
$scope.tb1=newVal;
})
And if you want to manage it on front end you can use ng-change directive like
<input ng-model="tb1" type="text" ng-change="tb2=tb1" name="numberofusers"/>
<input ng-model="tb2" type="text" ng-change="tb1=tb2"></input>
You can use two-way binding to achieve that. An example is: JSFiddle
Create your directive:
var app = angular.module('myApp', []);
app.controller("myCtrl", function($scope) {
$scope.myName = 'Carl';
}).directive("myDirective", function() {
return {
restrict: 'AE',
scope: {
twowayBindingProp: '=myName'
}
}
});
And bind it through:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<h1>From parent: </h1>
<h3>parentProp2: <input ng-model="myName"></h3>
<div my-directive my-name="myName">
<h1>From child: </h1>
<h3>twowayBindingProp: {{ twowayBindingProp }}</h3>
<h1>Set from child:</h1>
twowayBindingProp: <input ng-model="twowayBindingProp">
</div>
</div>
</div>

angularjs fail to custom validate model array value

I don't get this sorted out. I try to add validation (for form submit deactivation if invalid) wether an array contains items or not. i tried using a custom directive but it never gets called when the model updates :(
as the angular stuff is rendered inside a play applications template and the form is not defined within my scope I cannot do some easy form invalidation.
what i try to achieve is invalidating the form until some category has been added to $scope.app.categories thereby deactivating the submit button which is also not within my scope.
here comes the code (angular version 1.2.23):
<input type="text" ng-model="app.categories" name="size" custom />
<input id="tagsinput" type="text" ng-model="new.category" on-keyup="disabled" keys="[13]"/>
<a class="btn" ng-click="addCategory()">Add</a>
// loading of app happens above this is the contoller function
$scope.addCategory = function () {
$scope.app.categories.push({name: $scope.new.category});
$scope.new.category = "";
}
app.directive('custom', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$formatters.unshift(function(val1, val2, val3, val4) {
// not even gets called would like to validate app.categories here :(
console.log(true);
});
}
};
});
You're using $formatters, but for validation you should use $validators. Instead of your line starting with ctrl.$formatters, try the following:
ctrl.$validators.yourValidatorName = function(modelValue, viewValue) {
console.log("Validator is called.");
return true; // This means validation passed.
};
You can find this in the documentation. Also do not forget to add the directive to the HTML element, so in your case <input ... custom>. To disable a submit button if validation fails, use <input type="submit" data-ng-disabled="formName.$invalid">.

Angular : Adding Attribute directive on Element Directive

For my project, i'm currently developping custom form / inputs directive.
For example, I have the following directive :
angular.module('myApp').directive("textField", function() {
return {
restrict: 'E',
replace: true,
templateUrl : "/common/tpl/form/text-field.html",
scope : {
label : "#",
model : "="
}
};
});
with the associated template :
<div class="form-group">
<label for="{{fieldId}}" class="col-lg-2 control-label">{{label |translate}}</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="{{fieldId}}" ng-model="model" placeholder="{{label|translate}}">
</div>
</div>
I have many more custom fields ( date, select, double select, and so on... )
The usage is simple :
<text-field label="app.myLabel" model="myObj.myAttribute"></text-field>
The idea is to cleanup the main template by avoiding to verbosely add labels on every fields. Very common need i believe.
Now the problem :
Now I need to add custom validation to my input models.
I did a naive approach which was to create a validation directive :
directive('myValidation', function (){
return {
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
ngModel.$parsers.unshift(function (value) {
// do some validation
return value;
});
}
};
});
and then to use it like this :
<text-field label="app.myLabel" model="myObj.myAttribute" myValidation="validationOptions"></text-field>
But of course this doesnt work, simple because the text-field directive which is replace=true "erases" the validation directive on it's element.
Can someone tell one what is the correct approach to do "custom input with presentation" directive, while allowing the validation part to be declared on the directive ( here text-field ) and used on the directive's input ?
Per example, is there a way to say "attributes on my element directive will be 'copied' to inside my directive ?"
aka :
<text-field label="app.myLabel" model="myObj.myAttribute" **myValidation="validationOptions"**></text-field>
would result in :
<div class="form-group">
<label for="{{fieldId}}" class="col-lg-2 control-label">{{label |translate}}</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="{{fieldId}}" ng-model="model" **myValidation="validationOptions"** placeholder="{{label|translate}}">
</div>
</div>
Or am I simply missing something ?
I would like to avoid using transclusion to resolve this issue, because this would oblige the form template to look like this :
<field label="myLabel">
<input type="text" class="form-control" id="{{fieldId}}" ng-model="model" placeholder= {{label|translate}}">
</field>
which is just uselessly verbose in my opinion. But i'm starting to ask myself if there really is another option ?
Maybe the trick can be done in the pre (or post ?) directve link function, where I would copy attributes/ directive from the text-field tag to it's child (input) tag ?
Could someone just light the way for me there ?
Could you try this:
Write a validate directive. This will have a controller that exposes an addValidationFunction(fn) and a getValidationFunction() methods.
Have the myValidation directive require the validate controller and call ctrl.addValidationFunction(myValidationImplementation) where myValidationImplementation is a function implementing the validation logic for this specific directive.
Write another directive, validateInner. This will require optionally the validate controller from its parent. This directive will also require the ngModel controller. If it finds the validate controller, it calls ctrl.getValidationFunction() and registers the function with the ngModel i.e.:
require: ["^?validate", "ngModel"],
link: function(scope,el,attrs,ctrls) {
if( ctrls[0] != null ) {
var validationFn = ctrls[0].getValidationFunction();
// register validationFn with ngModel = ctrls[1]
}
...
}
In the template of your textField:
<input validate-inner type="text" class="form-control" id="{{fieldId}}" ng-model="model" placeholder="{{label|translate}}">
Usage:
<text-field label="app.myLabel" model="myObj.myAttribute"
validate my-validation="validationOptions"></text-field>
NOTE 1: I am not sure if a replace:true directive wipes the other directives. If so, it is not consistent behaviour.
NOTE 2: The myValidation directive gets called as <xxx my-validation> (note camelCase → dash-case). If your code above is not a typo, then this is why <text-field> seems to wipe myValidation.

Categories