I'm trying to overwrite a controller during run-time in Angular 1.4.
I'm working with Angular and Webpack and my ultimate goal is to be able to reload a controller with HMR (hot module replacement). However, my roadblock right now is figuring out how to have the controller update properly.
So in the end if you'd had
angular.module('app').controller('HomeController', function() {
this.message = 'Hello World!';
});
Then you modified your controller while the webpack-dev-server is running to:
angular.module('app').controller('HomeController', function() {
this.message = 'Goodbye World!';
});
The controller within the current state should update appropriately. I do have logic already in place to reload the state ($state.transitionTo)
I am using UI router and Babel, although Babel should be fairly irrelevant since I'm not using half of the benefits (i.e. classes).
Anyone have any ideas?
I'm not sure how the rest of your project is setup, but presumably your webpack config has a single entry point. Therefore it makes sense to import your controllers / services etc into that entry point sort of like an index.
Controller:
// Main.js
export default function HomeController () {
this.message = 'Hello World!'
}
Index:
// entry.js
import angular from 'angular'
import HomeController from './controllers/HomeController.js'
angular
.module('app', [])
.controller('HomeController', HomeController)
My controller changes seem to be updating correctly in this starter project I made https://github.com/alex-wilmer/app-starter/tree/angular, though it's not HMR, just live reloading.
I figured out the solution for y'all that are interested. To make HMR work with controllers in angular, effectively rewriting the body of the controller function, i bound an init function to a long-lasting JavaScript object that is outside the module (aka file).
var mod = angular.module('app').controller('HomeController', function($interval) {
mod.initHomeController(this, $interval);
});
mod.initHomeController = function(vm, $interval) {
vm.message = 'Hello World!';
};
Related
I have an angular 1.5 project with many modules and each module may depend on other modules. Trying to unit test say a controller which is part of a module I would do import the module like this:
angular.mock.module('SaidModule');
...then provide and inject its services where needed.
The problem is that SaidModule depends on AnotherModule1, AnotherModule2, AnotherModule3....
angular.module('SaidModule', ['AnotherModule1', 'AnotherModule2', 'AnotherModule3']);
So naturally when I call SaidModule the other modules are also invoked which is out of scope in terms of Unit testing
In the unit test I have tried the following solution
angular.module('AnotherModule1',[]);
angular.module('AnotherModule2',[]);
angular.module('AnotherModule3',[]);
angular.mock.module('SaidModule');
and although for the current unit test I have successfully decoupled the dependencies I have also destroyed
the actual AnotherModule1, AnotherModule2, AnotherModule3 so when its there turn to be unit tested they are
not even visible in the angular project which seems correct to me. as I am using angular.module to define a
new module which just happens to override the actual module.
This solution though is also suggested here mocking module dependencies
In the angular docs it states see angular docs mock module
If an object literal is passed each key-value pair will be registered on the module via $provide.value,
the key being the string name (or token) to associate with the value on the injector.
So it seems to me that the solution is using somehow angular.mock.module somehow to override the dependent
modules but so far I have not found a solution.
Any help much appreciated
By calling angular.module('AnotherModule1',[]) you are redefining the AnotherModule1, which I think is causing your downstream problems. Instead, use $provide for each dependent service. There's no need to mock the dependent modules.
Let's say your controller definition looks like this:
angular
.module('SaidModule', ['AnotherModule1', 'AnotherModule2'])
.controller('SaidController', [
'$scope',
'AnotherService',
function($scope, AnotherService) {
this.anotherService = AnotherService.helper();
}
);
Then your test might look like:
describe('SaidController', function() {
var controller, scope, AnotherService;
beforeEach(module('SaidModule'));
beforeEach(module(function($provide) {
AnotherService = { helper: function() { return 0; } };
$provide.value('AnotherService', AnotherService);
}));
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
controller = $controller('SaidController', {
$scope: scope
});
}));
it('creates controller', function() {
expect(controller).not.toBeNull();
});
});
There's no need to mock the dependent modules, just the dependent services.
I've using the angular fullstack generator for a long time and I recently updated it and noticed it no longer uses bower and that it uses require to get the modules in the client. My issue is that I can't understand how this works at all when I try to declare a service.
if I run, for example, yo angular-fullstack:service example it creates a example.service.js in the client/app/example path with the following code:
'use strict';
const angular = require('angular');
/*#ngInject*/
export function exampleService() {
// AngularJS will instantiate a singleton by calling "new" on this function
}
export default angular.module('sisaApp.example', [])
.service('example', exampleService)
.name;
Let's say that I want to use $http here. Do I pass it as a parameter to the function?
Apart from that, how do I go about injecting this service into a controller?
If I run yo angular-fullstack:route myroute and it generates th following controller file:
'use strict';
const angular = require('angular');
const uiRouter = require('angular-ui-router');
import routes from './myroute.routes';
export class MyrouteComponent {
/*#ngInject*/
constructor() {
this.message = 'Hello';
}
}
export default angular.module('sisaApp.myroute', [uiRouter])
.config(routes)
.component('myroute', {
template: require('./myroute.html'),
controller: MyrouteComponent,
controllerAs: 'myrouteCtrl'
})
.name;
Doing it the old was by passing it as a parameter into the constructor doesn't seem to be working for me.
Any help would be appreciated!
Thanks in advance!
I am currently building a base for AngularJS in combination with RequireJS and so far I got everything working. there's just a little thing that I do not understand at this point. I have a file which creates the angular module, when this module is created it requires a controller and assigns it to the module. The strange thing though, the controller needs the module as dependency while in the module's file the module has not been returned yet because the require statement is executed before the return statement. This somehow seems to work but it has a bad smell to it.
Module file:
// Home is defined here and can later be used in controllers (and Services)
define('home', ['require', 'angular'], function(require, angular) {
var homeModule = angular.module('AngularBase.home', ['AngularBase.core']);
homeModule.config(['$controllerProvider', '$provide', '$compileProvider', function($controllerProvider, $provide, $compileProvider) {
// We need this in order to support lazy loading
homeModule.controller = $controllerProvider.register;
homeModule.factory = $provide.factory;
// And more, not relevant at this moment
}]);
// It loads the controller that depends on this module here
require(['modules/home/controllers/homeController'], function() {
// Dependencies loaded
});
// Yet in my mind controllers that need this module can only use it when the following return statement is called.
return homeModule;
});
Controller File:
// As you can see this controller depends on home while home hasn't returned its module yet
// Yet it seems to work just fine
define(['home'], function(home) {
home.controller('homeController', ['$scope', 'homeService', function($scope, homeService) {
$scope.title = 'Home controller';
}]);
});
I assume that it is not a good approach to do it like this and therefore I need some suggestions on how to make this happen in a clean way. I thought about grabbing the AngularBase.home module via angular.module('AngularBase.home') in the controller file and defining my controller on this. This however no longer allows me to insert a mockModule for testing in this controller via RequireJS's map function.
map: {
'*' : {
'home' : 'mock-module'
}
}
Any suggestions on how to refactor this into a more clean solution?
I have found the solution to my problem. In the end it seems to be just fine to do it the way I am currently doing it. When a file is called and has a define statement in it it will wait until all dependencies are available until the function is executed. This means that the controller will actually wait for the module to finish initializing before calling its function to register itself.
The way I am doing it above is just fine.
Source: http://www.slideshare.net/iivanoo/handlebars-and-requirejs (slides 11 till 24)
In my Angular application I adding tracing functionality, which should work as separate plugin, so if script included into HTML Angular should create service and initialize (run) it. Plugin is service because it has mandatory dependency on $rootScope.
For implementation I select to use Angular factory, like (code is in myservice.js):
angular.module('app').factory('mysevice', ['$rootScope', '$interval', serviceFunc]);
function serviceFunc($rootScope, $interval) {
return {
run: function () { ... }
}
}
And now I have problem how to immediately initialize it. My solution is to enhance Angular app run function (code is in myservice.js):
window.app.run(['mysevice', function (srvc) {
srvc.run();
}]);
Where app defined in separate main app.js file like:
var app = window.app = angular.module('app', [...]);
app.run(['$state', 'breeze', ...,
function ($state, breeze, ...) { ..real initialization.. }
The code is working fine. Both main run and run for myservice calls are fine. The application together with the service are working well, but the code looks ugly, especially storing the app in the global window object and multicasting the run function.
Is there a better way to do Angular service and initialize it immediately after app starts with some other Angular services dependencies.
You can call angular.module('app').run(function(){...}). So you will not need a global variable anymore.
Example:
angular.module('app').run(['mysevice', srvc.run.bind(srvc)]);
I am trying to build a very simply demo to get AngularJS working with RequireJS.
Up to this point I have been closely following this tutorial.
I have defined a main.js file, which requires both app.js and hello.js, which are both called in turn.
app.js defines a new Angular module and returns it. hello.js then adds a controller named 'Hello' to the module.
In the page itself, the div should output 'Hello', which is returned by the sayHello method in the Hello controller. However, all my browser shows is {{sayHello}}.
I think your setup breaks the Angular bootstrap system. So you have to add the manual boostrap procedure in your hello.js file.
require(["app"], function(app) {
app.controller("Hello", ['$scope', function($scope) {
console.log('function entered');
$scope.sayHello = function() {
console.log('Hello');
return "Hello";
}
angular.element(document).ready(function() {
angular.bootstrap(document, ['app']);
});
}]
);
});
Well, it seems like Shay Friedman's solution worked.
I had seen Angularjs + RequireJs + Express Seed mentioned before, but I was sure there was a way to do the same thing by itself without too much effort. However, it seems like using this project is probably the easiest solution.