Simple Angular SPA in ASP .NET 5 - getting blank page on startup - javascript

I'm attempting to set up a very simple Angular single-page-application in ASP .NET 5. I've begun with an empty project - the only angular dependency at the moment is ngRoute.
The problem:
When I run the project, I get a blank page in my browser - with no errors in the developer console.
EDIT
I removed the [] from the angular.module('app', []).controller as suggested but now an error is thrown:
Uncaught Error: [$injector:modulerr] Failed to instantiate module app due to:
Error: [$injector:unpr] Unknown provider: a
I'm using npm, bower, and grunt - but I don't think they have anything to do with the problem.
Here's what the project structure looks like:
Here's the index.html:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title></title>
<!-- angular -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-route.js"></script>
<!-- app -->
<script src="app.js"></script>
</head>
<body ng-cloak>
<div ng-view>
</div>
</body>
</html>
Here's the app.js:
(function () {
'use strict';
var app = angular.module('app', ['ngRoute']);
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'Views/home.html',
controller: 'home'
})
.otherwise({
redirectTo: '/'
});
});
})();
Here's the home.js:
(function () {
'use strict';
angular.module('app').controller('home', function controller($scope) {
$scope.title = 'Home';
});
})();
Here's the home.html:
<div ng-controller="home">
{{ title }}
</div>
Why do I get a blank page and not the text "Home" rendered on screen?
The app.js under wwwroot contains all of the contents of the Scripts folder - with the app.js contents written first. Here's the gruntfile.js:
module.exports = function (grunt) {
// Load grunt plugins from npm.
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-watch");
// Configure plugins.
grunt.initConfig({
// Combine and minify all of the javascript files from the Scripts folder into the wwwroot
// folder, making sure the app.js is placed at the beginning.
uglify: {
my_target: {
files: { "wwwroot/app.js": ["Scripts/App/app.js", "Scripts/**/*.js"] }
}
},
// Re-run the uglify task when any of the files in the Scripts folder change.
watch: {
scripts: {
files: ["Scripts/**/*.js"],
tasks: ["uglify"]
}
}
});
// Define tasks.
grunt.registerTask("default", ["uglify", "watch"]);
};
I can see that the angular.js, angular-route.js, and app.js have loaded correctly in the browser. Here's the contents of the "uglified" app.js:
!function(){"use strict";var a=angular.module("app",["ngRoute"]);a.config(function(a){a.when("/",{templateUrl:"Views/home.html",controller:"home"}).otherwise({redirectTo:"/"})})}(),function(){"use strict";angular.module("app",[]).controller("home",function(a){a.title="Home"})}();

Claies is right when it comes to app initialization, but there is another thing.
You problem is much more complicated than you think. You are using uglifyjs, which changes names of variables in controller arguments. You need to use ngannotate in gruntfile.js or switch to longer controller definition.
This is brief explanation:
uglify wants to make your JS files lighter and changes it from this:
myApp.controller('GreetingController', function($scope, service) {
$scope.greeting = 'Hola!';
service.fnc();
});
To this:
myApp.controller('GreetingController', function(a,b) {
a.greeting = 'Hola!';
b.fnc();
});
This causes problems for angular because it does not know what a is.
If you want to solve it without changing your controllers, you can use ngannotate task. Alternative way is changing controller's definition:
myApp.controller('GreetingController', ['$scope', 'service', function($scope, service) {
$scope.greeting = 'Hola!';
service.fnc();
}]);
Uglify will transform that to:
myApp.controller('GreetingController', ['$scope', 'service', function(a, b) {
a.greeting = 'Hola!';
b.fnc();
}]);
NOTE: Please have in mind that using ngannotate is probably better way, beacause you will not face same problems with 3rd party services etc.

Your home.js has a subtle bug. You are re-declaring your app module, which is removing all the route configuration.
angular.module('app', []).controller... is the setter syntax for a module. Instead, you should use angular.module('app').controller getter syntax, to avoid re-declaring the module.

Related

Angularjs - Dynamic configuration based on Environment

