angularjs calling outer controller from nested ng controllers with ng-change - javascript

I'm trying to use angularjs to create a page that does the following:
Is initially empty, save for a dropdownlist that is automatically
populated with apps.
upon selecting one of those apps, data about it will be called from
another controller to the page.
I was successfully able to get the dropdownlist to automatically populate. however, I'm having issues getting it to make the page with ng-change, which I thing is due to the nested ng-controllers. The chartappsuccessfullogins function is not being called at all in my browser. Can anyone help me? Code is below:
My main html page. Note the use of ng-init:
<div ng-controller="chartsuccessfulapploginsController">
<div ng-controller="allappsController" ng-init="add()">
<form name="myForm">
<label for="repeatSelect"> Repeat select: </label>
<select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect" ng-change="chartsuccessfulapploginsController.add(value)">
<option ng-repeat="option in data.availableOptions" ng-init="Index = $index" value="{{data.availableOptions[Index].id}}" ng-model="APPID" >{{data.availableOptions[Index].name}}</option>
</select>
</form>
<hr>
<p> {{data}}</p>
<p> {{data.id[0]}}</p>
<p> {{data.name[0]}}</p>
<tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
</div>
<p>{{returnCount}}</p>
<table border = "1">
<tr>
<td>{{chartObject.data}}</td>
<td>{{returnCount}}</td>
</tr>
</table>
<div google-chart chart="chartObject" style="height:600px; width:100%;"></div>
</div>
My get all apps controller. The html page above relies on this to populate the dropdownlist.
angular.module('scotchApp').controller('allappsController',['$scope', function($scope){
var userurl='';
$scope.add = function(){
userurl = 'http://localhost:8085/rest/uafapp/appslist';
var userdata = {};
var userconfig =
{
headers: {
'Content-Type':'application/json'
}
};
var userPostRequest = $.get(userurl, userdata, userconfig);
var userjson = '{\"USER_DATA_RETRIEVED\" : \"fail\"}';
userPostRequest.done(function(userdata){
userjson = JSON.stringify(userdata);
console.log("userjson :: " + userjson);
var postResponse = jQuery.parseJSON(userjson);
$scope.returnName = postResponse['apps'];
var availableOptionsArray = [];
for(i = 0; i < postResponse['apps'].length; i++){
var availableOptions = {};
availableOptions['id'] = postResponse['apps'][i]['appId'];
availableOptions['name'] = postResponse['apps'][i]['appName'];
availableOptionsArray[i] = availableOptions;
}
var returnData = {};
returnData['repeatSelect'] = null;
returnData['availableOptions'] = availableOptionsArray;
$scope.data = returnData;
console.log($scope.returnData);
$scope.$apply()
});
};
}]);
Part of the controller that defines the chart. It's pretty long, so I didn't include the irrelevant code.
angular.module('scotchApp').controller('chartsuccessfulapploginsController',['$scope','$route','$http','AuthTokenService', function($scope, $route, $http, AuthTokenService){
var appurl = '';
var failedappurl= '';
$scope.add = function(APPID) {
...}

Is your allappsController within your chartsuccessfulapploginsController in your controller file?
It should be inside because allappsController is the child scope, and chartsuccessfulapploginsController is the parent scope. You are trying to access the parent scope from the child scope.
If it is not inside, it thinks that ng-change="chartsuccessfulapploginsController.add(value)" is a new controller.
If that is the issue, the fix would be something like this:
angular.module('scotchApp').controller('chartsuccessfulapploginsController',['$scope','$route','$http','AuthTokenService', function($scope, $route, $http, AuthTokenService){
var appurl = '';
var failedappurl= '';
$scope.add = function(APPID) {} ...
//allappsController inside chartsuccessfulapploginsController
angular.module('scotchApp').controller('allappsController',['$scope',function($scope){
var userurl='';
$scope.add = function(){ ... };
}]);
}]);
Check this out: Use ng-model in nested Angularjs controller

Related

AngularJS calling a controller in a directive

I am trying to follow from what I can tell so far is a pretty decent tutorial but I am a little stuck on a part where I need to create a directive to seperate a chunk of html and use a controller to generate the data.
var app = angular.module('newModule',[]);
app.directive('stateView', function(){
return{
restrict: 'E',
templateUrl: 'state-view.html',
controller: 'stateController',
controllerAs: 'stateCtrl'
}
});
app.controller('stateController',function(){
this.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: this.newState});
this.newState = "";
};
});
My HTML stateview looks like this (C is a value from another controller to itterate through a list of other objects).
<div>
<input type="text" name="state" ng-model="stateCtrl.newState">
<a href ng-click="stateCtrl.addStateTo(c)"> Add State {{ stateCtrl.newState }}</a>
</div>
and the only HTML referrence I have on my index is the following:
<state-view></state-view>
It looks clean, but the problem is that it does not reconize the function addStateTo unless I tell the DIV element that it is the ng-controller called StateController. Isn't this what the directive is telling the HTML attribute?
You are using the ControllerAs syntax and referencing the controller context appropriately (i.e. stateCtrl.newState and stateCtrl.addStateTo(c)). The problem is that you aren't creating the controller context properly. Your controller code should look like this:
app.controller('stateController', function() {
var vm = this;
this.addStateTo = function(country) {
if (!country.states) {
country.states = [];
}
country.states.push({
name: vm.newState
});
vm.newState = "";
};
});
Working example here
Try this instead ($scope instead of this):
app.controller('stateController',function($scope){
$scope.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: this.newState});
$scope.newState = "";
};
});
OR
app.controller('stateController',function(){
var vm = this;
vm.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: this.newState});
vm.newState = "";
};
});
Try adding bindto controller true in your directive. And also the above answer is correct in fixing other issues you may run into, i.e mapping your this to the function, though at present not doing that may not cause a problem.
var app = angular.module('newModule',[]);
app.directive('stateView', function(){
return{
restrict: 'E',
templateUrl: 'state-view.html',
controller: 'stateController',
controllerAs: 'stateCtrl',
bindToController: true
}
});
app.controller('stateController',function(){
var vm = this;
vm.addStateTo = function(country){
if(!country.states){
country.states = [];
}
country.states.push({name: vm.newState});
vm.newState = "";
};
});

