AngularJS calling a controller in a directive - javascript

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 = "";
};
});

Related

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

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

how to bind a directive var to controller

i have a problem with using 2 way binding in angular, when i change my input, the change dosnt affect to controller. but the first init from controller affect directive.
in the picture i changed the value, but vm.date still have value test.
my directive:
(function (app) {
app.directive('datePicker', function () {
//Template
var template = function (element, attrs) {
htmltext =
'<input ng-readonly="true" type="text" id="' + attrs.elementId +
'" ng-model="' + attrs.model + '" type="date" />';
return htmltext;
}
//Manipulation
var link = function ($scope, elements, attrs, ctrls) {
//Declare variables we need
var el = '#' + attrs.elementId + '';
var m = attrs.model;
var jdate;
var date;
$scope[attrs.model] = [];
$(el).on('change', function (v) {
jdate = $(el).val();
gdate = moment(jdate, 'jYYYY/jMM/jDD').format('YYYY-MM-DD');
if (moment(gdate, 'YYYY-MM-DD', true).isValid()) {
date = new Date(gdate);
$scope[m][0] = date;
$scope[m][1] = jdate;
//console.log($scope[m]);
$scope.vm[m] = $scope[m];
console.log($scope.vm); //----> Here Console Write Right Data
} else {
//console.log('Oh, SomeThing is Wrong!');
}
});
} // end of link
return {
restrict: 'E',
scope: {vm: '='},
template: template,
link: link
};
});
}(angular.module('app')));
and my controller:
(function (app) {
app.controller('test', ['$scope', function ($scope) {
var vm = this;
vm.date = 'test';
vm.mydate = 'test2';
}]);
}(angular.module('app')));
and html:
<body ng-app="app">
<div ng-controller="test as vm">
<date-picker element-id="NN" model="vm.date" vm="vm"></date-picker>
<p>{{vm.date}}</p>
<date-picker element-id="NN2" model="vm.mydate" vm="vm"></date-picker>
<p>{{vm.mydate}}</p>
</div>
</body>
I am not sure why you made the textbox as readonly, but if you remove that readonly and try to update the textbox then the two way binding works. Here's the fiddle for that
https://fiddle.jshell.net/dzfe50om/
the answer:
Your controller has a date property, not a vm.date property. – zeroflagL May 25 at 13:48
You should define vm to $scope instead of this;
var vm = $scope;

Input field not being updated from controller in Angularjs

I am in learning phase of Angularjs and am stuck in a problem for last two days. I have seen lots of answer but don't know how to adapt those solutions in my case. What I want to do is update the input field via buttons using angularjs.
// html
<body ng-controller="Controller">
<input type="number" ng-model="data" update-view>
<br>
<label for="data">{{data}}</label>
<button name="btn1" ng-click='updateInput(1)'>1</button>
</body>
// js
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = val;
// val = param;
// $scope.data = val;
}
});
The expressions gets evaluated but the input field is not updating. I have seen other updating views with $setViewValue and $render but I don't know how to use them here.
app.directive('updateView', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind('change', function () {
// console.log(ngModel);
scope.$apply(setAnotherValue);
});
function setAnotherValue() {
ngModel.$setViewValue(scope.data);
ngModel.$render();
}
}
};
});
Any help would be appreciated. Thanks
You don't need a directive for updating.
You seem to be setting a string value to $scope.data, which throws an error, because the input type is number.
angular.module('calculator', [])
.controller('Controller', function($scope){
$scope.data = 0;
var val = '';
$scope.updateInput = function(n){
val = n;
$scope.data = val;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<body ng-app="calculator" ng-controller="Controller">
<input type="number" ng-model="data">
<button ng-click="updateInput(1)">1</button>
</body>
I was noting that without converting the parameter into string, the input field would update with the changed model but as soon as I would change it into String, it would not update the input field. Also there was error thrown in console. So I just, on hit and trial basis, converted it back to int by changing only one piece of line $scope.data = val; into $scope.data = parseInt(val, 10); and hurrrayyy the input field is all updating just like I wanted. And as #cither suggested, I don't need to directive for this. Following is my working code
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = parseInt(val, 10);
}
});

