I'm using an angularJS and requireJS seed which you can download here: LINK
In that seed only the controllers that are called download the relevant controller which is then triggered. I've been trying to call a factory from my controller (with no luck) plus I would like the services/factories only to download the relevant factory if it has been called.
I've attempted to require a function within the factory method (much like the controller) but it is not working.
This is where I left off: Plunkr link
user971824.
I've put together something I call couchPotato that lazy-registers just about anything in angular using requirejs and the resolution features of $routeProvider (or any other router that does lazy promise-based resolution.
I've created a plunker based on yours demonstrating how you could do it with couchPotato. If you take a look at it, I think you'll see that it's a bit simpler because you don't actually create modules for all of the things you register lazily.
couchPotato grew out of some other example apps I found on the web. What I wanted was a tight way to do the lazy registration and a provider/service combo seemed ideal. I also wanted to maintain the ability for one component to depend on another, like in your example, you want the controller to depend on the factory... couchPotato lets you specify those dependencies in requirejs syntax.
So your controller, in my rendition, looks like this:
define(['app', 'myFactory'], function(app) {
app.couchPotato.registerController([
'mycontroller',
[
'myFactory',
function(myFactory) {
var message = myFactory.getCustomers();
alert(message);
}
]
]);
});
In this example, I made the controller, the factory and the value all lazy, but you could pick and choose and have some registered the "old fashioned way" at configuration time and others registered with couchPotato when they're needed for a given route.
http://plnkr.co/edit/Z3v1mszQiiq024po8Ocp?p=preview
A couple of things to note:
1) I put in a default route in order to trigger the lazy loading of your controller, your service (factory) and the version value.
2) I modified your service to append the version just to show how one component can depend on another (the service depends on the version value, the controller depends on the service).
So, within the require configuration, you don't actually specify any of this... it's all done lazily within your route.
$routeProvider.when('/',
$couchPotatoProvider.resolveDependenciesProperty({
templateUrl:'home.html',
controller: 'mycontroller',
dependencies: [
'mycontroller'
]
})
);
Since mycontroller depends on myFactory and myFactory depends on version, by the time your route is displayed they are all available and hooked up. I put some dummy text in the home.html partial just for kicks, but the controller is assigned by the $routeProvider so you don't actually need to specify it in the template.
couchPotato lives at https://github.com/afterglowtech/angular-couchPotato if you'd like to see a couple of other samples. I shim'ed it in dependent on angular because I designed it to be usable in cases where an entire application doesn't necessarily use requirejs... thus if you are loading angular with requirejs, you need to make couchPotato dependent on angular using the shim/deps technique.
LMK if you have any questions/comments... hope this helps!
Related
I have a controllers folders in www/js/controllers where I keep all the controllers needed by my app. But when I start the app all the controllers are loaded at once. Now how can I load only those controllers whose view is being used and keep other controllers at peace.
To dynamically load controllers, you need to use RequireJS.
http://requirejs.org/
Using https://oclazyload.readme.io/docs/with-your-router
Or,
Try this:
If you are using ngRoute:
$routeProvider
.when('/url',
{
templateUrl: '/views/abc.html',
resolve: resolveController('/controllers/abc.js')
});
Using UI-Router:
.state('abc',{
url : '/abc',
templateUrl : 'views/abc.html',
resolve: resolveController('/controllers/abc.js')
})
I don't know what you are trying to ask exactly.
See, JavaScript load All files(even won't take much time) while execute the app. Note it, files load only(allocate the memory) not execute the function except on-fly function. Ex (Angularjs, Jquery, Underscore - the file load first time only but function execute when we use).
Angularjs is JS framework right?. So same like above, here we are telling to execute controller in routing or ng-controler directive as per view, Note the file was loaded before only. In single view we can execute many controllers(ng-controller) as per need.
Create Controllers & Services in IIFE based. It's good.
I previously thought that the controller script would only be executed once, when my single page application is loaded.
Now I see that every time I change a view my controller script executes again. I can tell because i had a couple of statements in the script that are not nested in functions. I want them to happen only when the app is first loaded, but they are running when I change views.
The views are configured in my app.js with module.config:
myModule.config(function ($routeProvider) {
$routeProvider
.when('/search', {
templateUrl: 'views/search.html',
controller: 'searchCtrl'
})
.when...
So, is it normal for the controller script to run when the view changes? Or should I be searching for something I have configured wrong?
Yes this is normal. It was(/is) the same for me, however I use angular-ui-router... The solution I chose in order to make sure that the data in the controller would persist was to use a factory, you could use a service or provider just as well.
"Specialized objects conform to a specific Angular framework API. These objects are one
of controllers, directives, filters or animations.
The injector needs to know how to create these objects. You tell it by registering a
"recipe" for creating your object with the injector. There are five recipe types.
The most verbose, but also the most comprehensive one is a Provider recipe. The
remaining four recipe types — Value, Factory, Service and Constant — are just
syntactic sugar on top of a provider recipe."
https://docs.angularjs.org/guide/providers
I am trying out Angular Translate and I need to use the translations inside my controller, but the controller runs the code before my translation is even loaded yet..here is my code:
var myApp = angular.module('myApp', ['pascalprecht.translate']);
angular.module('myApp').config(function ($translateProvider) {
$translateProvider.useStaticFilesLoader({
prefix: 'locales/validation-',
suffix: '.json'
});
// load 'en' table on startup
$translateProvider.preferredLanguage('en');
});
angular.module('myApp').controller('Ctrl', ['$scope', '$translate', function ($scope, $translate) {
console.debug($translate('INVALID_INTEGER'));
$scope.switchLanguage = function (key) {
$translate.uses(key);
console.debug($translate('INVALID_INTEGER'));
};
}]);
when calling the console.debug($translate('INVALID_INTEGER')); it always shows INVALID_INTEGER instead of the real translation, so I conclude that my translation is not yet loaded. I'm new to both Angular and the Angular-Translate, so I'm not sure of what to do next? Do I have to a promise, I would prefer a built-in function within angular-translate instead. I want simple locale languages which are coming from external and separate json files for each language.
EDIT
The real problem I have is that all my translations are used directly inside the controller and not in the view. It seems that angular-translate works good for whatever is displayed in the view but in the controller it doesn't refresh the code inside the controller itself.
Please note that I now have a working Beta Project on Github which is where I use the translation... Angular-Validation (form validation made easy). My project is an Angular directive and uses the locales languages, if you run the project and choose a language before loading the template then it works, but after that calling the $scope.switchLanguage() won't have any effect on the controller, though any translation on the view would work.
Ok so from the github project above it is now apparent that the translation does not work for the following reasons:
Link code from ngxValidation directive is run only once on page load and it translates the strings to whatever the current language is set to. The code never runs again unless you fully refresh the page so even if you change the language with $translate.uses() the directive has the strings already translated and cached and does not even try to translate them again.
When you click the Load Testing Form multiple times nothing happens - the view is not reloaded (and the directive is not recreated, doh!). And that is not really surprising since it's the same view! Why would angular reload something that is already loaded - it cannot know you changed the translation and you actually do want to reload in this particular, special case.
So you would have a couple solutions the simplest of which I think is not to cache the translated strings, translate only just in time - inside validator(). That should always work. If you want to be smart with caching and what not you have to find a way to ensure the cache is always valid :) And as we all know there are only two hard problems in computer science one is cache invalidation and the other one is [insert_whatever_youre_working_on_right_now_here].
In detail:
In the directive you're doing:
messages[i] = $translate('INVALID_URL');
Replace all assignments like this with just:
messages[i] = 'INVALID_URL';
And then inside the validator function do:
if(!isValid) {
isFieldValid = false;
message += $translate(messages[j]);
}
Background: Let's suppose for the sake of argument that you have 100,000 views (partials). Let's also suppose you have accompanying view-scoped controllers, and potentially view-scoped services and filters as well. Try to envision an aggregating application that hosts 100,000 disparate small applications.
Issue: When you have "partials" that require accompanying controllers, the typical solution is to do something like this:
$routeProvider.when('/app1', {
templateUrl: 'partials/view1.html',
controller: 'controller1'
});
The controller is typically loaded from index.html via:
<script src="js/directives/Controller1.js"></script>
The problem with this approach is that it doesn't scale. There are solutions out there for dynamically loading controllers, but they still require adding touch points in various config.
Ideal Solution: Ideally - again for very small applications whose numbers are in the 000's, the controller could be loaded dynamically, and from within the partial itself. This would alleviate the need to manage several files and several configuration touch points (not to mention network requests), and keep each partial very well contained.
It would look something like this:
In router:
$routeProvider.when('/apps/:appId', {
templateUrl: 'partials/app-frame.html',
controller: 'AppCtrl'
});
In containing html (app-frame) include the relatively disparate "mini app":
<h1>Currently hosting {{appId}}</h1><hr>
<div class="ng-include: appUrl"></div>
In partial resolved with appUrl, define controller and markup in one:
<script>
myApp.controller('controller1', ['$scope', function ($scope) {
$scope.foo = "bar";
}]);
</script>
<div ng-controller="controller1">
{{foo}}
</div>
For cases like this, where there are a lot of partials and a 1-1 mapping for controller and view, it can make sense to couple the two for development efficiencies and maintenance. It's a lot cleaner than using several files and additional configuration touch points.
The problem is, this doesn't work. It could be as simple as forcing the script to load prior to applying the directive... but not sure how to do that?
Here are some similar explanations of the problem:
https://groups.google.com/forum/#!topic/angular/H4haaMePJU0
Loading Partial Page With Angular and Compile The Controller
Igor from the AngularJS team says:
I see.. we looked into supporting script tags in jqlite, but what needs to be done to get a cross-browser support involves a lot of black magic. For this reason we decided that for now we are just going to recommend that users use jquery along with angular in this particular case. It doesn't make sense for us to rewrite one third of jquery to get this working in jqlite.
But I don't know what he means by "use jquery" ... JQuery is already loaded into the application from index.html (and prior to angularjs), but it sounds like I need to do something specifically within the partial itself.
You cannot add new controllers through module('app').controller(name, function() { .. }) after AngularJS bootstrap. In order make it work you should use $controllerProvider.register(name, function() { .. }).
You can override the original controller registering function in following way to be able to add controllers pre and pos bootstrap:
var app = angular.module('app', [
'ui.router'
]);
app.config(function($controllerProvider) {
app.controller = function (name, controller) {
$controllerProvider.register(name, controller);
};
});
I wonder what's the best way to configure a route which only purpose is to make use of a service call and then redirect.
I'm currently using this hack:
$routeProvider.when('/talks', {
template: '<div></div>',
controller: ['dateService', '$location', function(dateService, $location){
var nextTalk = dateService.getNextTalkDate();
$location.path('talks/' + nextTalk.format('MM') + '/' + nextTalk.format('YYYY'));
}]
});
Currently I set the controller configuration to an inline controller implementation which has dependencies on the services and then does it's thing and redirects.
However, it feels a bit weird, since I have to set template to some dummy value because otherwise the controller would not be invoked at all.
It feels as if I'm stressing the controller property for something it wasn't intended for.
I guess there should be a way to run some view unrelated code that has access to services on a route. I could right my own $routeProvider I guess but that seems to be a bit heavy for something I would consider should be built in.
Looks like '/talks' is a kinda abstract since its just used to redirect to other routes.. So how about setting up a route like this:
''/talks/:month/:year'
Where :month and :year is optional. If no month or year is given, your service returns a default talk. Which is probably the next talk. If params are given you just fetch the requested data.
So there's no redirect required. Now you specifiy the controller and the needed view and expose your data on the scope. Optionally would it be better to wrap your service call in a promise and resolve it at routeProviders resolve property.
This makes sure that the view only change if everything's resolved fine.
Hope that helps!
There's no way to inject services into anywhere into a route. Whether it be redirectTo or template, any code in there is handled on the module level. This means that the module is loaded first and then when the application has boostrapped itself then the services and injection-level code is executed. So the controller is your best bet (since that does support injection).
Controller is used in a lot of areas in AngularJS and it should work fine for what you're trying to do. You can either handle the redirection like you do in there or you can setup three different routes that point to the same controller (where you build the page).
var handler = { controller : 'Ctrl' };
$routeProvider.when('/talks', handler).
when('/talks/:month, handler).
when('/talks/:month/:year', handler);
And if you do end up using redirection, then just use $location.path(url).replace(). This will make the history stack jump back one level and therefore that the redirection triggering URL won't be in your history.