I'm just getting started with AngularJs. And I'm trying to implement a login directive. But I don't see the output? I've no errors in my console.
My application structure:
(index.html is not visible)
login.directive.js :
(function() {
'use strict';
angular
.module('lnjapp.login',[])
.directive('login', login);
function login() {
var directive = {
template: '<p>test</p>',
//restrict: 'E',
Controller: LoginController,
controllerAs: 'vm'
};
return directive;
}
})();
app.js :
(function () {
'use strict';
angular.module('lnjapp', ['ngRoute', 'ngCookies', 'angular.filter','lnjapp.login','lnjapp.config'])
.constant('GLOBALS', {
url:'http://domain.dev/api/v1/'
});
$(document).ready(function() {
$.material.init();
});
})();
app/pages/login.html:
<login></login>
--EDIT--
login.controller.js:
(function() {
'use strict';
angular.module('lnjapp.login',[])
.controller('LoginController', LoginController);
function LoginController()
{}
})();
route-config.js:
angular
.module('lnjapp.config',[])
.config(config);
function config($routeProvider) {
$routeProvider
.when('/', {
templateUrl: '/app/pages/login.html'
});
}
What am I doing wrong here?
You are creating your lnjapp.login twice, once in login.directive.js and again in login.controller.js. The second time the module is created, it overwrites the first, and whatever was created in the first file will no longer be accessible.
You should always only create a module once, and get the already created module to add additional features in all other cases.
Set (create): angular.module('lnjapp.login',[])
Get (consume): angular.module('lnjapp.login')
For more info and other best practices, see John Papa's excellent Angular Style Guide.
Related
i'm working with angular-seed project.
I'm trying to retreive data from mysql database.
I need to know how to define different controller for each view.
For example, I have this structure:
js
|_modules
|_companies
|_controller.js
|_data.js
|_app.js
|_base.js
I have added this route to app.js
.state('app.companies', {
url: '/companies',
title: 'Companies',
templateUrl: helper.basepath('companies.html'),
controller: 'companiesCtrl' //THIS THROWS THE ERROR BELOW
})
companies.html has scripts added to botom of the page
<script src="app/js/modules/companies/data.js"></script>
<script src="app/js/modules/companies/controller.js"></script>
and this is the code for controller.js (also tested the commented part)
(function() {
'use strict';
angular
.module('appname')
.controller('companiesCtrl', companiesCtrl);
companiesCtrl.$inject = ['$scope','companiesData','$log'];
function companiesCtrl($scope, companiesData, $log) {
console.log('asd'); //NEVER REACH THIS LOG
};
});
/*var app = angular
.module('appname')
.controller('companiesCtrl', ['$scope','companiesData','$log', function($scope, companiesData, $log){
console.log('asd'); //NEVER REACH THIS LOG
$scope.companies = {};
Data.get('companies').then(function(data){
$scope.companies = data.data;
console.log('($scope.companies)');
});
}]);
*/
But I keep getting
Error: [ng:areq] Argument 'companiesCtrl' is not a function, got undefined
Same if I script ng-controller="companiesCtrl" on my view.
change your function to:
(function() {
'use strict';
angular
.module('appname')
.controller('companiesCtrl', companiesCtrl);
companiesCtrl.$inject = ['$scope','companiesData','$log'];
function companiesCtrl($scope, companiesData, $log) {
console.log('asd'); //NEVER REACH THIS LOG
};
})();// execute this function then it will work
See this example if you remove () breaket then it will give you the error.
If possible then create controller like this:
angular.module('appname')
.controller('companiesCtrl', ['$scope', function($scope) {
console.log('asd'); //NEVER REACH THIS LOG
}]);
Please change your controller as :
(function() {
'use strict';
function companiesCtrl($scope, companiesData, $log) {
console.log('asd'); //NEVER REACH THIS LOG
};
angular
.module('appname')
.controller('companiesCtrl', companiesCtrl);
companiesCtrl.$inject = ['$scope','companiesData','$log'];
})();
The problem was on script loading.
I had to use lazy loading of the files within my app.js
Here's the code:
app.js companies route
.state('app.companies', {
url: '/companies',
title: 'Companies',
templateUrl: helper.basepath('companies.html'),
resolve: helper.resolveFor('companiesCtrl'),
controller: 'companiesCtrl'
})
lazy load code
.constant('APP_REQUIRES', {
scripts: {
'modernizr': ['vendor/modernizr/modernizr.custom.js'],
'icons': ['vendor/fontawesome/css/font-awesome.min.css',
'vendor/simple-line-icons/css/simple-line-icons.css'],
'companiesCtrl': ['app/js/modules/companies/data.js','app/js/modules/companies/controller.js']
},
modules: []
});
Trying to get into the habit of using the controller as syntax, but seem to be missing something when trying out a simple example.
When trying to set a property like vm.name = 'John' it does not show up in my view when using {{vm.name}}.
Code is like so
controller
(function() {
'use strict';
angular
.module('myApp')
.controller('HomeController', HomeController);
HomeController.$inject = ['dataService'];
function HomeController(dataService) {
var vm = this;
vm.data = {};
vm.name = 'Richard';
}
routes
'use strict';
angular
.module('myApp')
.config(function($routeProvider) {
$routeProvider.when(
'/view1',
{
templateUrl: 'partials/partial1.html',
controller: 'HomeController',
controllerAs: 'vm'
});
$routeProvider.when(
'/view2',
{
templateUrl: 'partials/partial2.html'
});
$routeProvider.otherwise(
{
redirectTo: '/view1'
});
});
view
<p>View 1</p>
<h1>Hello {{ vm.name }}</h1>
Not sure if you are using angular version more than 1.3, here is the working Plunker
Your HomeController should be changed like this,
app.controller("HomeController", function(){
this.name="Richard";
});
Use $scope. I think it will work. Write your code:
function HomeController(dataService) {
var vm = this;
vm.data = {};
vm.name = 'Richard';}
using $scope, try this one:
function HomeController($scope, dataService) {
$scope.data = {};
$scope.name = 'Richard';}
view
<p>View 1</p>
<h1>Hello {{name}}</h1>
Please read this article too!!!
You need to declare variables on the $scope object to achieve two way bindings. Otherwise its just a regular javascript variables. The scope is like a glue between your view and controller in angularjs.Read Here.
The only thing I can see as an issue is that you dont have ngRoute module included...
var app = angular.module('plunker', [
'ngRoute'
]);
or your dataservice isnt included as a script which is making the injection fail. Based off the information you have included I created a working plunkr for you using the view-model concept instead of $scope.
Heres angulars best practices which has some very useful information and examples on using the view-model concept, accompanied by a lot of other information Angular Guide
I am able to lazy load angularjs with the help of requirejs. But, how can I load modules that needs to be associated to the controller?
My example configuration in app.js looks like the following, loading all the providers and keeping a reference.
var app = angular.module('myApp', ['ui.router'])
var cacheProviders = {};
app.getProvider = function () {
return cacheProviders.$provide;
}
app.getCompileProvider = function () {
return cacheProviders.$compileProvider;
}
app.getControllerProvider = function () {
return cacheProviders.$controllerProvider;
}
app.getFilterProvider = function () {
return cacheProviders.$filterProvider;
}
app.config(['$stateProvider', '$urlRouterProvider', '$controllerProvider', '$compileProvider', '$filterProvider', '$provide',
function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
(function () {
cacheProviders.$controllerProvider = $controllerProvider;
cacheProviders.$compileProvider = $compileProvider;
cacheProviders.$filterProvider = $filterProvider;
cacheProviders.$provide = $provide;
})();
var lazyCtrlLoad = function (controllerName) {
return ["$q", function ($q) {
var deferred = $q.defer();
require([controllerName], function () {
deferred.resolve();
});
return deferred.promise;
}];
}
$stateProvider.state('main.view2b', {
url: '/view2b',
templateUrl: 'forms/empl/searchEmplForm.html',
controllerAs: 'srchC',
controller: 'searchEmplCtrl',
resolve: {
loadOtherCtrl: lazyCtrlLoad('searchEmplCtrl')
}
})
In my other module, I am trying to register controllers, load services..
define([
'angular', 'angularResource'
], function (angular) {
angular.module('myApp')
.getControllerProvider()
.register(ctrl, ...)
But, while loading service below, I need access to $resource which is part of ngResource module in angularResource.
angular.module('myApp')
.getProvider().service('deptService', ['$resource', function ($resource) {
return $resource('/dept/:dept', {dept: '#_dept'});
}])
How can I load ngResource while initalizing the javascript controllers/services lazily?
Take a look to AngularAMD here. It allows you to load controllers in the ui-router without using lazyload. This AngularAMD is used to integrate requireJs and Angular.
$stateProvider
.state('home', {
url: '',
views: {
'#': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/basic/basicTplView.html',
controllerUrl: 'ngApplication/application/shared/layouts/basic/basicTplCtrl.js',
controller: 'basicTplCtrl'
}),
'header#home': angularAmd.route({
templateUrl: 'ngApplication/application/shared/layouts/header/headerView.html',
controllerUrl: 'ngApplication/application/shared/layouts/header/headerCtrl.js',
controller: 'headerCtrl'
})
},
});
Also, you are using requirejs, you can load all the dependencies for an specific controller using the define syntax of requireJs. Let's say you want to create a loginCtroller in a separately file, and this controller depends on another angular service:
define(['app', 'transformRequestAsFormPostService'], function (app) {
app.controller('loginCtrl', ['$scope', '$rootScope', '$sce', '$http', '$state', 'transformRequestAsFormPostService', function ($scope, $rootScope, $sce, $http, $state, transformRequestAsFormPost) {
$scope.login = function () {
/*do something here using the service*/
};
}]);
});
Here, the dependency called transformRequestAsFormPostService is another file, I defined it in the main.js (requireJs confifguration file) and it's defined using the same approach than the loginCtrol. Now I am using it in my project and its working so far so good.
Regards,
Ernesto
I'm going through the AngularJS PhoneCat tutorial and while the application seems to behave correctly when I click through it manually, one of the unit tests are failing for step 8.
Using Karma, I'm getting the following console output:
Chrome 42.0.2311 (Windows 7) PhoneCat controllers PhoneDetailCtrl
should fetch phone detail FAILED Error: [$injector:unpr] Unknown
provider: $routeParamsProvider <- $routeParams
The relevant part of the unit test file (controllersSpec.js) looks like this:
describe("PhoneCat controllers", function () {
describe('PhoneDetailCtrl', function () {
var scope, $httpBackend, ctrl;
beforeEach(inject(function (_$httpBackend_, $rootScope, $routeParams, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/xyz').respond({ name: 'phone xyz' });
$routeParams.phoneId = 'xyz';
scope = $rootScope.$new();
ctrl = $controller('PhoneDetailCtrl', { $scope: scope });
}));
});
});
The problem seems to be related to the declaration of the function parameter of the call to inject(). If I take $routeParams out then the script will execute.
From reading some related StackOverflow questions it seems I might be missing a dependency somewhere. The relevant parts of the karma.conf.js file look like this:
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'../lib/angular/angular.js',
'../lib/angular/angular-mocks.js',
'../lib/angular/angular-route.js',
'../app/*.js',
'unit/*.js'
],
browsers: ['Chrome'],
singleRun: false
});
};
My app.js looks like this:
var phonecatApp = angular.module("phonecatApp", [
"ngRoute",
"phonecatControllers"]);
phonecatApp.config([
"$routeProvider", function($routeProvider) {
$routeProvider
.when("/phones", {
templateUrl: "Scripts/app/partials/phone-list.html",
controller: "PhoneListCtrl"
})
.when("/phones/:phoneId", {
templateUrl: "Scripts/app/partials/phone-detail.html",
controller: "PhoneDetailCtrl"
})
.otherwise({
redirectTo: "/phones"
});
}
]);
And controllers.js looks like this:
var phonecatControllers = angular.module("phonecatControllers", []);
phonecatControllers.controller("PhoneDetailCtrl", ["$scope", "$routeParams", "$http", function ($scope, $routeParams, $http) {
$http.get("api/Phones/" + $routeParams.phoneId).success(function (data) {
$scope.phone = data;
});
}]);
As mentioned at the top, the app seems to work fine when I click through it in the browser so I'm quite confused as to where things are breaking down for the unit tests.
As is often the case with these kind of errors, the fix is pretty obvious when you see it! Basically there are two functions in controllersSpec.js, one for a list of phones and one for an individual phone's details. The phone list tests were working fine because they had
beforeEach(module("phonecatApp")); before the call to beforeEach(inject(function ());
The phone detail tests on the other hand were missing this line. Once I moved the line to the outer function the tests passed as expected.
The working controllersSpec.js looks like this:
describe("PhoneCat controllers", function () {
// This is the line that was moved.
// It existed for PhoneListCtrl but not PhoneDetailCtrl.
beforeEach(module("phonecatApp"));
describe("PhoneListCtrl", function() {
var scope, ctrl, $httpBackend;
beforeEach(module("phonecatApp"));
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
// Setup
}));
// Various tests
});
describe('PhoneDetailCtrl', function () {
var scope, $httpBackend, ctrl;
beforeEach(inject(function (_$httpBackend_, $rootScope, $routeParams, $controller) {
// Setup
}));
// Various tests
});
});
I am trying to lazy load my controllers for my AngularJS app I built along side with requireJS. I have created a custom "lazyLoad" library that creates a resolve object in app.config() routes (also I am using ui-router). If I code the state (without my library) to look like so it works
define(['angular', 'lazyLoader', 'uiRouter'], function(angular, lazyLoader, uiRouter){
var app = angular.module('myApp', ['ui.router']);
app.config(function ($stateProvider, $urlRouterProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
window.lazy = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
$urlRouterProvider.otherwise('/');
$stateProvider
.state('campaigns', {
url:'/campaigns',
views: {
"top-nav" : {
templateUrl: 'views/home/top-nav.html',
resolve : {
load : ['$q', '$rootScope', function($q, $rootScope){
var d = $q.defer();
require(['../app/controllers/header-controller'], function() {
$rootScope.$apply(function(){
d.resolve();
});
});
return d.promise;
}]
}
},
"fullpage": {
templateUrl: 'views/home/index.html',
resolve : {
load : ['$q', '$rootScope', function($q, $rootScope){
var d = $q.defer();
require(['../app/controllers/home-controller'], function() {
$rootScope.$apply(function(){
d.resolve();
});
});
return d.promise;
}]
}
//controller: 'home-controller'
}
}
});
});
return app;
});
If I attempt to replace the resolve object with my library function it looks would look like this:
define(['angular', 'lazyLoader', 'uiRouter'], function(angular, lazyLoader, uiRouter){
and
.state('home', lazyLoader.route({
url:'/',
views: {
"top-nav" : {
templateUrl: 'views/home/top-nav.html',
controllerUrl: '../app/controllers/header-controller'
},
"fullpage": {
templateUrl: 'views/home/index.html',
controllerUrl: '../app/controllers/home-controller'
}
}
}));
lazyLoader.js
define(function () {
'use strict';
function LazyLoader() {}
LazyLoader.prototype.route = function(config){
var controllerPath;
if(config && config.views){
var singleView = Object.keys(config.views);
for(var i in singleView){
var viewName = singleView[i];
controllerPath = config.views[viewName].controllerUrl;
delete config.views.controllerUrl;
config.views[viewName].resolve = {
load : ['$q', '$rootScope', function($q, $rootScope){
var d = $q.defer();
require([controllerPath], function() {
$rootScope.$apply(function(){
d.resolve();
});
});
return d.promise;
}]
};
}
}
return config;
}
return new LazyLoader();
});
Example Controller
define(['app/module'], function (module) {
lazy.controller('header-controller', ['$scope', function ($scope) {
// stuff here
}]);
});
On a side note I plan on implementing something better than attaching lazy variable to window.
When I code the router like the first example it works. When I use my lazyLoader the one of the two views loads it's controller, the second view's controller's file is started to load (console.logs at the beginning show this) but it cannot resolve "module" in the example above.
link to error: AngularJS Error
Again this issue only happens when using my lazyloader which is producing the same resolve object that I have hard coded in for the version that works.
I have searched high and low and there are a lot of resources out there but I could not find anything that addressed this issue.
Any advice is appreciated!
You are taking too much pain to do lazy loading of controllers & services. There is simple approach to lazy load files with ocLazyLoad. This article might help you resolve the same issue.
https://routerabbit.com/blog/convert-angularjs-yeoman-spa-lazyload/
What you should do is
Add a reference of ocLayzLoad & updated JS files’ reference to load on demand from app.js or .html file of their views.
`bower install oclazyload --save-dev`
Now load the module ‘oc.lazyLoad’ in application. Update app.js file
angular
.module('helloWorldApp', [
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'oc.lazyLoad',
])
Load JS file by adding reference of JS in .html file
<div oc-lazy-load="['scripts/controllers/about.js', 'scripts/services/helloservice.js']">
<div ng-controller="AboutCtrl as about">
Your html goes here
</div>
</div>
If you using Grunt, update Gruntfile to uglyfy, renamed file name & update references in the final .html or .js file.
On the 'myApp' module definition, shouldn't you be returning app variable instead of myApp?
And to avoid exposing lazy to window, you could define it as a property of app variable, this way when you define new functions, you require app first and you can use it:
app.js:
app.lazy = {
controller: $controllerProvider.register,
directive: $compileProvider.register,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
...
return app;
controller.js:
define(['app'], function (app) {
app.lazy.controller('header-controller', ['$scope', function ($scope) {
// stuff here
}]);
});