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

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.

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.

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.

Trying to update one input's value to another's using angularjs

I'm fairly new using AngularJS but I've been using for a pet project and I've run in to an issue. What I basically want to do is take the text input from this input field:
<form id="ci_search_form">
<p class="input-append"><label>Search:</label> <input type="text" id="query" ng:model="query" autofocus> <button ng:click="clearSearch()" class="btn"><i class="icon-remove"></i></button></p>
</form>
and update this input field's value with that value:
<div><input type="text" id="ciquery" ng:model="ciquery.Name"></div>
The second input filters some data and I can type in that directly and it works. However this page will have different sets of data, each with their own search input that I want updated by the master input at the top. I can't set value="" or use jQuery to set the value either, it just appears blank unless I explicitly type in that second input field. Can anyone assist with a solution?
EDIT
I thought I should include my app and controller code:
var App = angular.module('TicketAssistApp', []);
App.controller('SearchController', function($scope, $http, $filter){
$scope.query = '';
$http.get('static/ci_list.json').success(function(data){
$scope.ci_list = data;
});
$scope.clearSearch = function(){
$scope.query = '';
}
});
EDIT 2
Made some progress. Created a function that can be called an update ciquery in $scope:
var App = angular.module('TicketAssistApp', []);
App.controller('SearchController', function($scope, $http, $filter){
$scope.query = '';
$scope.ciquery = '';
$http.get('static/ci_list.json').success(function(data){
$scope.ci_list = data;
});
$scope.queryUpdate = function(){
$scope.ciquery = $scope.query;
}
$scope.clearSearch = function(){
$scope.query = '';
$scope.queryUpdate();
}
});
This works great. However, this creates another issue. Before in ciquery I was using ciquery.Name to filter only on the Name attribute. With this new solution I had to change it to this:
<div><input type="hidden" id="ciquery" ng:model="ciquery"></div>
This searches all fields in my data which returns unwanted results. Suggestions?
$scope and ng-model are differents. You should give ng-model's property to ng-click's function. Looks at this -> Ng-model does not update controller value
To update second input's field (here an example -> http://jsfiddle.net/yEvSL/1/)
<div><input type="text" id="ciquery" ng:model="query"></div>

Categories