I have a Rails/AngularJS app which works fine in local development environment.
However, when I deploy this app to Heroku the AngularJS doesn't work an returns this error:
Unknown provider: eProvider <- e
I did a bit of research and it seems it has something to do with the precompiling and minification of the assets, but I don't know what to do to solve this. Any ideas? Thanks!
This is how the controller looks:
function RemindersCtrl($scope, $http) {
$http.get('/reminders.json').success(function(data) {
$scope.reminders = data;
console.log(data);
});
}
And this is the code in the view:
%section.reminders
%div{"ng-controller" => "RemindersCtrl"}
%ul
%li{"ng-repeat" => "reminder in reminders"}
.title {{reminder.title}}
Update: I changed the controller to this, but with the same result:
var RemindersCtrl = function($scope, $http) {
$http.get('/reminders.json').success(function(data) {
$scope.reminders = data;
console.log(data);
});
}
RemindersCtrl.$inject = ['$scope','$http'];
According to AngularJS tutorial (http://docs.angularjs.org/tutorial/step_05) you can either add this to the controller to prevent minification problems:
function RemindersCtrl($scope, $http) {
...
}
RemindersCtrl.$inject = ['$scope', '$http'];
or instead of defining a function like this:
function RemindersCtrl($scope, $http) {
...
}
it should be done like this:
var RemindersCtrl = ['$scope', '$http', function($scope, $http) {
...
}];
You are probably defining your controller as FooController = function($http) {}, you should define as FooController = ["$http", function($http){}]
See mroe here
Angular team (and also generally speaking) recommends that we do not pollute the global scope.
.controller method,
var myApp = angular.module('myApp',[]);
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
}]);
worked fine for me. This is documented on Angular Understanding Controllers documentation
Related
I have a directive that uses a service function like so:
angular.module('testModule',
['serviceBeingUsed'])
.directive('testDirective', function(serviceBeingUsed) {
return {
restrict: 'AE',
templateUrl: 'testTemplate.tpl.html',
scope: {
boundVar1: "="
},
link: function(scope) {
scope.getRequiredData = function(data){
//gether data using service
serviceBeingUsed.fetchRequiredData(data).then(
function(result){
scope.requiredData = result;
}
);
};
}
};
});
In the above directive I inject the service I wish to use and this service function gets used within the scope function "getRequiredData()" which is inside the "link" of this directive.
I have my test suite set up like so:
describe('test suite', function () {
var scope,
$rootScope,
$compile,
$q,
element,
isoScope,
serviceBeingUsed;
beforeEach(module('testModule'));
beforeEach( inject( function(_$rootScope_,
_$q_,
_$compile_,
_serviceBeingUsed_) {
$rootScope = _$rootScope_;
$compile = _$compile_;
serviceBeingUsed = _serviceBeingUsed_;
$q = _$q_;
//This is where we create the directive and it's options.
element = angular.element('<test-directive bound-var1="blabla"></test-directive>');
//We create a new scope from the rootScope.
scope = $rootScope.$new();
//Now we compile the HTML with the rootscope
$compile(element)(scope);
//digest the changes
scope.$digest();
//We retrieve the isolated scope scope of the directive
isoScope = element.isolateScope();
}));
Now I have a test which runs and passes wherby I can spyOn the isolated scope function "getRequiredData()", this test looks like so:
it('getRequiredData runs', inject(function () {
spyOn(isoScope,"getRequiredData");
isoScope.getRequiredData();
expect(isoScope.getRequiredData).toHaveBeenCalled();
}));
This proves that the link functions CAN be tested however when trying to test if the service function is called the test fails and I have no idea why, the test for the service looks like this:
it('serviceFunction runs', inject(function () {
spyOn(serviceBeingUsed, "serviceFunction").and.callFake(function() {
var deferred = $q.defer();
var data = "returnedDataDummy";
deferred.resolve(data);
return deferred.promise;
});
isoScope.getRequiredData();
expect(serviceBeingUsed.serviceFunction).toHaveBeenCalled();
}));
How can I successfully test if the service function has been called here?
In writing this example I have solved my issue. In my actual code, inside the test "serviceFunction runs" I had also included a spyOn(isoScope,"getRequiredData)"
This has the effect of blocking the inner functionality of the function
getRequiredData()
which meant the the service function inside getRequiredData could never run.
To resolve this issue I needed to edit the spy for the outer function
from:
spyOn(isoScope,"getRequiredData");
to:
spyOn(isoScope,"getRequiredData").and.callThrough();
this simple change means that the function being spied on will also run its inner code and not just register that it has been called.
However one important lesson that I have learned is to not do too much inside each test and to separate the tests as much as possible.
So just to clarify, my original test which failed looked like this:
it('getRequiredData runs', inject(function () {
spyOn(serviceBeingUsed, "serviceFunction").and.callFake(function() {
var deferred = $q.defer();
var data = "returnedDataDummy";
deferred.resolve(data);
return deferred.promise;
});
spyOn(isoScope,"getRequiredData");
isoScope.getRequiredData();
expect(serviceBeingUsed.fetchRequiredData).toHaveBeenCalled();
expect(isoScope.getRequiredData).toHaveBeenCalled();
}));
the fix for this test which passes:
it('getRequiredData runs', inject(function () {
spyOn(serviceBeingUsed, "serviceFunction").and.callFake(function() {
var deferred = $q.defer();
var data = "returnedDataDummy";
deferred.resolve(data);
return deferred.promise;
});
spyOn(isoScope,"getRequiredData").and.callThrough();
isoScope.getRequiredData();
expect(serviceBeingUsed.fetchRequiredData).toHaveBeenCalled();
expect(isoScope.getRequiredData).toHaveBeenCalled();
}));
I am trying to use AngularJS with RequireJS currently, but I do not know how to make the test work with injection.
Without RequireJS we could,
Impl
PhoneListCtrl.$inject = ['$scope', '$http'];
var PhoneListCtrl = ['$scope', '$http', function($scope, $http) {
/* constructor body */
}];
Test
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/phones.json').
respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
scope = $rootScope.$new();
ctrl = $controller(PhoneListCtrl, {$scope: scope});
}));
However, when we use RequireJS we may define the controller as following,
demoController.js
define(["dependency"], function() {
/* constructor body */
});
When using this controller, we add it as one of the dependencies and do not have a variable declaration.(Let me just use "Controller" as an example since we'd better call it "Service")
someJS.js
define(["demoController"], function(controller) {
controller.method();
});
My Question
How can we inject the $http, $scope(or something else) to the target controller or service for testing when using RequireJS(AMD)?
Any help would be highly appreciated.
I've done something similar:
/*global define, document */
define(['angular', 'jquery'], function (angular, $) {
'use strict';
return function () {
var $injector = angular.bootstrap(document, ['myApp']);
var $controller = $injector.get('$controller');
var myController = $controller('myController');
};
});
The idea is that angular.bootstrap returns an injector, which allows you to fetch a service.
I finally made it work by following.
angular.module('app').controller('MyController', ['$scope', 'dep2', function ($scope, dep2) {
$scope.method = function () {//do something};
}]);
We can use this controller in test cases like this:
inject(function($controller, $rootScope, dep2) {
scope = $rootScope.$new();
myController = $controller("MyController",
{
$scope : scope,
dep2: dep2
});
);
In my ExtJS 4.0.7 app I have some 3rd party javascripts that I need to dynamically load to render certain panel contents (some fancy charting/visualization widgets).
I run in to the age-old problem that the script doesn't finish loading before I try to use it. I thought ExtJS might have an elegant solution for this (much like the class loader: Ext.Loader).
I've looked at both Ext.Loader and Ext.ComponentLoader, but neither seem to provide what I'm looking for. Do I have to just "roll my own" and setup a timer to wait for a marker variable to exist?
Here's an example of how it's done in ExtJS 4.1.x:
Ext.Loader.loadScript({
url: '...', // URL of script
scope: this, // scope of callbacks
onLoad: function() { // callback fn when script is loaded
// ...
},
onError: function() { // callback fn if load fails
// ...
}
});
I've looked at both Ext.Loader and Ext.ComponentLoader, but neither
seem to provide what I'm looking for
Really looks like it's true. The only thing that can help you here, I think, is Loader's injectScriptElement method (which, however, is private):
var onError = function() {
// run this code on error
};
var onLoad = function() {
// run this code when script is loaded
};
Ext.Loader.injectScriptElement('/path/to/file.js', onLoad, onError);
Seems like this method would do what you want (here is example). But the only problem is that , ... you know, the method is marked as private.
This is exactly what newest Ext.Loader.loadScript from Ext.4-1 can be used for.
See http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Loader-method-loadScript
For all you googlers out there, I ended up rolling my own by borrowing some Ext code:
var injectScriptElement = function(id, url, onLoad, onError, scope) {
var script = document.createElement('script'),
documentHead = typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
cleanupScriptElement = function(script) {
script.id = id;
script.onload = null;
script.onreadystatechange = null;
script.onerror = null;
return this;
},
onLoadFn = function() {
cleanupScriptElement(script);
onLoad.call(scope);
},
onErrorFn = function() {
cleanupScriptElement(script);
onError.call(scope);
};
// if the script is already loaded, don't load it again
if (document.getElementById(id) !== null) {
onLoadFn();
return;
}
script.type = 'text/javascript';
script.src = url;
script.onload = onLoadFn;
script.onerror = onErrorFn;
script.onreadystatechange = function() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
onLoadFn();
}
};
documentHead.appendChild(script);
return script;
}
var error = function() {
console.log('error occurred');
}
var init = function() {
console.log('should not get run till the script is fully loaded');
}
injectScriptElement('myScriptElem', 'http://www.example.com/script.js', init, error, this);
From looking at the source it seems to me that you could do it in a bit of a hackish way. Try using Ext.Loader.setPath() to map a bogus namespace to your third party javascript files, and then use Ext.Loader.require() to try to load them. It doesn't look like ExtJS actually checks if required class is defined in the file included.
I'm trying to figure out how ExtJS4 passes around config objects.
I want to do the equivalent of...
store = function(config){
if ( typeof config.call !== 'unndefined' ){
config.url = "server.php?c=" + config.call || config.url;
};
Sketch.Data.AutoSaveStore.superclass.constructor.call(this,config);
};
Ext.extend(store, Ext.data.Store{})
I am probably missing something obvious here, but having dug around in the sandbox file, the closest I have come is....
Ext.define('My.awesome.Class', {
// what i would like to pass.
config:{},
constructor: function(config) {
this.initConfig(config);
return this;
}
});
which doesn't seem to work if you do something like...
var awesome = Ext.create('My.awesome.Class',{
name="Super awesome"
});
alert(awesome.getName()); // 'awesome.getName is not a function'
However
Ext.define('My.awesome.Class', {
// The default config
config: {
name: 'Awesome',
isAwesome: true
},
constructor: function(config) {
this.initConfig(config);
return this;
}
});
var awesome = Ext.create('My.awesome.Class',{
name="Super awesome"
});
alert(awesome.getName()); // 'Super Awesome'
This is biting me in the rear end when trying to do complex store extensions.
Anyone have any idea how I pass a bunch of random params to the prototype?
You should not be using new operator to create new instance on your class. In ExtJS4, you should use Ext.create() method.
Try doing:
var awesome = Ext.create('My.awesome.Class');
alert(awesome.getName());
And if you want to pass some param when creating an instance, you can do the following
var awesome = Ext.create('My.awesome.Class',{name:'New Awesome'});
Given the following code:
JE.events = {
self: this,
controller: {
init: function(){
$(".monthheader").click(function () {
JE.events.model.get($(this).attr('title'));
return false;
});
return this;
}
},
model: {
get: function(monthnum){
...
}
}
}
How would i replace the call to
JE.events.model.get(..);
by something like
self.model.get(..);
The whole code is more or less in this gist https://gist.github.com/966270 . The idea is to create a really simple MVC in js (my first attempt) that i can reuse easily. Improvements are welcome!
JE.events = (function {
// Create closure
// Declare controller and model as local
var Controller = {
init: function(){
$(".monthheader").click(function () {
Model.get($(this).attr('title'));
return false;
});
return this;
}
}
var Model = {
get: function(monthnum){
...
}
}
// return object thats assigned to JE.events
return {
controller: Controller,
model: Model
}
)();
You may also want to look at backbone or spine which are lightweight MVC frameworks.
They give you some simple abstractions and a lot of control. There also small and simple.
If I were to write a micro MVC framework from scratch it would converge to either backbone or spine so it might be better to use one of those two.