How can I have multiple controller files? - javascript

SOLVED
So after viewing your responses and making minor changes to my code, I found a simple typo which prevented me to reach the result I was after. So thank you all to helping with where I was going wrong, I've now got the controllers seperated and everything's working as planned!
----
I am currently developing a hybrid mobile application using Cordova, Ionic and AngularJS within Visual Studio 2015. Due to the vast amount of code I have in my single controller.js file, I want to separate the code so I have a .js controller file per template; instead of everything in one file. Unfortunately, I I do not understand how to pull this off (learning AngularJS still). I've done some researched but the majority of examples I have seen show a very simple demo, which I replicate with my own code but it still doesn't work. So I was hoping if someone can give me an insight where I may be going wrong.
File Structure in /www
index.html
/js
app.js
controllers.js
/js/controllers
login.js
sales.js
/templates
login.html
sales.html
/js/app .js
angular.module('main', ['ionic', 'main.controllers', 'chart.js', 'ngCordova', 'ngIOS9UIWebViewPatch', 'angular.filter'])
.config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) {
$stateProvider
.state('login', {
cache: false,
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl"
})
.state('sales', {
cache: false,
url: "/sales",
templateUrl: "templates/sales.html",
controller: "SalesCtrl"
})
$urlRouterProvider.otherwise('/login')
$ionicConfigProvider.views.swipeBackEnabled(false);
});
/js/controllers.js
angular.module('main.controllers', ['ionic', 'ngCordova']);
/js/controllers/login.js
angular.module('main.controllers', [])
.controller("LoginCtrl", function ($scope, $state, $cordovaSQLite, $timeout, $ionicPopup, $cordovaDevice, $ionicLoading, $cordovaKeyboard, $cordovaToast) {
$ionicLoading.show({
template: 'Loading...'
});
// DO STUFF
/js/controllers/sales/js
angular.module('main.controllers', [])
.controller("SalesCtrl", function ($scope, $state, $http, $ionicLoading, $cordovaSQLite, $cordovaToast) {
$ionicLoading.show({
template: 'Loading data...'
});
// DO STUFF
Following this structure, I get this error (quote below): https://docs.angularjs.org/error/ng/areq?p0=LoginCtrl&p1=not%20a%20function,%20got%20undefined
Argument 'LoginCtrl' is not a function, got undefined
I managed to get it to work slightly, only when I had login.js and not sales.js, but of course $state.* stopped working when trying to change template. So I know that wasn't 100% either. Hopefully this will make sense, fi it doesn't just clarify what I may be not making sense in, and I shall explain more if need be, appreciate any help. :)
EDIT
index.html
<!-- App references -->
<link href="css/ionic.css" rel="stylesheet" />
<link href="css/angular-chart.css" rel="stylesheet" />
<link href="css/index.css" rel="stylesheet" />
<script src="lib/ionic/ionic.bundle.js"></script>
<script src="lib/ngCordova/ng-cordova.js"></script> <!-- Must be after Ionic but before Cordova-->
<script src="cordova.js"></script>
<script src="scripts/index.js"></script>
<script src="lib/angular-cookies/angular-cookies.min.js"></script>
<script src="lib/angular-chart/Chart.min.js"></script>
<script src="lib/angular-chart/angular-chart.min.js"></script>
<script src="lib/angular-ios9-uiwebview.patch.js"></script>
<script src="lib/angular-filter/angular-filter.min.js"></script>
<script src="js/directives/favourite.js"></script>
<script src="js/controllers.js"></script>
<script src="js/controllers/login.js"></script>
<script src="js/controllers/sales.js"></script>
<script src="js/app.js"></script>

You are redefining your module again & again in your each controller file. Which is clearing out old registered controller from that module.
You have already defined that module in your /js/controllers.js.
angular.module('main.controllers', ['ionic', 'ngCordova']);
So reuse that module in other Javascript files when your are binding any component to it as below.
angular.module('main.controllers')

The best approach is that you clearly separate module creation from module usage.
modules.js:
angular.module('main', ['main.sales']);
angular.module('main.sales', []);
src/sales/scripts/sales-controller.js:
angular.module('main.sales').controller(function() {});
If you concat and minify your js files via grunt or gulp, you should always explicitly include modules.js first, afterwards you can include the rest via a pattern like 'src/**/*.js' for example.
This way the modules are always defined before they are used. If that's not the case, angular will complain about a non existing module.
PS: it's way better to create functional modules (sales related functionality in 1 module) instead of technical modules (all controllers in 1 module)

You are declaring the main.controllers module twice, once for each controller. Also, it's not strictly required to declare a separate module for the controllers, you could just declare the controllers within your main module. Some would argue that you lose reusability - and they would be right - but depending on the size of your project and how tightly coupled your controllers are with your application (90% of the time the answer is: very) you could go that way. Since you are probably just starting out, try doing something like this:
js/app.js
angular.module('main', ['ionic', 'chart.js', 'ngCordova', 'ngIOS9UIWebViewPatch', 'angular.filter'])
.config(function ($stateProvider, $urlRouterProvider, $ionicConfigProvider) {
$stateProvider
.state('login', {
cache: false,
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl"
})
.state('sales', {
cache: false,
url: "/sales",
templateUrl: "templates/sales.html",
controller: "SalesCtrl"
})
$urlRouterProvider.otherwise('/login')
$ionicConfigProvider.views.swipeBackEnabled(false);
});
Note that I am no longer depending on main.controllers? That's because next I'm doing this:
angular.module('main')
.controller("LoginCtrl", function ($scope, $state, $cordovaSQLite, $timeout, $ionicPopup, $cordovaDevice, $ionicLoading, $cordovaKeyboard, $cordovaToast) {
$ionicLoading.show({
template: 'Loading...'
});
// DO STUFF
And this:
angular.module('main')
.controller("SalesCtrl", function ($scope, $state, $http, $ionicLoading, $cordovaSQLite, $cordovaToast) {
$ionicLoading.show({
template: 'Loading data...'
});
// DO STUFF
The different controllers can (and should) be declared each in a separate file, so that you have a clear structure. It might be more correct to have a separate module for the controllers, and I fear my opinion might be unpopular, but I don't really see the point - whereas I strongly urge you to separate your services and your directives into different modules, since it's much more likely that you're going to use them again in other projects.

My suggest we will organize code by split to the module, and inject it in the app.js file
This is detail my way: https://mymai91.github.io/ionic/2016/07/01/ionic-structure-code-for-project.html
Code demo: https://github.com/mymai91/mymaiApp

to anyone looking at the "solved code"...
The Square brackets in the inner controlleres should be removed:
angular.module('main.controllers', [])
-->
angular.module('main.controllers')

Related

$routeParams not populating

Some I'm new to routing and single page web apps, but I've been trying to learn Angular correctly. There's some trouble I'm experiencing with it however and a few weird questions. I followed a guide on structuring your directory and mine looks something like this:
app/
components/
profile/
profile.html
ProfileModel.js
ProfileController.js
ProfileFactory.js
app.module.js
app.routes.js
My main module is located in app.module.js and is dependency injected with ngRoute and profile.app (the module for profile view from ProfileModel.js). It is declared like this:
angular
.module('app', ['ngRoute', 'profile.app'])
.controller('MainController', MainController);
function MainController() {
this.message = 'This is the home page';
}
Then in my app.routes.js file, I have declared all the routes the applications needs (so far only one, which is profile):
angular
.module('app')
.config(routeConfig);
routeConfig.$inject = ['$locationProvider', '$routeProvider'];
function routeConfig ($locationProvider, $routeProvider) {
$routeProvider
.when('/user/:user_id', {
templateUrl: '/app/components/profile/profile.html',
controller: 'ProfileController',
controllerAs: 'profile'
}
)
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
}
This is my ProfileController.js:
angular
.module('app.profile')
.controller('ProfileController', ProfileController);
ProfileController.$inject = ['ProfileFactory', '$routeParams'];
function ProfileController(ProfileFactory, $routeParams) {
var vm = this;
vm.user_id = $routeParams.user;
console.log($routeParams.user_id);
vm = ProfileFactory.userProfile(vm.user_id); //Gets the profile of the user and sets to to vm
}
So I have two main questions. $routeParams.user_id is logged as nothing despite I have defined the route in app.routes.js. This is weird because I have an ng-view directive in my index.html (the HTML file that includes every single .js file). Which means that I should have immediate access to the routing parameters once the controller and its dependencies are instantiated. However, when I go to http://example.com/user/1, I get nothing logged (undefined).
My second question is I included ngRoute as a dependency in profile.app and my main module (in which profile.app is a dependency). However, I later removed ngRoute from profile.app as a dependency, yet I left the injection of $routeParams inside my ProfileController and AngularJS didn't complain at all. This seems weird because the dependency is no longer explicitly present inside profile.app. So how come I can still seemingly inject $routeParams despite not having ngRoute in my profile.app module? Is it because it is getting ngRoute from the main module?
I believe the problem is that you are setting controllerAs to 'profile' but then in the controller you write var vm = this;
These two need to be the same so you could write controllerAs: 'vm', or var profile = this;

Uncaught Error: [$injector:modulerr with angualrJs when i use routing

this is part of my problem = https://jsfiddle.net/2vrz38d6/
it contain just the js part open the console to see what i have
i get confused to solve this error
Error: $injector:modulerr
Module Error
Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.3.10/$injector/modulerr?p0=myApp&p1=Error%3A%…3A9641%2Fassets%2Fglobal%2Fplugins%2Fangularjs%2Fangular.min.js%3A38%3A435)
My Error happen with routing in angularJs
the error which i took it is :
my Error page here
my code is :
(function () {
var MyApp = angular.module('myApp');
MyApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/expenses.html',
controller: 'CurriculumController'
})
.otherwise({
redirectTo: '/'
});
and the controller
MyApp.controller('CurriculumController', ['$scope', '$rest', function ($scope, $rest) {
//some stuf here
for my script angular file :
in the first i have this
<script src="/assets/global/plugins/angularjs/angular.min.js"></script>
and in the middle of the project i have
the file of my angular apps
<script src="/Scripts/NGModel/Curriculum/Curriculum.js"></script>
and after that the file of the routing part for angularjs
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.min.js"></script>
As people above have suggested, you need to include that file, but you also need to make sure that you are doing it in the correct order as well in your index file. I suppose trying a different set of CDNs wouldn't hurt either.
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<script src="https://code.angularjs.org/1.3.15/angular-route.min.js"></script>
Also, in your route configurations, just a mention that you can do this as well for your otherwise:
MyApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/expenses.html',
controller: 'CurriculumController'
})
.otherwise("/");
});
you have to include angular-route.js.
Dependency injection would be angular.module('myApp', ['ngRoute']);
EDIT as the inclusion of Angular libraries were updated in the OP's problem statement :-
Replace
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.min.js"></script> with
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.10/angular-route.min.js"></script> as you have mentioned in comments that you are using v1.3.10
And as #Garrett has suggested, add,
.otherwise("/");
You will need to make this work:
1) Add the angular-route file:
Include the file below the angular.js
<script src="angular-route.js">
2) Inject the ng-route at the definition of your app:
angular.module('myApp', ['ng-route']);
you should add your dependencies in to the module like this
var app = angular.module('myApp', ['ngRoute']);

Angular + Laravel : loading view via ngRoute

I'm facing this problem when combining Laravel and Angular:
Following a very simple tutorial I found online, I was trying to load a view using the ng-view directive. However, I am unable to actually load the template. This is my app.js code:
(function() {
var app = angular.module('profuApp', ['ngRoute']);
app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
.when('/inicio', {
templateUrl: '../templates/inicio.html',
controller: 'homeCtrl'
})
.otherwise({ redirectTo: '/' });
}]);
app.controller('homeCtrl', ['$scope', '$http', function($scope, $http) {
$http.get('http://localhost:8888/profucom/public/getData').success(function(data) {
$scope.datos = data;
});
}])
})();
My file tree:
app/
bootsrap/
public/
js/
angular.min.js
angular-route.min.js
app.js
templates/
inicio.html
. . .
The site loads normally, but when I watch the source code, instead of watching the template inside of inicio.html, this happens...
Code with within my index.blade.php file
<div ng-view></div>
Code I see in the browswer's source code
<!-- ng-view: -->
The network tab on Chrome does not show a 404 error trying to load the view.
What I've tried so far:
Placing templates inside the js folder (templateUrl: 'templates/inicio.html')
Placing templates inside the public folder (templateUrl: '../templates/inicio.html')
also, templateUrl: '/public/templates/inicio.html'
Placing templates inside the root folder (templateUrl: '/templates/inicio.html')
Placing just the file inside the js folder: (templateUrl: 'inicio.html')
None of the above seem to work. Any help, please?
EDIT I also see this happenning in the url: instead of myapp/public/inicio it loads like myapp/public/inicio#/
Where is your index.blade.php located? templateUrl should be the path to the template from whatever file loads the app.js code (I'm assuming that's index.blade.php). So if that's in the root directory, templateUrl: '/public/templates/inicio.html'
As for the issue with the #, read this somewhat related question
Once again, it was nothing but a human mistake. I'm sorry and thankful at the same time for those who spent time trying to answer and comprehend my problem. This is the solution:
I was using the latest stable version directly from a CDN, version 1.3.5; however - and just to see what'd happen - I changed to version 1.2.28. What's the difference? A little syntax.
Instead of what I did above...
app.controller('homeCtrl', ['$scope', '$http', function($scope, $http) {
. . .
I did this:
app.controller('homeCtrl', function($scope, $http) {
. . .
Changing from one version to another was the answer, and changing a little the syntax.
I hope this helps anyone as distracted as I was.

Angular module config not called

I'm trying to get my head around AngularJS and ran into a problem.
var myApp = angular.module('myApp', ['myApp.services']);
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
console.log('Configuring module "myApp"');
$routeProvider.when('/dashboard', {templateUrl: 'partial/dashboard', controller: DashboardController});
$routeProvider.when('/menu', {templateUrl: 'partial/other', controller: OtherController});
$routeProvider.otherwise({redirectTo: '/dashboard'});
$locationProvider.html5Mode(true);
}]);
To my understanding the configuration function should be called when the module gets loaded. But it does not!
I have the ng-app="myApp" directive on the <body> tag. And scripts loaded just before the body closing tag in the correct order (as far as that matters).
<script src="angular.js"></script>
<script src="app.js"></script>
<script src="services.js"></script>
<script src="controllers.js"></script>
</body>
I'm pretty sure I must be overlooking something trivial here. There should be a message printed to the console. But it remains empty with no errors or warnings whatsoever.
The parts of the app not depending on the routes runs just fine.
Update: simplified version of my service.js
'use strict';
angular.module('myApp', function ($provide) {
$provide.factory('myService', function ($http) {
var service = function () {
...
service implementation
...
};
return new service();
});
});
It seems that the method I used for defining the service was overriding my myApp module.
This is the overriding line
angular.module('myApp', function ($provide) ...
This code simply redefines the myApp module and adds a function to configure that one.
I refactored service.js to this simplified version and it started to work.
var myApp = angular.module('myApp');
myApp.factory('securityService', function () {
return SomeFancyServiceObject();
});
I was missing ng-app="myApp" inside my html tag - and I realized this while reading your question :-)
I usually see routing applied by chaining them together:
$routeProvider
.when('/dashboard', {templateUrl: 'partial/dashboard', controller: DashboardController})
.when('/menu', {templateUrl: 'partial/other', controller: OtherController})
.otherwise({redirectTo: '/dashboard'});
Can't see why yours would not work, but might be worth a go.

How to split AngularJS application into smaller modules and handle routing correctly?

What would be the best way to split AngularJS application into smaller pieces/module? For example if I have a blog post and commenting enabled for that, I think I could break it down to modules like "posts" and "comments"(?) (maybe not the best example, but the idea is to split the application logic into separate modules rather than building a huge one-module-app).
I've tried to bootstrap both modules in the separate DOM nodes and use routing in both of the modules accordingly. There are few problems:
As a "single-page" application I'm bootstrapping comments module to be used even on the front page even though it's not used there.
Since I'm not able to use multiple ng-views inside ng-app, I'm forced to write all the wrappers for my modules in the index.html view and bootstrap them? Should it be like that? Seems a bit wrong. How/where should I bootstrap those?
Are there any tips for the routing? Should I spread those in the modules or should I combine them all together somehow? (creating one "blog" module to include "posts" and "comments" modules as dependencies would still make it hard to define for example the "/post/:id" routing..?)
index.html
<div class="post"><ng-view></ng-view></div>
<div class="comments"><ng-view></ng-view></div>
javascript.js
angular.module('posts', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
'template': 'Showing all the posts',
'controller': 'postCtrl
})
.when('/post/:id', {
'template': 'Showing post :id',
'controller': 'postCtrl
});
}]);
angular.module('comments', []).config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/post/:id', {
'template': 'Showing post :id comments',
'controller': 'CommentsCtrl'
});
}]);
angular.bootstrap($('.post'), ['posts']);
angular.bootstrap($('.comments'), ['comments']);
I would divide the app in "view modules" and these sub modules.
Then I use the $routeProvider to switch between the views. I define the different routing config in each module.
If I need further submodules, I load these with ng-include.
/* App Module */
angular.module('MyApp', ['MyApp.home', 'MyApp.blog'])
.config( function myAppConfig ( $routeProvider ) {
'use strict';
$routeProvider.otherwise({ redirectTo: '/home' });
});
/* home Module */
angular.module('MyApp.home', [])
.config(['$routeProvider', function config( $routeProvider ) {
$routeProvider.when('/home', {
controller: 'HomeController',
template: '<p>This is my Home</p>'
});
}]);
I created a little repository on github to explain this.
You can define routes in the submodules:
angular.module('app', ['ngRoute', 'app.moduleX'])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/home', {
templateUrl: 'partials/home.html',
controller: 'HomeCtrl'
});
//Handle all exceptions
$routeProvider.otherwise({
redirectTo: '/home'
});
})
angular.module('app.moduleX', []).config(function($routeProvider) {
$routeProvider.when('/settings', {
templateUrl: 'partials/settings.html',
controller: 'SettingsCtrl'
});
})
I also wrote a blog post about this topic.
We're doing something similar with a portal app and sub-apps. A few things we've discovered:
Only one "app" can have routes and routeParams. Because of this, if the "sub-app" needs access to the $routeParams, you either have to go "old school" for URL parsing, or use an event service.
Speaking of events, there is no Angular service for the apps to communicate, so you'll need to roll your own event service talking to root scope for both apps and inject it into both apps.
I can't see where we used ng-view for the "sub-app". Apparently bootstrapping directly to an element works similarly.
Because only one app can have routes, the apps should be bootstrapped in order. So something like this:
$( function () {
$.when(angular.bootstrap($('.post'), ['posts'])).done( function() {
console.log('POSTS: bootstrapped');
//Manually add the controller to the comments element. (May or may not be
//necessary, but we were doing something that required it to work.)
$('.comments').attr('ng-controller', 'CommentsCtrl');
$.when(angular.bootstrap($('.comments'), ['comments'])).done( function() {
console.log('COMMENTS: bootstrapped');
});
});
});
I hope you can use "ui-router" routing module.
Here is good tutorial for this http://www.ng-newsletter.com/posts/angular-ui-router.html

Categories

Resources