I am a total newbie in AngularJs, so please be patient with me.
I have the following angular App, which contains two controllers
(function () {
angular.module("app-machines", ['ngFlatDatepicker'])
.controller('mainController', ['$scope', mainController])
.controller("machinesController", machinesController);;
function mainController($scope) {
$scope.datepickerConfig_From = {
allowFuture: true,
dateFormat: 'DD.MM.YYYY',
minDate: moment.utc('2015-09-13'),
maxDate: moment.utc('2015-09-17')
};
$scope.datepickerConfig_To = {
allowFuture: true,
dateFormat: 'DD.MM.YYYY',
minDate: moment.utc('2015-09-13'),
maxDate: moment.utc('2015-09-17')
};
$scope.date_from = "14.09.2015";
$scope.date_to = "15.09.2015";
$scope.change = function () {
//somehow execute machinesController get function
};
}
function machinesController($http) {
var vm = this;
vm.errorMessage = "";
vm.machines = [];
$http.get("/api/machine/2015-09-14_2015-09-16")
.then(function (response) {
//success
angular.copy(response.data, vm.machines);
}, function (error) {
//failure
vm.errorMessage = "Failed to load data:" + error;
});
}
})();
my machinesController is supposed to call a GET function with parameters. Here parameters are 2015-09-14 and second one is 2015-09-16 (for now they are hard coded).
What I would like to achieve is, that I have a two input controls on my main page, which trigger $scope.change function (located at the bottom of the first mainController). Here I would like to pass values of date_from and date_to to the GET function, so that I can retrieve certain values.
What I can do (in the end, if nothing works) is to copy the ode from machinesController into my mainController and that would solve the problem.
However I would like to learn how to work with this a bit better, therefore I would like to learn how to do it the proper way (in this case calling one module from the other).
What do I need to change in order to achieve this?
EDIT:
The reason why I have machinesController is, as was mentioned, to donwload the json data and show it to the user. So in the end in my html code I have the following:
<div ng-controller="machinesController as vm" class="col-md-6 col-md-offset-3">
<div class="text-danger" ng-show="vm.errorMessage"> {{ vm.errorMessage }}</div>
<table class="table table-responsive table-striped">
<tr ng-repeat="machine in vm.machines">
<td> {{ machine.name }}</td>
</tr>
</table>
</div>
Which displays a table with machine names.
you can active this is two way:
First : $broadcast and $on
//PUBLISHER
angular.module('myApp').controller('CtrlPublish', ['$rootScope', '$scope',
function ($rootScope, $scope) {
$rootScope.$broadcast('topic', 'message');
}]);
//SUBSCRIBER
angular.module('myApp').controller('ctrlSubscribe', ['$scope',
function ($scope) {
var unbind = $scope.$on('topic', function (event, arg) {
$scope.receiver = 'got your ' + arg;
});
$scope.$on('$destroy', unbind);
}]);
Second : Through common service
angular.module('myApp', [], function($provide) {
$provide.factory('msgBus', ['$rootScope', function($rootScope) {
var msgBus = {};
msgBus.emitMsg = function(msg) {
$rootScope.$emit(msg);
};
msgBus.onMsg = function(msg, scope, func) {
var unbind = $rootScope.$on(msg, func);
scope.$on('$destroy', unbind);
};
return msgBus;
}]);
});
and use it in controller like this:
controller 1
function($scope, msgBus) {
$scope.sendmsg = function() {
msgBus.emitMsg('somemsg')
}
}
controller 2
function($scope, msgBus) {
msgBus.onMsg('somemsg', $scope, function() {
// your logic
});
}
From : Post
To takes care of downloading the data you should use a factory.
Look at this answer for further details about good practices.
I modified your code to use a factory.
angular.module("app-machines", ['ngFlatDatepicker'])
.factory('MachinesService', ['$http', MachinesService])
.controller('mainController', ['$scope', 'MachinesService', mainController]);
function mainController($scope, MachinesService) {
// date pickers config...
$scope.date_from = "14.09.2015";
$scope.date_to = "15.09.2015";
$scope.change = function () {
MachinesService.getMachines($scope.date_from, $scope.date_to).then(function (response) {
vm.machines = response.data;
}, function (error) {
vm.errorMessage = "Failed to load data:" + error;
});
};
}
function MachinesService($http) {
return {
getMachines: getMachines
};
function getMachines(from, to) {
return $http.get("/api/machine/" + from + "_" + to);
}
}
Why dont u create a service instead of second controller and inject it into your main controller and use it.
May be you can refer this :
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Related
Yallo,
I have the below snippet.
I would like an explanation and perhaps a better solution provided as to why this doesn't work I have suspicions. First I bound the $scope.message to the service var equivalent messageToggle(). then I triggered the timer function timer. but the $scope.message doesn't update with it. The reason I am trying to have the method in a service is because I use the timer a decent amount through my code.
Angular
var app = angular.module('myApp', [])
app.controller('Ctrl', function($scope, Service){
$scope.status = "words"
$scope.message = Service.getMessage()
Service.timer()
})
app.service('Service', function($timeout){
var messageToggle = false
return {
getMessage: function(){
return messageToggle
},
timer: function(){
messageToggle = true;
$timeout(function (messageToggle = false) {}, 2000)
}
}
})
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="Ctrl">
<span ng-show="message"> {{status}} </span>
</div>
</div>
</body>
</html>
It doesn't work because $scope.message = Service.getMessage() does not make $scope.message reference the Service.getMessage() value, but merely ends up copying it. Thus, although the Service.getMessage() value changes, the copy in $scope.message does not, and the angular machinery can not pick up the change.
You need to propagate the change. One alternative:
app.controller('Ctrl', function($scope, Service){
$scope.status = "words"
$scope.message = Service.getMessage()
Service.timer()
$scope.$watch(function() {return Service.getMessage()}, function(message){
$scope.message = message;
});
})
Here we are adding a “manual” watch which will update your scope variable message upon change of the watched value Service.getMessage().
Live example
First thing we need to change here is to use angular.factory instead of angular.service as we are returning an object, service is a constructor function and factory is not so here is difference between the syntax of both:
app.service('myService', function() {
// service is just a constructor function
this.sayHello = function(name) {
return "Hi " + name + "!";
};
});
app.factory('myFactory', function() {
// factory returns an object
// you can run some code before
return {
sayHello : function(name) {
return "Hi " + name + "!";
}
}
});
Now, second change is to notify the change in messageTogglefrom service to controller, to do so one option is to handle the promise returned from $timeout.
So here is the syntax:
var getPromise = function () {
return $timeout(function() {
// logic here
}, 2000);
}
and in the caller function, we need to handle it that is:
getPromise().then(
function(success){
},function(error){
});
And lastly here is the working plunker for your code:
https://plnkr.co/edit/4PDuY3KgwCe7F5Ys12A6?p=preview
Where status is visible for 2000msecs and then gets invisible.
I have an application built with Node.js and AngularJS. And I am making simple CRUD operations.
In chrome, everything works fine, but in Internet Explorer, because of cache, I have to refresh data manually.
After a few hours I found a solution but it still doesn't work 100%.
For example:
Enter the name in the textbox ("Partner_name_1") and press Add
Nothing happens
Again enter the name in the textbox ("Partner_name_2") and press Add (or just simply refresh the page)
On the list below appears ("Partner_name_1")
...and you can repeat from the beginning
Here is my code
View:
form(name="AddPartner")
|Name:
input(type='text', name='name' ng-model="dataPartner.name")
button(type='add', ng-click="addPartner(dataPartner)")
| Add
table(style="width:100%")
tr(ng-repeat="partner in partnerList")
td
p {{ partner.name }}
td
button(type='remove', ng-click="removePartner(partner._id)")
| Remove
Angular script:
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config(['$routeProvider', '$httpProvider', function($routeProvider, $httpProvider) {
$httpProvider.defaults.headers.common['Pragma'] = 'no-cache';
}]);
myApp.controller("myCtrl", function($scope, $http, $timeout){
$scope.loadPartnersData = function () {
$http.get("/main/partner-list").then(function(result) {
$scope.partnerList = result.data.partnerList;
});
};
$scope.loadPartnersData();
var addPartner = '/main/addPartner'
var removePartner = '/main/removePartner'
$scope.addPartner = function(data) {
$http.post(addPartner, data)
.then(function(response) {
console.log(response);
$timeout($scope.loadPartnersData(), 5000)
});
};
$scope.removePartner = function(id) {
var data = {"id": id}
$http.post(removePartner, data)
.then(function(response) {
console.log(response);
});
$scope.loadPartnersData();
};
});
Try this:
$scope.addPartner = function(data) {
$http.post(addPartner, data)
.then(function(response) {
console.log(response);
$scope.loadPartnersData();
});
};
You were calling loadPartnersData() outside the http post promise, that caused it to run right away, probably before the add actually processed in the backend.
Will want to fix the remove, it has the same issue.
I've been searching for hours how to update a service value from a nested controller.
My child controller needs to update a value in a service. And that value needs to be shown in the parent controller.
I've made a jsfiddle to make it more clear and easy to help
http://jsfiddle.net/jtsmduxw/3/
<body ng-app="MyApp">
<div ng-controller="parentCtrl">
<p>{{username}}</p>
<div ng-controller="childCtrl">
<p>{{username}}</p>
</div>
</div>
</body>
-
var app = angular.module("MyApp", []);
app.service('authenticationSrv', function () {
var user = 'anonymous';
return {
getUser: function () {
return user;
},
setUser: function (value) {
user = value;
}
};
});
app.controller("parentCtrl", function ($scope, authenticationSrv) {
$scope.username = authenticationSrv.getUser();
});
app.controller("childCtrl", function ($scope, authenticationSrv) {
authenticationSrv.setUser('my name'); // I need this function to also update the scope of the parent
$scope.username = authenticationSrv.getUser();
});
(I've read and tried Update parent scope variable, but I could not make it work with the service.)
Thanks!
Make use of an object literal instead of the variable username.
Parent
app.controller("parentCtrl", function ($scope, authenticationSrv) {
$scope.parentObject = {};
$scope.parentObject.username = authenticationSrv.getUser();
});
Child
app.controller("childCtrl", function ($scope, authenticationSrv) {
authenticationSrv.setUser('my name');
$scope.parentObject.username = authenticationSrv.getUser();
});
Working Example
var app = angular.module("MyApp", []);
app.service('authenticationSrv', function () {
var user = 'anonymous';
return {
getUser: function () {
return user;
},
setUser: function (value) {
user = value;
}
};
});
app.controller("parentCtrl", function ($scope, authenticationSrv) {
$scope.parentObject = {};
$scope.parentObject.username = authenticationSrv.getUser();
});
app.controller("childCtrl", function ($scope, authenticationSrv) {
authenticationSrv.setUser('my name');
$scope.parentObject.username = authenticationSrv.getUser();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="MyApp">
<div ng-controller="parentCtrl">
<p>{{parentObject.username}}</p>
<div ng-controller="childCtrl">
<p>{{parentObject.username}}</p>
</div>
</div>
</body>
Make user in the Service an object instead of a primitive (string). Then use {{user.name}} in your view.
Notice that I did some minor changes to authenticationSrv.setUser()
and renamed it to authenticationSrv.setUserName().
See my working fiddle: https://jsfiddle.net/rbwk3rqb/
var app = angular.module("MyApp", []);
angular.module("MyApp")
.service('authenticationSrv', function () {
var user = {name: 'anonymous'};
return {
getUser: function () {
return user;
},
setUserName: function (value) {
user.name = value;
}
};
});
angular.module("MyApp")
.controller("parentCtrl", function ($scope, authenticationSrv) {
$scope.user = authenticationSrv.getUser();
});
angular.module("MyApp")
.controller("childCtrl", function ($scope, authenticationSrv) {
authenticationSrv.setUserName('my name');
$scope.user = authenticationSrv.getUser();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="MyApp">
<div ng-controller="parentCtrl">
<p>{{user.name}}</p>
<div ng-controller="childCtrl">
<p>{{user.name}}</p>
</div>
</div>
</body>
As user is a primitive value in the service, when you put the value from the service to your controller's scope with this line:
$scope.username = authenticationSrv.getUser();
the value of user is copied into $scope.username. So just because later on you overwrite the value of user in the service, nothing is changing in your "parent" $scope.
There are several ways to get around this, the easiest is probably to create a user object in your service - if you store the reference to this object in your scopes, it will reflect the changes made to it from other controllers. (Objects in javascript are passed by reference value, so all controllers will be affecting the same object, not copies of the values.) For the actual implementation, I'd guide you back to the same link you posted - when you tried implementing that, what was the problem with it? Show us the code you tried.
Alternatively, you can also implement the observer pattern using this service (this is considerably more work), or use events on the scope hierarchy to notify the controllers of the change of user (this is a questionable practice).
The idea is to create and object to update and not just a primitive:
$scope.user = {};
$scope.user.name = authenticateSrv.getUser();
and in the child scope you just set it:
$scope.user.name = authenticateSrv.setUser('my name');
here is a Fiddle
I have the Controller
function loginController($scope, $http, $cookieStore, $location) {
var token = $cookieStore.get('token');
var conId = $cookieStore.get('Cont_Id');
var exId = $cookieStore.get('ex_Id');
$scope.log_me = function() {
$scope.login_me = [];
var login_un = $scope.uservals;
var login_pwd = $scope.passvals;
var logs_me = "api call here";
$http.get(logs_me)
.success(function(response) {
$cookieStore.put('token', response.token);
$cookieStore.put('ex_Id', response.ExId);
$cookieStore.put('Cont_Id', response.contactId);
$cookieStore.put('email', response.email);
$cookieStore.put('name', response.name);
$scope.log_sess = response;
$scope.sess_id= response.ss_id;
alert($scope.sess_id);
if (response.status == "failure, invalid username or password") {
$('.login_error').show();
$('.login_error').html('Invalid username or password');
$('.login_error').delay(4000).fadeOut();
$('.loading').hide();
} else {
$location.path('/dashboard');
}
});
}
}
I have used the above controller in my login page and it is working fine. Now i want to use the same controller in another template and retrieve the value "$scope.sess_id"
My Template is
<div class="page" >
<style>
#report_loader object {
width: 100%;
min-height: 700px;
width:100%;
height:100%;
}
</style>
<div class="loading"> </div>
<section class="panel panel-default" data-ng-controller="loginController">
<div class="panel-body" style=" position: relative;">
<div id="report_loader" style="min-height:600px;">
{{sess_id}}
<script type="text/javascript">
$("#report_loader").html('<object data="https://sampleurl/contact/reports/members/sorted_list.html?ss_id=' + sess_id+' />');
</script>
</div>
</div>
</section>
</div>
I am unable to retrieve the value {{sess_id}} here. What should be done so that i can bring this value in my template
You're routing the user to the "dashboard" route upon successful log in. Even though it might feel like you're using the same "loginController" for both login and dashboard, it will be an entirely new instance of both the controller and $scope. Which is why the {{sess_id}} is not displaying on the dashboard template.
If you're following an MVC-like pattern of AngularJS, ideally you want to be creating a new controller for your dashboard template. See explanation: https://docs.angularjs.org/guide/controller#using-controllers-correctly
So, I would create a DashboardCtrl and share the sess_id between the two. There are plenty of examples out there of how to share data between controllers:
You can use a factory: Share data between AngularJS controllers
You can use $rootScope: How do I use $rootScope in Angular to store variables?
Hope it helps.
I would use the rootScope approach, but an easier way to do that is to simply create a 'global' variable.
In your main controller (not your login controller), define a global scope variable like this:
$scope.global = {};
Then in your login controller, modify your session id to use the global variable:
$scope.global.sess_id= response.ss_id;
alert($scope.global.sess_id);
Then in your html:
<div id="report_loader" style="min-height:600px;">
{{global.sess_id}}
It's simple and works like champ.
I would create a service :
services.sessionService = function(){
var sessionID = null;
this.setSessionID = function(id){
sessionID = id;
}
this.getSessionID = function(){
return sessionID;
}
}
then in your controller :
$scope.sess_id= response.ss_id;
alert($scope.sess_id);
sessionService.setSessionID( $scope.sess_id );
and in your dashboard controller :
$scope.sess_id = sessionService.getSessionID();
Approaches
Your question's answer has many approach. They are:
Using value or service, you can call it wherever your controllers need them.
Using $rootScope, this is very common and easy to use. Just define your $rootScope inside your main controller or whatever controller that called first and then you can call it from other controllers like any $scope behavior.
Using $controller service or usually called controller inheritance. Define this in controller function's parameter, then type $controller('ControllerNameThatIWantToInheritance', {$scope:$scope});
Maybe any other approach can be use to it. Each of them have strength and weakness.
Examples:
using value
.value('MyValue', {
key: null
})
.controller('MyCtrl', function ($scope, MyValue) {
$scope.myValue = MyValue;
})
you can modified MyValue from service too
using $rootScope
.controller('FirstCtrl', function ($scope, $rootScope) {
$rootScope.key = 'Hello world!';
})
.controller('SecondCtrl', function ($scope, $rootScope) {
console.log($rootScope.key);
})
will print 'Hello World', you can also use it in view <div>{{key}}</div>
using $controller
.controller('FirstCtrl', function ($scope) {
$scope.key = 'Hello world!';
})
.controller('SecondCtrl', function ($scope, $controller) {
$controller('FirstCtrl', {$scope:$scope});
})
Second controller will have $scope like first controller.
Conclusion
In your problem, you can split your controller for convenient. But if you dont' want to, try to define $scope.sess_id first. It will tell the Angular that your sess_id is a defined model, and angular will watch them (if you not define it first, it will be 'undefined' and will be ignored).
function loginController($scope, $http, $cookieStore, $location) {
var token = $cookieStore.get('token');
var conId = $cookieStore.get('Cont_Id');
var exId = $cookieStore.get('ex_Id');
$scope.sess_id = null //<- add this
$scope.log_me = function() {
$scope.login_me = [];
var login_un = $scope.uservals;
var login_pwd = $scope.passvals;
var logs_me = "api call here";
$http.get(logs_me)
.success(function(response) {
$cookieStore.put('token', response.token);
$cookieStore.put('ex_Id', response.ExId);
$cookieStore.put('Cont_Id', response.contactId);
$cookieStore.put('email', response.email);
$cookieStore.put('name', response.name);
$scope.log_sess = response;
$scope.sess_id= response.ss_id;
alert($scope.sess_id);
if (response.status == "failure, invalid username or password") {
$('.login_error').show();
$('.login_error').html('Invalid username or password');
$('.login_error').delay(4000).fadeOut();
$('.loading').hide();
} else {
$location.path('/dashboard');
}
});
}
}
So I have a bootstrap list:
<div class="ajax_company_list" ng-app="app">
<div class='list-group' ng-controller="PolicyController as policyCtrl">
<a href="#" class='list-group-item' ng-repeat="company in policyCtrl.companies">{{company.primary_name}}
</a>
<div id="loadingIcon" class='list-group-item'>
Loading...
</div>
</div>
</div>
Here is my Angular Javascript:
var app = angular.module('app', []);
app.controller('PolicyController', ['$scope', 'CompanyService', function($scope, CompanyService) {
$scope.companies = [
{
policy_number: 12345,
primary_name: "test"
}
];
$scope.getCompanies = function() {
CompanyService.fetchCompanies()
.success(function(data) {
$scope.companies = data.companies;
})
}
}]);
app.factory('CompanyService', ['$http', function($http) {
return {
fetchCompanies: function() {
return $http.get('http://spoonerinc:8886//json/glmod_Spooner-Inc?pagenum=1');
}
}
}]);
I basically have 2 questions. If I set $scope.companies equal to an array of objects, it does not show up but if I change $scope.companies to this.companies, it starts working again. Why is this?
2nd question, I can see the service call running in my net tab and can console.log the data and it reads fine. But it is not updating my actual list at all and I'm not sure what I'm doing wrong.
I am fairly new to Angular so if there is any advice on how I can do my code better, please let me know.
Thanks!
Because you are using the "Controller As" syntax, which effectively publishes the entire controller object to the scope.
What happens under the hood looks something like this:
function myCtrl($scope){
$scope['someAlias'] = this;
}
If you are going to use the controller as syntax, it's best to use a more object based approach instead of pushing things onto the $scope
Either on the prototype:
function myCtrl(companiesService){
this.companiesService = companiesService;
this.init();
}
myCtrl.prototype = {
init:function(){
var _this = this;
_this.companiesService.get()
.then(function(result){
_this.companies = result.data;
});
}
};
Or as closure style object:
function myCtrl(comapniesService){
var ctrl = {};
function init(){
companiesService.get()
.then(function(result){
ctrl.companies = result.data;
});
}
return ctrl;
}
For your second question, I think your problem is here:
ng-repeat="company in policyCtrl.companies"
You don't need to specify the controller as a prefix, since you've already declared it with ng-controller. It should be:
ng-repeat="company in companies"
And ng-controller to be:
ng-controller="PolicyController"
My guess is that the first problem will go away once you correct this.