Angular controller's attribute not accessible outside its function

I have a form with two input fields (session.email and session.psw) bound to the LoginController.session attribute. When I click the reset button, I call the LoginController.reset() function.
I would like make it clear the session attribute, utilizing the variable sessionDefault (empty). However it works just one time, if I reset two times the form, sessionDefault is undefined.
How could I make it as a constant attribute of the controller?
app.controller('LoginController', function ($scope)
{
this.session={};
var sessionDefault=
{
email : "",
psw: ""
};
this.reset = function()
{ this.session = sessionDefault; };
});
Try out this out
for reset function just reset it with sessionDefault copy like as shown below
vm.reset = function () {
vm.session = angular.copy(sessionDefault);
};
here this refers to the controller instance
Notice that I use var vm = this; and then I decorate vm with the members that should be exposed and data-bindable to to the View. vm simply denotes view modal
This does 3 things for me.
Provides a consistent and readable method of creating bindings in my controllers
Removes any issues of dealing with this scoping or binding (i.e. closures in nested functions)
Removes $scope from the controller unless I explicitly need it for something else
Working Demo
script
var app = angular.module('myApp', []);
app.controller('LoginController', function ($scope) {
var vm = this;
vm.session = {};
var sessionDefault = {
email: "",
psw: ""
};
vm.reset = function () {
vm.session = angular.copy(sessionDefault);
};
});
html
<div ng-app='myApp' ng-controller="LoginController as login">
Email:<input type="text" ng-model="login.session.email"/>{{login.session.email}}
<br>
Psw:<input type="text" ng-model="login.session.psw"/>{{login.session.psw}}
<br>
<button ng-click="login.reset()">Reset</button>
</div>
Take a look at this beautiful stuff.
AngularJS’s Controller As and the vm Variable
this.reset = function()
{ this.session = sessionDefault; };
The this in this context refers to the function (reset). If you want to access the 'original' this you need to store it in a variable.
app.controller('LoginController', function ($scope)
{
this.session={};
var sessionDefault=
{
email : "",
psw: ""
};
var self = this;
this.reset = function()
{ self.session = angular.clone( sessionDefault); };
});

alternative method instead $watch in angularjs

I have a scope variable, when it returns true, i need to trigger some events or do something. I my case, the every first time, the scope variable returns undefined and later it returns true. In this case i used $watch method to get the expected funcionality. Is there any alternative approach to do the same instead using $watch ?
scope.$watch () ->
scope.initiateChild
, (value) ->
if value is true
$timeout ->
scope.buildOnboarding()
, 1000
You can try using AngularJS $on(), $emit() and $broadcast().
Here is an example: http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx
You can use JavaScript getters and setters without any expense of using $watch.
Write code in the setter to do what you want when angular changes the your model's value you are using in scope. It gets null or an a State object as user types. Useful for working with type ahead text boxes that have dependencies on each other. Like list of counties after typing state without user selecting anything.
Here is some pseudo style code to get the idea.
<input ng-model="searchStuff.stateSearchText" />
<div>{{searchStuff.stateObject.counties.length}}</div>
<div>{{searchStuff.stateObject.population}}</div>
$scope.searchStuff=new function(){var me=this;};
$scope.searchStuff.stateObject = null;
$scope.searchStuff.getStateObjectFromSearchText = function(search){
// get set object from search then
return stateObject;
};
$scope.searchStuff._stateSearchText= "";
Object.defineProperty($scope.searchStuff, 'stateSearchText', {
get: function () {
return me._stateSearchText;
},
set: function (value) {
me,_stateSearchText = value;
me.stateObject = getStateObjectFromSearchText (value);
}
});
See this fiddle: http://jsfiddle.net/simpulton/XqDxG/
Also watch the following video: Communicating Between Controllers
A sample example is given below
Html:
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">LOG</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
javascript:
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
ControllerZero.$inject = ['$scope', 'mySharedService'];
ControllerOne.$inject = ['$scope', 'mySharedService'];
ControllerTwo.$inject = ['$scope', 'mySharedService'];

Categories

Resources