Angularjs form is readonly. Why? - javascript

How I define my app in html:
<div ng-app="md-app">
<div id="general">
<ng-include src="template1" ng-controller="GeneralCtrl"></ng-include>
</div>
</div>
The js to fetch a person from rest and set template:
function GeneralCtrl($scope, $http, $location) {
$scope.template1 = 'some_path';
$scope.person = $http.get('route', {id: personId})).then(function(response){
return response.data;
});
}
In the template I can read all data. I have a form to edit some data of the person, however the form is readonly by default:
<form>
<div class="form-group">
<input type="text" class="form-control" ng-model="person.nick"/>
</div>
</form>
The nickname of the person is displayed in the input field, but I cannot edit it. When I start typing it is just ignored. Why?

$http.get doesn't return data to your model, as it is asynchronous. It returns the Promise object. You need to assign the resulting value to $scope.person in the .success() callback:
$scope.person = {};
$http.get('route', {id: personId})).success(function(response){
$scope.person = response.data;
});
Please see the documentation for $http with examples

Related

Why is an object utilized by Angular undefined?

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

Passing ng-model from view to controller angular js

I am trying to pass value like this from view to controller in angular js of this form. I do not wish to hardcode it in this way. How could it be done in proper manner?
angular.module('user').controller('UsersController', ['$scope', '$stateParams', 'Users',
function($scope, $stateParams, Orders) {
$scope.create = function() {
var user = new Users({
child: [
{ columnA: child[0].columnA, columnB: child[0].columnB, columnC: child[0].columnC },
{ columnB: child[1].columnA, columnB: child[1].columnB, columnC: child[1].columnC },
...
{ columnC: child[10].columnA, columnB: child[10].columnB, columnC: child[10].columnC }
]
});
}
}
});
<form data-ng-submit="create()">
<input type="text" data-ng-model="child[0].columnA">
<input type="text" data-ng-model="child[0].columnB">
<input type="text" data-ng-model="child[0].columnC">
<input type="text" data-ng-model="child[1].columnA">
<input type="text" data-ng-model="child[1].columnB">
<input type="text" data-ng-model="child[1].columnC">
......
<input type="text" data-ng-model="child[10].columnA">
<input type="text" data-ng-model="child[10].columnB">
<input type="text" data-ng-model="child[10].columnC">
</form>
It would be better if an reusable directive that may perform above function.
$scope.create = function() {
child: toJSON(child);
}
function toJSON(var a) {
//automatically search through the view for ng-model with child[index].columnName and change to the form above.
}
I wrote out a plunker that demonstrates one way to do something similar to what you are trying to do using angular practices.
You'll note that I eliminated all the duplication in the view by using ng-repeat, and have made the number of child elements dynamic. I initialized the users object with an empty array, but you could easily initialize the object with data from the server.
Note also that changes to the form are immediately reflected in the object, meaning in the create() function, you can serialize the users object, not the form values. In practice, this is probably not necessary, however, since if you use an angular library like $http, serialization to and from JSON is performed automatically.
$scope.users = {
child: [{}]
};
$scope.create = function() {
var data = angular.toJson($scope.users);
alert(data);
};
$scope.addUser = function() {
$scope.users.child.push({});
};
<form ng-submit="create()">
<div ng-repeat="user in users.child">
<input type="text" ng-model="user.columnA">
<input type="text" ng-model="user.columnB">
<input type="text" ng-model="user.columnC">
</div>
<button type="submit">Submit</button>
</form>
<button ng-click="addUser()">Add New User</button>
<pre> {{users}}</pre>
The main takeaway from this, however, should be that the view and the controller work together to eliminate duplication and unnecessary references. we are no longer referring to child[0] in the HTML, making the HTML more readable and maintainable.

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.

Transferring data between controllers of different apps in Angular using a service

So I am trying to pass data from a front page form of a website to an application on the website where the user can fill out some additional information.
My problem is that every time I console.log() information after getting the object from the service, it is undefined. I can console.log() when the object is set though, and this is correct.
Any ideas of what I might be doing wrong? I'm assuming it is because I think when the service gets injected, it creates a new instance of it. How can I get the data to persist?
I have a form on the front page:
<div id="zipcodeForm" ng-app="form">
<form name="zipcodeLookup" ng-controller="FormCtrl" novalidate="true" ng-submit="zipcodeLookup.$valid && submitForm()">
<div class="error" ng-show="zipcodeLookup.$dirty && zipcodeLookup.$invalid">
<span ng-show="zipcodeLookup.zipcode.$error.pattern || zipcodeLookup.zipcode.$error.required === true">Please enter a valid zip code.<br /></span>
<span ng-show="zipcodeLookup.residence.$error.required === true">Please choose a residency type.</span>
</div>
Enter Zipcode:
<input type="text" id="zipcode" name="zipcode" required="true" ng-pattern="/^\d{{5}}(-\d{{4}})?$/" ng-model="zipcode" ng-model-options="{{debounce: 1000}}"/><br />
<input type="radio" name="residence" value="residential" ng-model="residence" ng-required="!residence"/> Residential
<input type="radio" name="residence" value="business" ng-model="residence" ng-required="!residence" /> Business
<br />
<input type="submit" id="submitButton" value="Submit" />
</form>
</div>
App Definition:
var app = angular.module('app', ['ui.router','formService']);
var formApp = angular.module('form', ['formService']);
Form Controller:
formApp.controller('FormCtrl', ['$scope', '$element', 'FormService', function($scope, $element, formService) {
$scope.zipcode = "";
$scope.residence = "";
$scope.submitForm = function() {
var obj, object, data = $element.serializeArray();
obj = {};
for (object in data) {
obj[data[object].name] = data[object].value;
}
formService.set(obj);
window.location = '/application';
};
}]);
Home Controller:
app.controller('HomeCtrl', ['FormService', function(formService) {
var data = formService.get();
console.log(data);
}]);
Service:
angular.module('formService', [])
.factory('FormService', function() {
var savedData = {};
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
};
});
you are right, there are two instances created, one for each app definition. If you want to persist data between those two instances, the only choice you have is to read and write to a global variable that is defined outside of the service.
However, this is of course not recommended at all. I wonder why you have to work with two apps that are bootstrapped on one page (I assume at least that is the case). Is there no way to combine both apps into one? If so, I would go for that strategy.

Stop AngularJS Instant Search

I have built a simple search form in my AngularJS app, that as you type uses the built in filter magic of angular to filter a list of phones. I've wrapped mine in a form as when the user submits the form it ALSO does the filter, but creates a query string so you can navigate away from the list and return etc.
HTML:
<form class="form-search" ng-submit="$parent.queryChanged()">
<div class="control-group">
<label class="control-label" for="filter">Filter:</label>
<div class="controls">
<input name="q" ng-model="$parent.query" id="filter" type="text">
</div>
</div>
</form>
JS:
phonecatControllers.controller('PhoneListCtrl', ['$scope', 'Phone', '$location',
function($scope, Phone, $location) {
$scope.query = $location.search()['q'];
$scope.queryChanged = function () {
$location.search('q', $scope.query)
}
$scope.phones = Phone.query();
$scope.orderProp = 'age';
}]);
What I want to do is disable the instant search, so the user ONLY submits the form and never gets the results on keyup alone. How do I do this?
Use 2 different variables for the ng-model of the input (ng-model="$parent.query") and the parameter of the filter (| filter:filterQuery). This will make the input and the filter unrelated to each other. Then when the form is submitted (in queryChanged), update the filter parameter with the value of the ng-model ($scope.filterQuery = $scope.query).

Categories