ng-change is not firing when changing value from service

I need to reflect some changes to controller B (inside some event) when I make change at controller A. For that I am using a service.
When I am changing service value from FirstCtrl, ng-change is not firing at SecondCtrl. Is there anything I have missed or need to change?
Please note that I am using angular 1.5.6. and don't want to use watch or even scope.
Below is my code.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
Data.FirstName = self.FirstName;
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.FirstName}}
<input type="text" ng-model="c1.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
As you dont want to use $scope trying modifying the code in order to use $emit and $on feature in angular js to communicate between two controllers. You can refer this link.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
//Data.FirstName = self.FirstName;
Data.$on('emitData',function(event,args){
Data.FirstName=args.message
document.write(Data.FirstName)
})
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
Data.$emit('emitData',{
message:Data.FirstName
})
};
}
]);
The only way then is to directly copy the reference of the data object within the controller. Note that you don't need ng-change to update the value then.
If you want something else, either wrap the FirstName in a sub object of Data and do the same i did :
Data = {foo:'FirstName'};
Or use $watch since it's the whole purpose of that function.
Here is a working code with copying the Data object in the controller.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
self.Data=Data;
debugger
self.changeM = function() {
debugger
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.Data = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.Data.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.Data.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.Data.FirstName}}
<input type="text" ng-model="c1.Data.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
The only way I know to solve the problem is using watch, unfortunately. (I am new to angular.)
From the ngChange document (https://docs.angularjs.org/api/ng/directive/ngChange):
The ngChange expression is only evaluated when a change in the input value causes a new value to be committed to the model.
It will not be evaluated:
if the value returned from the $parsers transformation pipeline has not changed
if the input has continued to be invalid since the model will stay null
**if the model is changed programmatically and not by a change to the input value**

Get input value in a variable in Angularjs before submit to call a webapi

I would like to access the input field value inside a variable that could be used in AngularJS so that I could add it to a string with the help of which I could call a rest api.
kindly help.
<body ng-app="myApp">
<div ng-controller="myCtr">
<form name="myForm">
<input type="text" ng-model='pinCode' id="zip" onBlur="myZipcode">
{{city}}
{{state}}
</form>
</div>
<script>
var zip;
var pat1;
function myZipcode(){
zip = document.getElementById("zip").value;
pat1 = 'http://ziptasticapi.com/'+zip;
}
var myApp = angular.module('myApp' , []);
myApp.controller('myCtr', function($scope, $http){
var path = 'http://ziptasticapi.com/12345'
$http.get(pat1).success(function (response) {
$scope.city = response.city;
$scope.state = response.state;});
});
</script>
</body>
Here in http.get service if I use path variable instead of pat1 it works.
Another thing that I want the state and city to come dynamically without the form to be submitted and to be called from an REST API. That is why I am trying to get the input value inside a variable to accomplish the task
No need to define extra var for pinCode because of you used ng-model so you can access pinCode from your controller. Also should use ng-blur instead of onBlur.
You can use like
HTML:
<input type="text" ng-model='pinCode' id="zip" ng-blur="myZipcode()">
Controller:
var myApp = angular.module('myApp' , []);
myApp.controller('myCtr', function($scope, $http){
$scope.pinCode= ''; // defaulr empty
var path = 'http://ziptasticapi.com/';
$scope. myZipcode = function() {
$http.get(path + $scope.pinCode).success(function (response) {
$scope.city = response.city;
$scope.state = response.state;
});
};
});
You should not access html elements from your controller code. Angular's two way data-binding already transfers the form input's value into the $scope.pinCode variable. So you only need some action to trigger your server call. See this sample in the angular docs: https://docs.angularjs.org/api/ng/directive/ngSubmit
myApp.controller('myCtr', function($scope, $http) {
$scope.doCall = function() {
// $scope.pinCode() is set here
$scope.$http.get(...).then(
function(response) {
$scope.city = response.data.city; // or similar
}
);
}
});
just bind zip and pat1 on controller's scope
Controller:
myApp.controller('myCtr', function($scope, $http){
$scope.zip = document.getElementById("zip").value || 0;
$scope. pat1 = 'http://ziptasticapi.com/'+ $scope.zip || '';
$scope.myZipcode();
});
and then in zipcode
Zipcode function:
$scope.myZipcode = function myZipcode(){
$scope,zip = document.getElementById("zip").value;
$scop.pat1 = 'http://ziptasticapi.com/'+zip;
$http.get(pat1).success(function (response) {
$scope.city = response.city;
$scope.state = response.state;}
}
Complete code:
<body ng-app="myApp">
<div ng-controller="myCtr">
<form name="myForm">
<input type="text" ng-model='pinCode' id="zip" ng-blur="myZipcode">
{{city}}
{{state}}
</form>
</div>
<script>
myApp.controller('myCtr', function($scope, $http){
$scope.zip = document.getElementById("zip").value || 0;
$scope. pat1 = 'http://ziptasticapi.com/'+ $scope.zip || '';
$scope.myZipcode();
$scope.myZipcode = function myZipcode(){
$scope,zip = document.getElementById("zip").value;
$scop.pat1 = 'http://ziptasticapi.com/'+zip;
$http.get(pat1).success(function (response) {
$scope.city = response.city;
$scope.state = response.state;}
}
});
</script>
</body>

Strange behavior with variable names and data between controllers

I have a controller that is supposed to write text from an input field to the screen. If I use this controller by itself everything works as expected:
(function() {
angular.module('test', []);
function OneCtrl() {
vm = this;
vm.changeHandler = changeHandler;
vm.item = "";
vm.value = "";
}
angular
.module('test')
.controller('OneCtrl', OneCtrl);
var changeHandler = function() {
vm.value = vm.item;
console.log(vm.item);
};
})();
Try here: http://codepen.io/minuskruste/pen/qdrZqq
However, if I add another controller with the same behavior something really weird happens. First of all, the input from field 1 is not sent to console anymore and the text is also not inserted into the html body. Second of all, when I type something into input field 2 it behaves correctly. If I now go back to field 1 and type there, suddenly field 2 input is output to console, even though controller two was never told to do so! This is controller 2:
(function(){
function TwoController(){
vm = this;
vm.changeHandler = changeHandler;
vm.item = "";
vm.value = "";
}
angular
.module('test')
.controller('TwoController', TwoController);
var changeHandler = function() {
vm.value = vm.item;
};
})();
Try here: http://codepen.io/minuskruste/pen/QbpNdY
Is this normal behavior? I was very surprised by it. I also checked if maybe the changeHandler() leaked to global space but since I've put everything in closures that's not the case. Furthermore this is consistent over different platforms i.e. Chrome and FF. Any ideas?
Part of the issue you are having is that you are declaring vm without the var keyword, which makes it a global variable.
However, vm with the var keyword is in the local scope of the controller. As a result, it's not available to the changeHandler() anymore. If you reorder your code and declare changeHandler() inside the controller, it will work.
(function() {
angular.module('test', []);
function OneCtrl() {
var vm = this;
vm.item = "";
vm.value = "";
vm.changeHandler = function() {
vm.value = vm.item;
console.log(vm.item);
}
}
angular
.module('test')
.controller('OneCtrl', OneCtrl);
})();
(function(){
function TwoController() {
var vm = this;
vm.item = "";
vm.value = "";
vm.changeHandler = function() {
vm.value = vm.item;
console.log(vm.item);
}
}
angular
.module('test')
.controller('TwoController', TwoController);
})();
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<body ng-app="test">
<h1>Choice array</h1>
<div>
<form novalidate ng-controller="OneCtrl as one">
<input type="text" ng-change="one.changeHandler()" ng-model="one.item">
<div>{{one.value}}</div>
</form>
<br><br><br>
</div>
<div>
<form novalidate ng-controller="TwoController as two">
<input type="text" ng-change="two.changeHandler()" ng-model="two.item">
<div>{{two.value}}</div>
</form>
</div>
</body>
This happens because you're using global "vm" variable, which containg this reference of controller. And with second controller you're overwriting vm variable with reference to second controller.
I've updated your code to use proper this references
Also angular supports another data binding approach with special $scope object: https://docs.angularjs.org/guide/scope
(function() {
angular.module('test', []);
function OneCtrl($scope) {
// This construction makes sure I know which context is addressed. I can now hand vm (view-model) inside an object and the context doesn't change.
this.changeHandler = angular.bind(this, changeHandler);
this.item = "";
this.value = "";
}
angular
.module('test')
.controller('OneCtrl', OneCtrl);
var changeHandler = function() {
this.value = this.item;
console.log(this.item);
};
})();
(function(){
function TwoController(){
// This construction makes sure I know which context is addressed. I can now hand vm (view-model) inside an object and the context doesn't change.
this.changeHandler = angular.bind(this, changeHandler);
this.item = "";
this.value = "";
}
angular
.module('test')
.controller('TwoController', TwoController);
var changeHandler = function() {
this.value = this.item;
};
})();
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<body ng-app="test">
<h1>Choice array</h1>
<div>
<form novalidate ng-controller="OneCtrl as one">
<input
type="text"
ng-change="one.changeHandler()" ng-model="one.item">
<div>{{one.value}}</div>
</form>
<br>
<br>
<br>
</div>
<div>
<form novalidate ng-controller="TwoController as two">
<input
type="text"
ng-change="two.changeHandler()" ng-model="two.item">
<div>{{two.value}}</div>
</form>
</div>
</body>
Here is a good article on the Controller As syntax.
http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
If you declare the vm variable in each controller it will prevent the overwriting behavior you are seeing. Javascript is functional scoped, which means if it doesn't find the variable declaration for vm in the current function, it will go up the prototypical chain until it finds the declaration (var vm). If it doesn't find any declaration in the global scope, it will automatically create one for you. By declaring inside each controller you will prevent them from both sharing the same global scope.
function OneCtrl() {
// This construction makes sure I know which context is addressed. I can now hand vm (view-model) inside an object and the context doesn't change.
var vm = this;
vm.item = "";
vm.value = "";
vm.changeHandler = function() {
console.log(vm.item);
vm.value = vm.item;
};
}
http://plnkr.co/edit/KdZvG7d2COLNcjRIfyHb?p=preview

Why not input array give result in AngularJS

i am using this code to get result using AngularJS
<div ng-repeat="item in CartItems" class="row">
<input type="hidden" ng-init="cart.premium[$index] = item.bid_amount" ng-model="cart.premium[$index]">
</div>
In controller
app.controller('ShoppingCart', function($scope, MenuData, $rootScope, $http, RouteData, MyCache) {
$scope.route = RouteData.url;
$scope.user_detail = $rootScope;
var total = 0;
// Cached data
$scope.msg = 1;
$scope.CartItems = MyCache.get('CartItems');
$scope.total = MyCache.get('total');
$scope.itemsCount = MyCache.get('itemsCount');
// data is not in cache then get from server
if(!$scope.CartItems) {
};
$scope.cart = {};
$scope.check_out = function(){
if($scope.cart.total <= 9999){
alert($.param($scope.cart));
PayPalMobile.renderSinglePaymentUI($scope.createPayment1($scope.cart.total), $scope.onSuccesfulPayment, $scope.onUserCanceled);
}
};
but it cannot give any result. While when we do
<input type="hidden" name="test[]" value="test">
give the required result.
How can I achieve this thing with AngularJS?

Categories

Resources