I've a many functions which is repeated in many controllers and for now I just copy and paste it there but what I want is to create 1 global function and inject and call it in all controller.
for example :
<div ng-bind="mycustomreturn(scopeVal)"></div>
which is the best way to do it in service or in root scope I dont want my root scope to be very large so please suggest if we can do it by service
TIA
The best way to share data in angular is by using Services:
angular.module('test', []).service('MyService', function() {
this.mycustomreturn = function() {}
})
.controller(function(MyService) {
// you have two ways
//One:
$scope.MyService = MyService;
// and the view will be:
//<p>{{MyService.mycustomreturn()}}</p>
//Two:
$scope.mycustomreturn = MyService.mycustomreturn();
// and the view will be:
//<p>{{mycustomreturn}}</p>
});
Related
I'm new on Angularjs and I'm trying to build my first application. Let's say I have to routes that loads two different views:
127.0.0.1:8080/site
127.0.0.1:8080/site_details
Maybe having two different routes is not the right procedure but that it is another problem.
I have two controllers:
Controller 1:
app.controller('controller_1', function($scope, $http, user) {
user.set('Test Example')
});
and Controller 2
app.controller('controller_2', function($scope, $http, user) {
var xxx = user.get()
});
What I want to do is to share data between these two controllers. To do that I did a service in this way:
app.factory('user', function($rootScope) {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
By looking around it seems that having a service built like this should solve the problem. However, what I obtain with the function get() in controller 2 is always an empty return.
By setting breakpoints I can see that both set() and get() functions enters in their respective function in the service.
Is this a correct procedure to share data between controllers belonging of different routes?
EDIT1
The two views are built in the same ways and the are loaded inside ng-view
<html ng-app="app" ng-controller='controller_1'>
CONTROLLER 1
</html>
First, sharing data between a service is a correct approach.
In your case, you need to ensure the order of getting data is after setting data.
Using a $timeout is not a good approach, i think there should be another way, it depend on your detail code.
If your data is set after some event, you just need to pay attention to the order sequence like 'get after data has been set'
If you have to set data in initialization of controller_1, and controller_2 is sibling of controller_1, you can put the initialization logic of user data before bother controller_1 and controller_2 is entered.
I think you had giving factory reference to both html where first and
second controller you given have. in that case you have to give factory referee to main single page where your also loading sub pages(where you kept ng-view)
The problem occurs because, controller_1 was not created before the creation of controller_2. You can modify the controller_2 to introduce some delay using $timeout:
app.controller('controller_2', function($scope, $timeout, $http, user) {
// The time out is added to check your code working,
// You can replace the code or can use, its up to your requirement
$timeout(function(){
var xxx = user.get();
console.log(xxx);
}, 500);
});
Using $timeout will allow some time for creation of controller_1.
Also instantiate the controller_2:
<html ng-app="app">
<body>
........
<div ng-controller='controller_1'>
<div ng-controller='controller_2'>
</div>
</div>
</body>
</html>
You can use rootscope like below.
app.controller('controller_1', function($scope, $http, $rootScope) {
$rootScope.UserInfo ="Test Example";
});
app.controller('controller_2', function($scope, $http, $rootScope) {
var xxx = $rootScope.UserInfo;
console.log(xxx)
});
I have a factory that needs to listen for a broadcast event. I injected $scope into the factory so I could use $scope.$on. But as soon as I add $scope to the parameter list I get an injector error.
This works fine:
angular.module('MyWebApp.services')
.factory('ValidationMatrixFactory', ['$rootScope', function($rootScope) {
var ValidationMatrixFactory = {};
return ValidationMatrixFactory;
}]);
This throws an injector error:
angular.module('MyWebApp.services')
.factory('ValidationMatrixFactory', ['$scope', '$rootScope', function($scope, $rootScope) {
var ValidationMatrixFactory = {};
return ValidationMatrixFactory;
}]);
Why can't I inject $scope into a factory? And if I can't, do I have any way of listening for events other than using $rootScope?
Because $scope is used for connecting controllers to view, factories are not really meant to use $scope.
How ever you can broadcast to rootScope.
$rootScope.$on()
Even though you can't use $scope in services, you can use the service as a 'store'. I use the following approach inspired on AltJS / Redux while developing apps on ReactJS.
I have a Controller with a scope which the view is bound to. That controller has a $scope.state variable that gets its value from a Service which has this.state = {}. The service is the only component "allowed" (by you, the developer, this a rule we should follow ourselves) to touch the 'state'.
An example could make this point a bit more clear
(function () {
'use strict';
angular.module('app', ['app.accounts']);
// my module...
// it can be defined in a separate file like `app.accounts.module.js`
angular.module('app.accounts', []);
angular.module('app.accounts')
.service('AccountsSrv', [function () {
var self = this;
self.state = {
user: false
};
self.getAccountInfo = function(){
var userData = {name: 'John'}; // here you can get the user data from an endpoint
self.state.user = userData; // update the state once you got the data
};
}]);
// my controller, bound to the state of the service
// it can be defined in a separate file like `app.accounts.controller.js`
angular.module('app.accounts')
.controller('AccountsCtrl', ['$scope', 'AccountsSrv', function ($scope, AccountsSrv) {
$scope.state = AccountsSrv.state;
$scope.getAccountInfo = function(){
// ... do some logic here
// ... and then call the service which will
AccountsSrv.getAccountInfo();
}
}]);
})();
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="AccountsCtrl">
Username: {{state.user.name ? state.user.name : 'user info not available yet. Click below...'}}<br/><br/>
Get account info
</div>
</div>
The benefit of this approach is you don't have to set $watch or $on on multiple places, or tediously call $scope.$apply(function(){ /* update state here */ }) every time you need to update the controller's state. Also, you can have multiple controllers talk to services, since the relationship between components and services is one controller can talk to one or many services, the decision is yours. This approach focus on keeping a single source of truth.
I've used this approach on large scale apps... it has worked like a charm.
I hope it helps clarify a bit about where to keep the state and how to update it.
I'm building a small two-language app with the use of angular-translate. I want to have a language switcher in every view (controller). I'm trying to figure out how to put the code responsible for language switching into every controller. The code looks like this:
var langSwitch = $Scope.setLang = function (langKey) {
$translate.use(langKey);
};
So far I've figured that I can create a factory that looks like this:
app.factory('langSwitch', function ($rootScope, $translate) {
var langSwitch = $rootScope.setLang = function (langKey) {
$translate.use(langKey);
};
return langSwitch;
});
and inject it into controllers in this maner:
app.controller('HomeCtrl', function (langSwitch) {
// normal controller code here
});
This works but 1) I'm using $rootScope and I have a feeling this is bad practice & 2) jsHint screams that "langSwitch" is not defined. Maybe there is a simpler way to make the function global without putting it into every controller?
I'm still pretty new to Angular so don't scream at me :) Thanks.
edit
My view:
<button ng-click="setLang('en_GB')">English</button>
<button ng-click="setLang('pl_PL')">Polish</button>
Although you got the idea, you overcomplicated things a bit. You could declare the service as follows:
app.service('langSwitch', function ($translate) {
this.setLang = function (langKey) {
$translate.use(langKey);
};
});
And then inject langSwitch in the controller responsible for lang switching, as you already did. No need to inject $rootScope in the service.
You don't need $rootScope indeed unless you need to process some global events in your application. All services and factories in angular are singletons by default. That means once it created, it will be passed as the same instance in every place it is declared as a dependency. So if you want to share data and functionality between different controllers - the services will suit fine. You can change your factory code to:
app.factory('langSwitch', function($translate) {
return {
setLang: function(langKey) {
$trasnlate.use(langKey);
};
};
});
I have a directive which is associated with one controller and the functions in my controller defined as
MyFormController.prototype.addNewRow = function addNewRow() {
//Adding row code
};
I want to call this method from another controller, possible ways?
I ve user the service and moved the code into that service which is shared across the controllers, however the service code does the DOM manipulation, and then i guess the next question would be that can we use $compile in a service test case
service or factory is used to share data between controller.so it would be best to define function in service and factory.
demo:
(function() {
angular.module('app', [])
.service('svc', function() {
var svc = {};
svc.method = function() {
alert(1);
}
return svc;
})
.controller('ctrl', [
'$scope', 'svc', function($scope, svc) {
svc.method();
}
]);
})();
You should not!!!
That defeats the whole purpose of modularity.
If possible try to make the function generic and create a service/factory. Now both the places where you need, use the same generic function defined in service and do their stuff.
Otherwise you can also look at events to make changes accordingly.
Look at this blog post:
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Last but the worst solution is (avoid using this, this is literally an aweful way) is catching the element inside directive and getting its scope and taking the function from it.
Example,
var otherControllerFunc = $(".inside-directive").scope().yourfunc;
I have page with two tables. I need to make some actions with both tables, triggered outside: some functions in view controller.
So, both tables has same methods and variables and I should reload or search both tables with button and input in view controller. This is it.
What I want is to separate tables - I dont want to call reloadTable1 and reloadTable2, and use table1Data and table2Data in scope to render it.
First idea was to create service for each table. But the problem was data rendering, when I use just variable to render it.
Here is example: http://plnkr.co/edit/bzgvtFzRWUDad1CR4mZV?p=preview . Not really related, but question: why it doesnt work? I know workaround - use accessors, but I dont really want to create function per variable! I want to use same class for 2 services, but with difference - one argument.
Second idea to use 3 controllers: parent and 1 per each table, reload tables with $scope.$broadcast. But I don't really like this idea: it uses events(I don't think it is good idea to use it in this case) and it will use additional controllers. Also, I think, it is good idea to use service here.
Any advices?
What I normally do if I need to share data between controllers, I use a service.
I modified your Plunker to demostrate
Example:
<div ng-controller="main">
{{service.data | json }}
<button ng-click="service.query()">refresh data</button>
</div>
<div ng-controller="controller2">
{{service.data | json }}
</div>
Controllers and service:
app.controller('controller1', function($scope, myservice) {
$scope.service = myservice;
});
app.controller('controller2', function($scope, myservice) {
$scope.service = myservice;
});
app.service('myservice', function() {
return {
data: [],
query: function() {
for (var i in [1, 2, 3]) {
this.data.push(Math.floor((Math.random() * 100) + 1));
}
}
}
});
You could also use $scope.$broadcast. Or if you want to generate the event from inside the service use $rootScope.$broadcast (you need to inject $rootScope)