call function inside a controller with ui-router - angularjs

I am looking for a solution to call a function inside a controller. How could i achieve this.
State
.state('confirm.registration',
{ url: "/confirm/registration",
controller:'userController',
onEnter:'confirmregistration()',
permissions:{}
})
Controller
$scope.confirmregistration = function(){
//do something
}

This works. But want to know whether is this the right approach.
app.js
.state('confirmregistration',
{ url: "/confirm/registration/:registrationseed",
controller:"userController",
permissions:{}
})
Controller
if($stateParams.registrationseed){
console.log($stateParams.registrationseed);
}

I'd suggest you to put $broadcast event inside your onEnter function, that would be broadcasted in $rootscope and that event can be listen by using $on inside controller.
Config
.state('confirm.registration', {
url: "/confirm/registration",
controller: 'userController',
onEnter: function($rootScope, $timeout, $stateParams) {
//used $timeout to load after controller gets initialized
//so that $on event should get define before broadcasting
$timeout(function() {
$rootScope.$broadcast('eventBroadcasted', $stateParams.registrationseed);
})
},
permissions: {}
})
Controller
$scope.$on('eventBroadcasted', function(event, data) {
//you can get $route parameter inside data itself
});
I think this would be approach, rather than what you are doing. Thanks.

Related

Get resolve variable and pass to controller

