Angular scope singleton - javascript

I have this controller:
class Ctrl{
constructor($http, $scope, $stateParams){
if(!$scope.items){
$http.get(...).then((res)=> {
$scope.items = res.data;
});
}
if($stateParams.id){
$scope.currItem = $scope.items[$statePrams.id];
}
}
I have 2 states:
.state('dashboard.items', {
url: '/items',
templateUrl: 'items.html',
controller:'Ctrl'
})
.state('dashboard.items.details', {
url: '/:id',
templateUrl: '/ItemDetails.html',
controller: 'Ctrl'
})
I want to be sure that items array already initialized before I show the details of one of them.
How is it possible without code duplicating? (If else and same code inside each)
Thanks.

you can use state resolve on parent level (that is exact answer on your question)
or just use service and make initialisation there. service is singleton.

Related

How to deal with specific route state url parameters on AngularJS or Ionic? How to do route state override on specific parameters?

My question is simple, but I couldn't find solution anywhere else.
For example I have a normal route state with parameter,
.state('page', {
url: '/page/:pageid',
templateUrl: 'templates/pages.html',
controller: 'pagesCtrl'
})
However for example, if I have 1000 pages, but for the Number 1 and Number 999 pages I have to use another template. How could I simply do this? Something like
.state('page', {
url: '/page/:pageid',
templateUrl: 'templates/pages.html',
controller: 'pagesCtrl'
})
.state('page', {
url: '/page/1',
templateUrl: 'templates/page1.html',
controller: 'pagesCtrl'
})
.state('page', {
url: '/page/999',
templateUrl: 'templates/page999.html',
controller: 'pagesCtrl'
})
Will this work?I tested, the later 2 options are not overriding the original state with parameter.
If I wish to use the same controller, how to load the 1 and the 999 as the pageid parameter in the controller?
Definitely you shouldn't have two more state for separate template. You should use single generic state which will take templateUrl with the help of passed state parameter.
Code
.state('page', {
url: '/page/:id',
templateUrl: function($stateParams){
var template = $stateParams.id.indexOf([1, 1000]) > -1?'pages.html'
:'page'+$stateParams.id+'.html';
return 'templates/'+template ,
}
controller: 'pagesCtrl'
})

Setting controller dynamically Angular

I have a query related to setting controllers at runtime.
I want something like:
.state{'app.thisState',
url: '/thisUrl',
views:{
templateUrl: 'templates/some_template.html',
controller: 'XYZCtrlr' //This is where I want to set different controllers depending on the scenario.
}};
How can we set controllers at runtime?
You could use controllerProvider option of ui-router state
.state ('app.thisState', { //<-- correct syntax here
url: '/thisUrl',
views: {
templateUrl: 'templates/some_template.html',
controller: 'XYZCtrlr',
controllerProvider: function($stateParams) { //<-- add dependencies here
//perform logic here
var ctrlName = $stateParams.type + "Controller";
return ctrlName; //return string name here, which will the name of controller.
}
}
};

Getting access to parent scope variables using ui router

I am currently using ui router as below: -
$stateProvider
.state('login', {
url: '/login'
, resolve: loadSequence(
'base')
, templateUrl: 'app/shared/main/login-main.html'
, controller: 'mainController'
, abstract: true
})
.state('login.signin', {
url: '/signin'
, resolve: loadSequence(
'login-items'
, 'spin'
, 'ladda'
, 'angular-ladda'
, '_loginController'
)
, templateUrl: "app/components/login/login_login.html"
, controller: 'loginController'
});
Now in loginController I want to be able to access a function in the mainController.
Is that possible with my current implementation:-
angular.module('app').controller('mainController', function($scope, $state) {
$scope.showWarning= function(){
//show warning
}
});
angular.module('app').controller('loginController', function($scope, $state) {
// I want to access $scope.showWarninghere;
});
Extract the getData() method out into a service and then you can inject it into both controllers:
angular.module('app').factory('dataService', function () {
return {
getData: function() { ... }
}
});
angular.module('app').controller('mainController', function($scope, $state, dataService) {
// You probably don't need to put this into your scope, but if you do:
$scope.getData = dataService.getData.bind(dataService);
});
angular.module('app').controller('loginController', function($scope, $state, dataService) {
dataService.getData();
});
It is useful to remember that scopes and controllers are created and destroyed as you navigate between states, so anything that actually wants to exist in more than one state really does want to be stored in a service.
As said by Duncan and Sanjay, it might indeed be a better idea to use a service to get the data, but I thought I'd answer the original question so you know :
As stated in the docs, for prototypal inheritance to be active the views must be nested, not only the states.
So in order for loginController to have access to the scope of mainController, the login-main.html template must use the uiView directive (e.g <div ui-view></div>) which will be the placeholder for the login_login.html template of the login.signin state.
you can use angular service for this.
I found a Plunker code to resolve your problem

how do I set up my angularjs controller

I'm just messing around with angular a bit and I built a simple task API. This api has assigned and accepted tasks. Now when building the app I have these routes:
TaskManager.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/:username/assigned-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController'
}).
when('/:username/accepted-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController'
}).
otherwise({
redirectTo: '/'
});
}]);
And here is the task controller I started building and then realized this was not going to work
TaskManager.controller('TaskController', ['$scope', 'AssignedTasksService', function($scope, AssignedTasksService)
{
$scope.tasks = [];
loadAssignedTasks();
function applyRemoteData( Tasks ) {
$scope.tasks = Tasks;
}
function loadAssignedTasks() {
AssignedTasksService.getAssignedTasks()
.then(
function( tasks ) {
applyRemoteData( tasks );
}
);
}
}]);
The getAssignedTasks funciton is just a function that runs a http get request to the api url and either returns and error or the api data
now as you can see the assigned tasks are automatically loaded once it hits the TaskController which is obviously a problem since I need to also be able to get accepted tasks. Now do I need to create a separate controller for accepted tasks or is there a way for maybe me to check the url from the controller and from there I can decide if I want to run the loadAssignedTasks function or the loadAcceptedTasks (which I haven't created yet). but it would just do the same thing as the loadAssignedTasks function but for the accepted tasks
As mentioned in the comments there are multiple ways to solve. All depending on current use case. But you should probably use seperate controllers to solve this problem. Also inject the data(tasks) into the controller rather than fetching them inside the controller. Consider the following example:
var resolveAssignedTasks = function(AssignedTasksService) {
return AssignedTasksService.getAssignedTasks();
};
var resolveAcceptedTasks = function(AcceptedTasksService) {
return AcceptedTasksService.getAcceptedTasks();
};
TaskManager.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/:username/assigned-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController',
resolve: {
tasks: resolveAssignedTasks
}
}).
when('/:username/accepted-tasks', {
templateUrl: 'app/partials/assigned-tasks.html',
controller: 'TaskController',
resolve: {
tasks: resolveAssignedTasks
}
}).
otherwise({
redirectTo: '/'
});
}]);
Controllers:
TaskManager.controller('AssignedTaskController', ['$scope', 'tasks', function($scope, tasks)
{
$scope.tasks = tasks;
}]);
TaskManager.controller('AcceptedTaskController', ['$scope', 'tasks', function($scope, tasks)
{
$scope.tasks = tasks;
}]);
You could also by doing this use a single controller by merging the resolveFunctions into one function that returns the appropriate tasks depending on the current route. Hope this helps.

AngularJS. Initialize controller with different data

I'm using $routeProvider for routing in my Angular app. And for 2 routes I'm using same HTML template and same Controller.
when('/products, {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl'
}).
when('/myProducts', {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl'
}).
Only difference in data that I want to show. I.e. for path products I want AJAX request to myserver:8080/products and for for path myProducts I want to load data from AJAX request to myserver:8080/products/my.
For now I i'm using $location service to distinguish the current page (products or myProducts) and load apropriate data.
Is there some more elegant way to do it? For example using resolve method of $routeProvider?
The best way to reuse controller name in today scenario is to use resolve with $routeparams.
You can modify your code as below
when('/products, {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl',
resolve: {
product: function($http) {
return $http.get('/products')
},
needToShowFilter:function($q){
var showfilter = $q.defer();
showfilter.resolve(false);
return showfilter.promise
}
}
}).
when('/myProducts', {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl',
resolve: {
product: function($http) {
return $http.get('/products/my')
},
needToShowFilter:function($q){
var showfilter = $q.defer();
showfilter.resolve(true);
return showfilter.promise
}
}
}).
And then in your controller you can inject the product into the controller code.
try to add $route in your controller, and log
$route.current
to see what you have inside, i think thats the way to get the information

Categories

Resources