I have a sample angular APP - app.js
angular
.module('myUiApp', [
'restangular',
'ngRoute',
'ngCookies',
'ui.bootstrap',
'ui.sortable',
'smart-table',
'config'
])
.config(function($routeProvider, $httpProvider, $sceProvider, $logProvider, RestangularProvider, config) {
RestangularProvider.setBaseUrl(config.apiBaseUrl);
RestangularProvider.setDefaultHeaders({'Content-Type': 'application/json'});
//routing here
.....
});
my Config.js looks like -
angular.module('config', []).service('config', function($location, ENV) {
return ENV.dev;
});
my constants.js looks like -
'use strict';
angular.module('config', []).constant('ENV', (function() {
return {
dev: {
appBaseUrl:'http://localhost:9000/',
apiBaseUrl:'http://localhost:8082/api/'
}
}
})());
I am getting the error saying, Failed to instantiate module myUiApp due to:
[$injector:unpr] Unknown provider: config.
My assumption is injecting config module will invoke the service, which in turn return the json object. any thoughts or suggesstions to do this dynamic config better?
You can only inject providers into an angular .config() block. You're attempting to inject a service, and that is likely the cause of your error.
Also, you have angular.module('config', []) in two different places. This should only be used once to instantiate the module. Use angular.module('config') (without the second argument) subsequently to reference that module.
I would avoid calling the module config, in favor of something that isn't a method used by angular module.config() -- maybe myConfigModule
Secondly, make sure your script includes the constants.js file and the Config.js file before it includes the app.js file
Lastly double check that this situtation is not affecting you:
defining the module twice with angular.module('config', []) ( emphasis on the [ ] ..) When you define the module with the square brackets, you are saying "New Module". In the second file that you include, change it to angular.module('config') -- or, combine the files into this:
angular.module('myConfigModule', [])
.constant('ENV', (function() {
return {
dev: {
appBaseUrl:'http://localhost:9000/',
apiBaseUrl:'http://localhost:8082/api/'
}
}
}).service('config', function($location, ENV) {
return ENV.dev;
});
UPDATE: And typically I see this syntax for controllers, services, anything that is injecting anything else
.service('config', ['$location', 'ENV', function($location, ENV) {
return ENV.dev;
}]); // see beginning and end of square bracket
// also add new injected modules to both the array (surrounded by quotes) and the function

Why my AngularJS + RequireJS application is not building via grunt-contrib-requirejs?

I have src of my application. I use AngularJS. I use RequireJS as module loader. I use Grunt as task runner. When I run application using src: everything is good. When I build application with Grunt, application is not working. I got no errors in console.
Main thing I noticed: my code (code of my application: app.js and files under js/) does not appear in output file which is set in grunt task settings. Also, I don't think there is something about AngularJS.
Main config file:
require.config({
paths: {
'angular' : '../components/angular/angular',
/* etc..... */
'jquery': '../components/jquery/dist/jquery',
'application': './app'
},
shim: {
/* etc */
application: {
deps: ['angular']
},
angular: {
exports : 'angular'
}
},
baseUrl: '/js'
});
require(['application', 'angular', 'ngRoute', 'bootstrap' /* ngRoute and bootstrap from etc :) */], function (app) {
app.init();
});
My app in app.js is:
define([
'require', 'angular', 'main/main', 'common/common'
], function (require) {
'use strict';
var angular = require('angular');
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
I add main RequireJS config file at the end of body tag:
<script type="text/javascript" src="components/requirejs/require.js" data-main="js/bootstrap.js"></script>
Now I have problem. I have Grunt as build system. I have this task:
grunt.initConfig({
requirejs: {
compile: {
options: {
baseUrl: "public/js",
mainConfigFile: "public/js/bootstrap.js",
name: 'bootstrap',
out: "build/js/bootstrap.js",
optimize: 'none'
}
}
},
// etc
I have no optimisation, so I get ~11k lines of code in output file.
As I said. Main problem is: no AngularJS code and no application code in output file.
Why? I set up mainConfigFile correctly. Problem is in RequireJS config file? But everything is okay, when I am running my app on src.
It would be better if you can provide the exactly error output you get. And where you got it (from browser's console or from terminal during build process)
For now I will suggest some adjustments what could possibly help with your case.
angular: {
exports : 'angular'
}
Here you have already export angular.js into global local variable (inside every require and define block).
And by doing var angular = require('angular'); you are possibly asynchronously override angular variable inside your app.js module.
For 'require' being added into define block, as r.js always reading what module got to be loaded in very first step, and then merged into single file. And this may confuse r.js to merging requireJS into itself.
Suggest this adjustment for your app.js:
define([ // Removed 'require' because no needed , it is already global and usable anywhere
'angular', 'main/main', 'common/common'
], function () {
'use strict';
// var angular = require('angular'); // This is a very common mistake. You are not going to call angular this way, requireJS difference with commonJS.
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
And last but not least data-main="js/bootstrap.js" I think it should be js/main.js or a typo.
EDIT added explanations for 'require' in define block, and angular local variable.

Why is my directives module not being included in my Angular app module?

Just learning Angular and I'm encountering a few issues with module resolution. In js/directives/directives.js I have the following directive scoped to a directives module:
angular.module("directives").directive("selectList", function() {
return {
restrict: 'E',
link: function() {
// do stuff
}
}
});
On my webpage:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
<script>
angular.module("editUserApp", ["directives"])
.controller("editUserController", ['$http', '$scope', function($http, $scope) {
// do stuff here
}]
);
</script>
The error I'm getting is as follows:
Error: [$injector:modulerr] Failed to instantiate module editUserApp due to:
[$injector:modulerr] Failed to instantiate module directives due to:
[$injector:nomod] Module 'directives' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
Now, obviously, editUserApp cannot know where directives is, all by itself, so how do I tell it to fetch the directives.js file? Do I have to include it in a script tag (which doesn't seem very scalable)?
I need some way to import directives to my angular app. How can I do this?
You need to include your js/directives/directives.js file into your html and remove the directives dependency on your App module.
your code should be :
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js"></script>
<script>
angular.module("editUserApp", [])
.controller("editUserController", ['$http','directives', '$scope', function($http, $scope,directives) {
// do stuff here
}]
);
</script>
You need
angular.module("directives" ,[])
in your first block
angular.module("directives")
tries to find an existing module called directives
If you are looking for a way to import these files on a as needed basis, you might want to look at http://requirejs.org/ or http://browserify.org/ or similar tools.

This syntax doesn't work with the syntax of Angular's dependency injection, how do I fix this?

I'm sure that this is an easy fix, but I just can't get it sorted out.
I have a app file, a routes file and a config file. Here's each one:
The App File:
(function () {
'use strict';
angular
.module('app', [
'app.config',
'app.routes',
'app.authentication'
]);
angular
.module('app.config', [
'restangular',
'ngAnimate',
'ngMaterial'
]);
angular
.module('app.routes', ['ui.router']);
})();
The Routes file:
(function () {
'use strict';
angular
.module('app.routes')
.config(config);
config.$inject = ['ui.router'];
function config($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('register', {
url:"/register",
controller: 'RegisterController',
controllerAs: 'register',
templateUrl: "/static/templates/authentication/register.html"
})
.state('', {
url:"/",
templateUrl: "/static/templates/landing.html"
});
}
})();
The Config file:
(function () {
'use strict';
angular
.module('app.config')
.config(config);
config.$inject = [
'restangular',
'ngAnimate',
'ngMaterial'
];
function config($locationProvider, $uiViewScrollProvider, RestangularProvider, $mdThemingProvider, $mdIconProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
RestangularProvider.setBaseUrl('/api/');
RestangularProvider.setDefaultHttpFields({xsrfCookieName:'csrftoken', xsrfHeaderName:'X-CSRFToken'});
$uiViewScrollProvider.useAnchorScroll();
$mdThemingProvider.theme('default').primaryPalette('blue').accentPalette('pink');
$mdIconProvider.fontSet('fa', 'fontawesome');
}
})();
And then in my index file I'm including them like so:
<script src="{% static 'app/app.config.js' %}" type="text/javascript"></script>
<script src="{% static 'app/app.routes.js' %}" type="text/javascript"></script>
<script src="{% static 'app/app.js' %}" type="text/javascript"></script>
The error that I'm getting is:
Error: [$injector:nomod] Module 'app.routes' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.4.2/$injector/nomod?p0=app.routes
and
Error: [$injector:nomod] Module 'app.config' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.4.2/$injector/nomod?p0=app.config
The weird thing is they are the abbreviated errors, not the normal really long error when I screw up injection in the past.
You are trying to use the modules before declaring them in your script. You would need to reorder the scripts loaded, i.e app.js needs to be loaded before app.config.js and app.routes.js.
<!-- This comes first before the files that uses it-->
<script src="{% static 'app/app.js' %}" type="text/javascript"></script>
<script src="{% static 'app/app.config.js' %}" type="text/javascript"></script>
<script src="{% static 'app/app.routes.js' %}" type="text/javascript"></script>
Or move the declarations of config and routes to its own files respectively.
//in app.config.js, declare its module
angular
.module('app.config', [
'restangular',
'ngAnimate',
'ngMaterial'
]);
//app.routes.js
angular
.module('app.routes', ['ui.router']);
Well you got more issues: You cannot inject a module, you can only list them as dependency in module declaration. Instead you inject the module's services, providers, controllers etc.. with $inject annotation of array annotation so that they are minification safe.
So you would need to change:
config.$inject = ['ui.router'];
to
config.$inject = ['$stateProvider', '$urlRouterProvider'];
and
config.$inject = [
'restangular',
'ngAnimate',
'ngMaterial'
];
to
config.$inject = [
'$locationProvider',
'$uiViewScrollProvider',
'RestangularProvider',
'$mdThemingProvider',
'$mdIconProvider'
];
I am not sure what other (actual) errors will pop out after you fix this. But if you follow the error message you should be able to fix them. Most Angular error messages are very descriptive (if not use non-minified in dev mode to see more descriptive errors) also have a good read on dependency injection before using it.
The second parameter to define a angular's module is required.
angular.module('app.config', []);
Also, you are defining the app.config and app.routing modules twice. One in app.js and other in app.config.jsand app.routing.js respectively.
If you run angular.module('app.config', []); in the console you will get the same error you posted.
Error: [$injector:nomod]....

ocLazyload does not load new modules

I want dynamically include module into my app module with ocLazyload. My code is
angular.module("one",["oc.lazyLoad"])
.config(function ($ocLazyLoadProvider) {
$ocLazyLoadProvider.config({
modules: [
{
name: 'directive',
files: ['loaded.js']
}
],
debug: true
})
})
.run(function ($ocLazyLoad) { $ocLazyLoad.load('directive')});
angular.module("one").controller("first", function ($scope, $ocLazyLoad) {
});
loaded.js
angular.module("directive", []).directive("blank", function() {
return {
restrict: "E",
template: "<h1>Test</h1>"
}
});
index.html
<body ng-app="one">
<div ng-controller="first">
FOO
<blank></blank>
</div>
</body>
So i can see that loaded.js is loaded successfully. However the module "directive" is not included into dependencies of the "app" module that is why the directive "blank" is not rendered. How i can make ocLazyload to include newly loaded module "directive" into my main module. Does this is supported. Most of examples i have seen used existing module in loaded files.
This question from the FAQ is probably what you are looking for:
https://oclazyload.readme.io/v1.0/docs/faq
I lazy-loaded a directive but nothing changed on my page
If the directive was in your DOM before you lazy-loaded its definition, you need to tell Angular that it should parse the DOM to recompile the new directive. To do that you need to use $compile :
$ocLazyLoad.load('directive').then(function() {
$compile(angular.element('body'))($scope);
});
You could also use the ocLazyLoad directive, it was mainly written just for this case.

Categories

Resources