Hi I created a model named billerModel and a route that has a resolve with a variable of billers. Now I want to retrieve and assign this variable inside my controller but I get this billerData unknown provider error. Below are my code for the route:
app.config(["$routeProvider", "$httpProvider",'$locationProvider',function($routeProvider, $httpProvider, $locationProvider) {
$routeProvider
.when("/billers", {
templateUrl: 'admin/billers/index.html',
controller: 'billerController',
resolve: {
billerData: function(billerModel) {
return billerModel.getData();
}
}
});
Below is my code for the model
app.factory('billerModel', ['$http', '$cookies', function($http, $cookies) {
return {
getData: function() {
return $http({
method: 'GET',
url: 'billers'
});
}
}
}]);
Below is my controller code that gives me the error
app.controller('billerController', ['$scope', 'billerData',
function($scope, billerData){
$scope.billers = billerData;
}]);
I also tried to remove the ng-controller from my view but if I do this then I get an error of unknown variable
<div style="text-align: left;" ng-controller="billerController">
{{ billers }}
</div>
Below is a jsfiddle but I'm not familiar on how to use it but the basic structure is included here
https://jsfiddle.net/bd06cctd/1/
Resolve data in .when blocks is only injectable into controllers defined by the .when block. Child controllers injected by the ng-controller directive can not inject resolve data.
Also if you inject billerController in the .when block and with the ng-controller directive in the .when block template, then the controller will be instantiated twice.
The $routeProvider also makes resolve data available on the view scope on the $resolve property. Child controllers instantiated by the ng-controller directive can use that.
app.controller('childController', ['$scope',
function($scope){
$scope.billers = $scope.$resolve.billerData;
}]);
Controllers instantiated by the ng-controller directive will find the $resolve property by prototypical inheritance from the view scope.
From the Docs:
resolve - {Object.<string, Function>=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired. For easier access to the resolved dependencies from the template, the resolve map will be available on the scope of the route, under $resolve (by default) or a custom name specified by the resolveAs property.
-- AngularJS $routeProvider API Reference
Could you try changing:
app.factory('billerModel', ['$http', '$cookies', function($http, $cookies) {
return {
getData: function() {
return $http({
method: 'GET',
url: 'billers'
});
}
}
}]);
to
app.factory('billerModel', ['$http', '$cookies', function($http, $cookies) {
return {
getData: $http({
method: 'GET',
url: 'billers'
});
}
}]);
You're returning an anonymous function, not a Promise

Call Ionic function from the state views options

I have a controller 'ProjectController' which has various CRUD functions inside.
I want to be able to call my API when loading a view and without having to create another controller.
Pseudo code:
.state('app.my-project', {
url: '/projects/my-projects',
views: {
'menuContent': {
templateUrl: 'templates/my_projects.html',
function: myProject()
}
}
});
And in my controller:
.controller('ProjectCtrl', function($scope, $state, $auth, $http, $ionicPopup, $rootScope) {
$scope.myProjects = function() {
// foo
}
});
So the goal is that when setting up the states, I can call a function instead of just going to a controller?
The example im using is that when setting up the state for "my projects" I can call a function from within the controller.
Instead of having to create individual controllers for each action.

inject resolve into toParams on $stateChangeStart

I have a config file which contains a state that looks like the following:
$stateProvider.state('home', {
url: '?data',
abstract: true,
resolve: {
data: ['MyModel', '$stateParams', function (MyModel, $stateParams) {
return MyModel.getData($stateParams);
}]
}
});
and then a module file that looks like this:
App.module.run(['$rootScope', '$state', function ($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
where toParams would return object{data:undefined}. I know that the resolve works and should have data. How do I correctly pass the resolve from my state into the $stateChangeStart and then access it?
I have tried injecting my data resolve manually into my module file, but it returns undefined. I have tried setting the params attribute to see if that was the way to go:
resolve: {
data: ['MyModel', '$stateParams', function (MyModel, $stateParams) {
return MyModel.getData($stateParams);
}]
},
params: {
'foo':'bar', // this would be available in toParams
'data': //can i put my data here?
}
You should simply be able to inject MyModel into your run function, and read it the same way you read it inside your resolve process. Example run function (assumes your getData function returns a promise):
App.module.run(function($rootScope, MyModel) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
// get the same data your resolve is using (you can also pass in toParams or the like)
MyModel.getData(toParams).then(function(data){
// do something with your data here inside $stateChangeStart
});
});
});
This is what makes injection great, you simply inject something when you want to use it.

Need to stop ongoing operation when controller becomes background (or invalid)

I'm writing my first AngularJS application. The landing page has two links #/view1 and #/view2. Every link will call individual controller to render <div ng-view></div> individually.
The view1Ctrl will fetch data from server periodically. I can see ajax call in console every X seconds. Now I click #/view2, the app should use view2Ctrl. I expect view1Ctrl should no longer fetch data from the server. But actually it does.
.controller('View1Ctrl', function($scope, $http) {
$scope.update = function() {
$http.get('/api/foo')
.success(function(data) { /* got new data */ })
.error(function(data) { /* error occurred */ })
.finally(function() {
setTimeout($scope.update, 1000);
});
}
$scope.update();
});
I've two questions:
Is a controller always active after it is initialized.
What is the best practice to stop any background controllers? My idea is to check the $location.path().
Update 1:
The controller is actually destroyed. But the operation update will invoke it by itself, so the operation will be called endless.
My current workaround will check whether the current location has been change. So it works. I'm looking for a more elegant way, if it exists.
.controller('View1Ctrl', function($scope, $http) {
$scope.update = function(path) {
$http.get('/api/foo')
.success(function(data) { /* got new data */ })
.error(function(data) { /* error occurred */ })
.finally(function() {
if (path === $location.path()) {
setTimeout($scope.update, 1000, path);
}
});
}
$scope.update($location.path());
});
Update 2:
A better way is to watch destruction and cancel an ongoing timeout promise. Thanks #pankajparkar
.controller('View1Ctrl', function($scope, $http, $timeout) {
var timeoutPromise;
$scope.$on("$destroy", function(){
$timeout.cancel(timeoutPromise);
});
$scope.update = function() {
$http.get('/api/foo')
.success(function(data) { /* got new data */ })
.error(function(data) { /* error occurred */ })
.finally(function() {
timeoutPromise = $timeout($scope.update, 1000);
});
}
$scope.update();
});
I think you are mentioned ng-controller on the body tag or the parent of your ng-view, In your case you should load the controller from your $routeProvider that will handle it,
Config
app.config(function($routeProvider) {
$routeProvider
.when('/view1', {
templateUrl: 'view1.html',
controller: 'view1Ctrl' //load specific controller from here
})
.when('/view2', {
templateUrl: 'view2.html',
controller: 'view2Ctrl' //load specific controller from here
})
.otherwise({
redirectTo: '/view1'
});
});
HTML
<div class="wizard">
<ng-view></ng-view>
</div>
Below are the answer of your questions.
Now when $routeProvider load view1.html the view1Ctrl will exists, as soon as user navigates to view2.html the view1Ctrl will stop its working view2Ctrl will comes into a picture as you mentioned that inside your route configuration.
You should always use $routeProvider or $stateProvider(angular-ui-router) to set up routing inside you application, and load templates and controller on basis of routing. No need to check $location.path()
Working Plunkr
Update
Your problem was with the repetitive function call, which was running in background even after controller scope has vanished from DOM.
The best way to clear out this function while redirecting to other controller.
I'd suggest you to put your $timeout promise(you used setTimeout changed it to $timeout) in one java script variable, and cancel that while leaving(unregistered the controller). While leaving controller $destroy event gets dispatched by angular, which is act like destruct-or of controller, you can use of it, you could cancel your $timeout promise inside that.
CODE
var timeoutPromise = setTimeout($scope.update, 1000);
$scope.$on("$destroy", function(){
$timeout.cancel(timeoutPromise);
});
Plunkr with canceling promise of $timeout.
Reference Link
Hope this could help you, Thanks.

Angular router with popup

I'm currently working on an app where a button triggers a method that will emit an event to elsewhere. This works great, however I also want to add a url to trigger this action.
So currently my button looks like this
<a class="addJob" ng-click="addNewJob()" ng-controller="AddJobController"></a>
But what I really want is it to just be
<a class="addJob" href="/new"></a>
Now, I can't figure out how to do the routing for this. It would mean that when I go to /new, the AddJobController should be triggered.
When I go directly to http://www.example.com/new, it should still load the page properly and trigger that action.
I don't want to create a separate page for this route as it is an essential part of the app flow.
(Think of it like when you create a new note in trello.com)
One Option
If you are willing to move to uiRouter, this is a common pattern.
Copied and pasted directly from the uiRouter FAQ
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal-at-a-certain-state
$stateProvider.state("items.add", {
url: "/add",
onEnter: ['$stateParams', '$state', '$modal', '$resource', function($stateParams, $state, $modal, $resource) {
$modal.open({
templateUrl: "items/add",
resolve: {
item: function() { new Item(123).get(); }
},
controller: ['$scope', 'item', function($scope, item) {
$scope.dismiss = function() {
$scope.$dismiss();
};
$scope.save = function() {
item.update().then(function() {
$scope.$close(true);
});
};
}]
}).result.then(function(result) {
if (result) {
return $state.transitionTo("items");
}
});
}]
})
Second Option
The second options would be to launch the modal the constructor of your controller. I have included a modalFactory. This is a common pattern. It allows your modals to be reusable, and cleans up your controllers. The uiRouter example above should use the factory pattern as well to abstract the modal setup out of the state config.
This example should work with ngRouter.
app.controller('addJobModalController', ['modalFactory', function(modalFactory) {
modalFactory.addJob();
}]);
app.factory('modalFactory', ['$modal', function($modal) {
return {
addJob: function() {
return $modal.open({
templateUrl: 'views/addjob-modal.html',
controller: 'AddJobController',
size: 'md'
});
}
}
}]);
The addJob() method returns the modal's promise. If you want, you can store this promise in the factory to be returned by another method so that another controller or service can act on the result of the modal.

Resources