Why is an object utilized by Angular undefined? - javascript

I'm fairly new to Angular. Here is a controller I'm working on...
svs.controller('registrationCtrl', function($scope, validatorService) {
$scope.$watch("registrationForm.email.value", function(newValue, oldValue) {
if (validatorService.validateEmail(newValue)) {
$scope.registrationForm.email.valid = true;
} else {
$scope.registrationForm.email.valid = false;
}
});
});
On the associated view, there is a text input for the user's email. It's set to have Angular use $scope.registrationForm.email.value as the model. This seems to be the case, as if I remove everything from inside the $watch function, and just do a simple console log, it logs whenever I change the value of the text input.
The idea here is to have an object at $scope.registrationForm that looks similar to this...
{
email: {
value: "someEmail#emailProvider.com",
valid: true
}
}
I'm attempting to watch the value of the text area, use a service method to validate the email, and setting the valid property of registrationForm.email to true when it is valid.
Unfortunately, I'm getting an error...
TypeError: Cannot read property 'email' of undefined
I have not explicitly defined in the JavaScript registrationForm.email.valid, nor have I made any reference to it in the HTML of my view.
Do I need to create this property before setting it? What is going on here?

yes you have to create a property before setting.
$scope.email={};

You don't have to do it like this, because... angular already makes it.
Everything you need is adding attribute name to form and to input.
<script>
angular.module('emailExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.email = {
text: 'me#example.com'
};
}]);
</script>
<form name="myForm" ng-controller="ExampleController">
<label>Email:
<input type="email" name="input" ng-model="email.text" required>
</label>
<div role="alert">
<span class="error" ng-show="myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.email">
Not valid email!</span>
</div>
<tt>text = {{email.text}}</tt><br/>
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
<tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
</form>
More details available here

Related

AngularJS not sending hidden input value

I'm trying to get data from my form in AngularJS, this all works fine except for the field I did not type anything in. I changed the field from hidden to text, but both do not work, however if you inspect element you can see the correct value in it. Here's my HTML:
<div ng-controller="postMessageCtrl as Ctrl">
<form ng-submit="processMessage()">
<div class="form-group">
<input type="message" class="form-control" placeholder="Message" ng-model="formData.message">
a{{data.receiver.id}}a
<input type="hidden" class="form-control" ng-model="formData.receiver" ng-value="data.receiver.id" />
</div>
<button type="submit" class="btn btn-primary btnq-lg btn-block">Verzenden</button>
</form>
</div>
And here's my controller:
app.controller('postMessageCtrl', function ($scope, $http, $state, localStorageService) {
$scope.formData = {};
//$scope.formData = localStorageService.get('userKey');
$scope.formData = {
key: localStorageService.get('userKey'),
message: '',
receiver: ''
};
console.log($scope.formData);
});
The key and message are filled correctly, but the receiver id is not. any suggestions?
From the answer AngularJS does not send hidden field value:
You cannot use double binding with hidden field. The solution is to use brackets:
<input type="hidden" name="someData" value="{{data}}" /> {{data}}
See this thread on GitHub: https://github.com/angular/angular.js/pull/2574
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" />
Update:
Another solution could be to directly set the value in $scope.formData rather using the hidden input field when you are initializing it:
$scope.formData = {};
//$scope.formData = localStorageService.get('userKey');
$scope.formData = {
key: localStorageService.get('userKey'),
message: '',
receiver: ''
};
$scope.formData.receiver = $scope.data.receiver.id // Set the value directly in your `formData` since you are using Angular;
console.log($scope.formData);
The simple solution is to use ngInit directive:
<input type="hidden" class="form-control"
ng-model="formData.receiver"
ng-init="formData.receiver = data.receiver.id" />
Avoid submit complexion by just handling things with a function call on a button click, like on this Plunk.
Html:
<div ng-controller="postMessageCtrl as Ctrl">
<form>
<div class="form-group">
<input type="message" class="form-control" placeholder="Message" ng-model="messageInput">
<button ng-click="Add()">Add</button>
<p></p>
<button type="submit" class="btn btn-primary btnq-lg btn-block" ng-click="Send()">Send</button>
</div>
<p></p>
<b>Messages</b>
<ul>
<li ng-repeat="message in formData.messages">{{message}}</li>
</ul>
</form>
</div>
AngularJS Controller:
app.controller("postMessageCtrl", [
"$scope",
"$http",
function($scope, $http){
var self = {};
$scope.messageInput = '';
$scope.formData = {
key: 'someUserKey',
messages: [],
receiver: null
};
$scope.Add = function(){
console.log($scope.messageInput);
if($scope.messageInput.length > 0) {
$scope.formData.messages.push($scope.messageInput);
}
};
$scope.Send = function() {
console.log($scope.formData);
$http.post("/somehost/action/", $scope.Person).success(function(data, status) {
$scope.hello = data;
});
};
}]);
The sample will have a 400 bad request error in console, because the url used is obviously not going to work, but the principle is correct.
This way you don't even need to add hidden fields, because they aren't needed (you always have their value from $scope.Person).
Conclusion:
There are 2 things that didn't make sense from your original question:
a{{data.receiver.id}}a
You should use formData here, data isn't defined.
JSON is incorrect
Receiver doesn't contain id, given your sample code, it should be defined like so:
$scope.formData = {
key: localStorageService.get('userKey'),
message: '',
receiver: {
id: 1,
name: 'SomeReceiver'
}
};
So if your receiver is set like this:
$scope.formData.receiver = $scope.formData.messages[0].receiver;
You will need to implement some way of providing that receiver through messages[0];
You'll notice that the receiver becomes an Object in the console log.

