Basically we have a lot of controllers and templates and we wanted to dynamically load both the controller and the template for a given route.
Something along the lines of:
$routeProvider.when('/page/:page', {
templateUrl: fuction(params) {
return '/templates/pages/' + params.page;
},
controller: function(params) {
return params.page + 'Controller';
}
})
However we don't receive the params argument for the controller function. Is there any way to achieve this kind of flexibility?
I guess you can use the $injector.instantiate function which will invoke the given function using new with the dependencies injected. Since a controller is used as a constructor you just need to wrap the controller function like this:
controller: function () {
return $injector.instantiate(function (params) { ... });
}
This is just a guess, I don't guarantee it will work.
Related
Hi I am using Angular with ES6, now I want to get rid of the $scope since Angular2 will not use it anymore and I want to create futureprove code,... this works:
let tab = this.tabManager.getArea.open({
controller: ['$scope', function (scope) {
console.log(scope);
scope.close = function () {
tab.close();
}
}],
template: '<component-name on-close="close()" ...></component-name>'
});
but how do I rewrite it without injecting the scope, I thought about something like this:
let tab = this.tabManager.getArea.open({
controller: [class {
constructor() {
console.log('construct');
}
close() {
console.log('close');
tab.close();
}
}],
template: '<component-name on-close="close()" ...></component-name>'
});
But it does not seem to work properly, the construtor is called up, however the binding for on-close does not seem to work.
the controller class in 1.6 exposes $ctrl object , can access "close()" using that.try once
So, UI-router resolves were a thing of beauty in angular 1:
$stateProvider.state('myState', {
resolve:{
myVarFromResolve: function(){
return 'test';
}
}
})
controller: function($scope, myVar){
$scope.myVar= myVarFromResolve;
if(true){console.log($scope.myVar}; //logs 'test'
}
How do I do the same with an Angular 1.5. component (example below)?
export default function FooComponent() {
let fooComponent = {
restrict: 'E',
templateUrl: 'foo.html',
controller: controller,
controllerAs: 'foo',
bindToController: true,
};
return landingComponent;
}
And the resolve...
.state('fooState', {
parent: 'someParent',
url: '/fooUrl',
template: '<foo></foo>',
resolve:{
myVarFromResolve: function(){
return 'test';
}
}
})
I read a guide on how to do this, but I don't really understand it. Seems like the functionality isn't fully in place and that's a hack.
Looks like the answer is "Yes, kinda"...
I was given a work-around to this problem by a team member that works for angular-ui-router 0.2.18 (use npm list angular-ui-router --depth=0 to check your version quickly). It does work while using ES6 classes.
The work around is based on the fact that there is no (easy/known) way to get the variable passed into the controller, like you would in Angular 1 before component architecture introduced in 1.5. Instead, we create a service method that creates a promise (or returns an http call). Then you simply put the promise in the resolve. The promise method WILL return before the controller loads. So while you do not have a easy to use var, you have the data you need in a service.
resolve: {
preloadSomeVariableIntoAService: function (MyExampleService) {
return MyExampleService.presetSomeVariable();
}
//Note that 'preloadSomeVariableIntoAService' won't be used anywhere in our case
}
The service could be something like this:
export default class MyExampleService {
constructor( $q ) {
this.q = $q;
this.myVariableFromResolve = undefined;
}
presetStreamStatus(){
var deferred = this.q.defer();
return $http.get(someUrl).then(response) => {
this.myVariableFromResolve = response.data;
deferred.resolve(events);
});
return deferred;
};
};
You will then be able to get myVariableFromResolve from the service. I am not sure you have to use $q deferred, but it seems to be necessary since I am not simply returning the call but also setting the variable. Not sure. Hope this helps someone.
I have a page in which there is a drop down, My goal is when I select any value from drop down and click on Next,It is switching to Next page using ngRouteProvider and doing $http.post , I need to pass data as payback load.
.when('/xyz/step2', {
templateUrl: partial('replacStep2.html'),
controller: 'xyzCtrl',
resolve: {
cardTypeDetails: ['$http', function ($http) {
return $http.post(appContext('xyz.json'),{});
}]
}
})
You can create a factory and set a variable inside it, later you can inject that factory inside your resolve block and access your variable through it.
resolve: {
cardTypeDetails: ['$http','sharedFactory', function ($http,sharedFactory) {
console.log(sharedFactory.sharedVariable); //use it like this
return $http.post(appContext('xyz.json'),{});
}]
}
and factory
angular.module('demoApp').factory('sharedFactory',function(){
return {
sharedVariable:null
};
});
Or in same way you can use $rootScope to pass variables (but not great for performance if you do for large no. of variables)
Passing variables from one controller to another with $rootscope
I have a scenario where I want to render the template only when some condition is met(on the basis of data we get from the REST call). So, I did the following :
My Code (by injecting the factory into the template function):
.state('dashboard.deal.xyz', {
url: "/xyz/:dealId",
resolve: {
permissions: function(dealUnit, $state) {
console.log('Inside the resolve of permissions :::', $state.params.dealId);
return dealUnit.getUnitPermissions('123412341234DealId');
}
},
views: {
"viewA": {
template: function(dealUnit) {
//Here I want to inject either 'permissions' (the resolve object)
//or the 'dealUnit' factory which gets me the permissions from the server directly.
return '<h1>return the template after getting the permissions resolve object</h1>';
}
},
"viewB": {
template: "viewB"
}
}
})
My 'dealUnit' factory is working fine and returns an object when I use it inside the 'resolve' of the state. But, that factory is not being injected when I inject it inside the template function of the nested view.
Am I doing it correctly? And if not then how should I go about doing it ?
In case, we want to do some "magic" before returning the template... we should use templateProvider. Check this Q & A:
Trying to Dynamically set a templateUrl in controller based on constant
Because template:... could be either string or function like this (check the doc:)
$stateProvider
template
html template as a string or a function that returns an html template as a string which should be used by the uiView directives. This property takes precedence over templateUrl.
If template is a function, it will be called with the following parameters:
{array.} - state parameters extracted from the current $location.path() by applying the current state
template: function(params) {
return "<h1>generated template</h1>"; }
While with templateProvider we can get anything injected e.g. the great improvement in angular $templateRequest. Check this answer and its plunker
templateProvider: function(CONFIG, $templateRequest) {
console.log('in templateUrl ' + CONFIG.codeCampType);
var templateName = 'index5templateB.html';
if (CONFIG.codeCampType === "svcc") {
templateName = 'index5templateA.html';
}
return $templateRequest(templateName);
},
I saw some sample code here Delaying AngularJS route change until model loaded to prevent flicker
And straight away I though this was the right way to go, I need to have my controller LOAD only when a resolve is finished loading, normally most of the examples around tell you to put the code under resolve in the routeprovder as an inline function, but this sounds wrong. The controller needs it so why not have the controller implement the function to resolve. This sounded just what I was looking for ie. This seems to use the prototype pattern ??
function PhoneListCtrl($scope, phones) {
$scope.phones = phones;
$scope.orderProp = 'age';
}
PhoneListCtrl.resolve = {
phones: function(Phone, $q) {
// see: https://groups.google.com/forum/?fromgroups=#!topic/angular/DGf7yyD4Oc4
var deferred = $q.defer();
Phone.query(function(successData) {
deferred.resolve(successData);
}, function(errorData) {
deferred.reject(); // you could optionally pass error data here
});
return deferred.promise;
}
}
Problem is I have my controller like so
'use strict';
angular.module('TestApp')
.controller('ItemsCtrl', function ($scope) {
});
So how do I apply a new function on the controller when my controller is declared inside a module ?
What I really need is something like
TestCtrl.resolve = {
items: function( $q) {
..........
return deferred.promise;
}
}
This then would allow me to have in my routeprovider..
when('/items', {
templateUrl: 'views/items.html',
controller: 'TestCtrl',
resolve: 'TestCtrl.resolve'}). // Need to use ' around resolve?
But I am confused how I would get this to work ?
I would really love any feedback, I am at a loss.
Not possible to define like 'TestCtrl.resolve' if you want to use resolve with .controller syntax then you have to define inline within route provider . The advantage of inline resolve in routeprovider is that you can reuse controller easily but using same controller and changing the logic in resolve function
You can also use a service:
resolve: {data : function(service) {
return service.getData();
}}