Get value input form

I would like to retrieve the password value type for the display in the console.log .
I use a html form with ng-model="passwd" to retrieve the value.
And I then uses a controller with $scope.passwd=null; to retrieve the input field.
For now, $scope.passwd remains null in google chrome => Console
'use strict';
angular.module('djoro.controllers')
.controller('WifiSmartConfigCtrl', function($scope, $window, $ionicPlatform){
$scope.passwd = null;
$scope.startSmartconfig = function(passwd){
var onSuccess = function(success){
$scope.passwd = passwd;
};
var onFail = function(){};
$ionicPlatform.ready(function(){
$window.cordova.plugins.Smartconfig.startSmartconfig(onSuccess, onFail, $scope.passwd);
console.log('Password = ' + $scope.passwd);
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form name="wifi_form" class="simple-form">
<div class="list input-fields">
<label class="item item-input" show-hide-container>
<span class="input-label">Password : </span>
<input type="password" name="password" ng-model="passwd" id="passwd" show-hide-input>
</label>
</div>
</form>
<div class="startWifi">
<button class="button button-full button-balanced" ng-click="startSmartconfig()">Start</button>
</div>
Someone an idea to edit the entered value ?
Thank you
Replace $scope.passwd = null; to $scope.passwd = '';
You are using ng-click="startSmartconfig()" and passing nothing but in controller, you have used $scope.startSmartconfig = function(passwd){ so this code will not work.
Set controller function to $scope.startSmartconfig = function(){ and another thing is angularjs is two way binding means when you add value in input text with ng-model="passwd", it also bind the textbox value to $scope.passwd.
I am not aware of $window.cordova thing but what I observed is you didn't pass passwd in ng-click="startSmartconfig()" in html and you are assigning the passwd to $scope.passwd which will be undefined.
And no need to pass passwd in function. You can directly get updated value in $scope.passwd

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.

How can a controller know that the scope is valid

I'm a little confused by validation in angular. All of the validation appears to be bound to the form. But what do you do in cases where the controller needs to know if the model is valid or not?
I've knocked up and example:
HTML
<div ng-app="stpApp">
<div id="multiStop" class="fullBottomContent" ng-controller="multiStopController">
<ul class="journey">
<li ng-repeat="journey in inboundJourney">
<ng-form name="journeyForm">
<span>
<input type="text" class="AirportName" name="DepartureAirport" ng-model="journey.DepartureAirport" ng-required="true" />
<span ng-show="journeyForm.DepartureAirport.$error.required">Invalid</span>
</ng-form>
<a class="removeJourney" ng-click="removeInboundJourney($index)" href="javascript:void(0)">Remove</a>
</li>
</ul>
<span ng-show="valid()">this is all valid</span>
<span ng-click="addInboundJourney()" title="Add a journey">+</span>
</div>
</div>
JavaScript
var stpApp = angular.module('stpApp', []);
stpApp.controller('multiStopController', function ($scope, $compile, $http) {
$scope.showAddButton = true;
$scope.dataLoaded = false;
$scope.inboundJourney = [
{ 'DepartureAirport': '',
'DestinationAirport': '',
'DepartureDate': '',
'DepartureTime': '9',
'Class': 'All'
},
{ 'DepartureAirport': 'Test1',
'DestinationAirport': '',
'DepartureDate': '',
'DepartureTime': '9',
'Class': 'All'
}
];
$scope.valid = function() {
//how do I test validity here?
return true;
}
$scope.addInboundJourney = function () {
$scope.inboundJourney.push({ 'DepartureAirport': '',
'DestinationAirport': '',
'DepartureDate': '',
'DepartureTime': 9,
'Class': ''
});
}
$scope.removeInboundJourney = function (index) {
$scope.inboundJourney.splice(index, 1);
}
});
Fiddle here
So I want my valid() function to return true or false depending on whether the data in the model is valid or not. I've tried journeyForm.$valid, $scope.journeyForm.$valid and $scope.journeyFormDepartureAirport.$valid. None of which work.
I can't figure out how to check $valid from within my controller. Especially being as I have a variable number of forms.
Also should the controller have knowledge of the forms? That's in the view?
I think the issue is that the validation all resides within the view. I'm guessing that angular has no concept of an invalid model. It's just data. But, well, that's a problem for me. I want to know that the model meets all of me criteria (added into the view, such as ng-required) before I perform an action in the controller.
To explain the comment clearer on the use of $valid
<form name="myform" >
<input type="text" name="input1" ng-required="true"/>
....
JS
$scope.valid = function() {
return $scope.myform.$valid; /* how to return $valid for 'myform' form */
}
Could you use a $watch
$scope.valid = false;
$scope.$watch('inboundJourney', function(){
var isValid = false;
//set isValid depending on inboundJourney
$scope.valid = isValid;
});
Then set it as the show condition
<span ng-show="valid">this is all valid</span>
I figured it out, Rob helped quite a bit actually. My problem was the placement of the form:
<li ng-repeat="journey in inboundJourney">
<ng-form name="journeyForm">
Angular did not seem to like the form within the ng-repeat. I changed it to:
<ng-form name="journeyForm">
<ul class="journey">
<li ng-repeat="journey in inboundJourney">
i.e. the form is outside of the ng-repeat and then did as Rob suggested,
$scope.valid = function() {
return $scope.journeyForm.$valid; /* how to return $valid for 'myform' form */
}
and it all worked
Fiddle
To moan a little this did not complain about the invalid syntax. From my limited experience of angular this is a constant issue, it's got a nasty habit of failing silently. Needz moar error pleaz

AngularJS does not send hidden field value

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"/>

Categories