i'm having angularJS app where all controllers are in one file , i want break it down and seperate each controler then refactor it to component based as it's recommended for angularJS 1.5 and above. my current structure looks like this:
.controller('HomeController', ['$scope', function ($scope){
//logic
}
])
and the same for all other controllers are in the same file "controllers.js", in my app.js i do:
var app = angular.module('app', [
'services',
'controllers',
]);
what you advice to break down the controllers into components, thank you for answers and recommendations.
Provided that you are using $scope, you could start by changing from scope to controllerAs syntax, that is, $scope.foo = 'bar' -> <p>{{ foo }}</p> would become this.foo = 'bar' -> <p>{{ ctrl.foo }}</p>.
controllerAs Syntax
From this:
app.controller('HomeController', ['$scope', function ($scope){
$scope.title = 'Home';
}]);
To this:
app.controller('HomeController', [function (){
this.title = 'Home';
}]);
declaring components
Afterward, you'd be able to declare your components in two ways:
Partial refactor using the existing controller:
app.component('homeComponent', {
controller: 'HomeController'
});
Or, full refactor replacing the controller:
app.component('homeComponent', {
controller: [ function (){
this.title = 'Home';
}]
});
refactoring routers
Assuming that you are using ui-router (it's very similar to ngRoute so it wouldn't be a problem).
From this:
$stateProvider
.state('home', {
url: '/',
templateUrl: 'home/home.html',
controller: 'HomeController'
})
To this, for ui-router 1.0+:
$stateProvider
.state('home', {
url: '/',
component: 'homeComponent'
})
Or this, for older versions:
$stateProvider
.state('home', {
url: '/',
template: '<home-component></home-component>'
})
Ref.: Guide: Route to Component
final thoughts
There's a pretty good style guide for angularjs written by John Papa that you should have a look at and use what's worth for you. However, as it's been told by #MikeFeltman it's not accurate with angularjs 1.5 components application style practices; therefore, you should have a look at Todd Motto's revised version of the guide including component application practices.
Disclaimer: I see no point in converting a controller to a directive before making the component, you can declare the component directly because components are special directives and they carry a controller so it's convenient for you.
Related
I am learning angularJS and creating a web application which uses ui-router.
I have defined states as follows:
angular.module('test', [])
.config(function($stateProvider){
$stateProvider.
state('root',{
url: '/',
abstract:true,
templateUrl: '/root.html',
controller: 'MyController'
})
.state('root.route1',{
url: '/route1',
parent: 'root',
views:{
'':{
templateUrl: '/route1.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
.state('root.route2',{
url: '/route2',
parent: 'root',
views:{
'':{
templateUrl: '/route2.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
});
While navigating back and forth between route1 and route2, I want to share scope variables from MyController. When I navigate to route2 from route1, it is loosing value of scope variable.
I am not sure what I am doing wrong.
Can anyone help me?
Thanks in advance.
I have yet to work with the ui-router, but I have worked with AngularJS for the last couple of years and this is how the language generally has worked in the past.
A controller's main purpose is to control the data on a single template. These controllers can communicate to each other through an AngularJS factory, often known as a service. In your case, you probably want to use a service as the controllers are getting destroyed on successful route change.
angular.module('test', [])
.factory('myFactory', function() {
var info = "Hello World";
return {
info: info
};
})
.controller('CtrlOne', function($scope, myFactory) {
$scope.info = myFactory.info;
})
.controller('CtrlTwo', function($scope, myFactory) {
$scope.info = myFacotry.info;
});
You can then use the two controllers on the two different views and they share the variables from the service that connects them.
Use $stateParams to pass parameters between two states.
Fallow the below steps :
Define your state with params object.
.state('route.route1', {
url: 'your url name',
params: {
nameOfParamObj: null
},
controller: 'your controller',
templateUrl: 'your template url',
})
From the controller where you want to send scope data use as fallows
$state.go(toState, params, options);
In toState controller catch state params using $stateParams
$stateParams.yourParamObjectName
Make sure $stateParams, $state services as dependency in your regarding controller
Have a look into the official ui-router documentation Here.
So I'm trying to learn how to use Angulars routing, following tutorials online, and I can't seem to figure out where I'm going wrong. I have the following code:
var app = angular.module('gamersplane', ['controllers', 'ngCookies', 'ngRoute']);
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/pms/:box?', {
controller: 'pmList'
}).when('/pms', {
controller: 'pmList'
}).otherwise({
controller: 'pmList'
});
}])
var controllers = angular.module('controllers', []);
controllers.controller('pmList', function ($scope, $cookies, $http, $routeParams) {
console.log($routeParams);
});
However, no matter what I do, the controller doesn't get hit. I have otherwise in the router, so isn't that where it should hit if all else fails?
Yes it will hit otherwise but you can only define the redirect path into it and that redirect path will tell the url and the controller to set for the $route.current :-
redirectTo: '/pms'
Doc
You need to add a template to each route:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/pms/:box?', {
controller: 'pmList',
template: 'test.html'
}).when('/pms', {
controller: 'pmList',
template: 'test.html'
}).otherwise({
controller: 'pmList',
template: 'test.html'
});
}])
squiroids suggestion regarding otherwise was correct, you won't see a change in your test application though.
Routing is meant to be used to navigate between regions of your application. You could have an app with two routes: pms which shows a list of PMs and pms/:box zu view a particular PM Box. The main task for ngRoute is to replace a placeholder in your application (ng-view) with a given template. Without using a template on the individual routes, your $routeProvider will not navigate as expected.
Given you have two views for the regions (pmBox.html and pmList.html), you could configure your $routeProvider zu setup routing like this: https://gist.github.com/kpko/bd0231ccefbaf8c415c7
Is there a way to build an URL based on the defined Angular routes? Something like Symfony does (http://symfony.com/doc/current/book/routing.html#generating-urls).
Here is an example of how it would be used:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/document/:documentId', {
name: 'document',
templateUrl: 'partials/document.html',
controller: 'documentController'
});
}]);
Then in templates we could use something like:
View document
That would be compiled into:
View document
I have user $stateParams (from angular-ui-router) for this.
.controller('MainCtrl', function ($scope, $stateParams) {
$scope.link = $stateParams.documentId;
});
UI Router wiki on Github
I am trying to create an Angular Dynamic Routing. My routing is like this:
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives']).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', { templateUrl: 'partials/blank.html' });
$routeProvider.when('/:name', { templateUrl: 'partials/blank.html', controller: PagesController });
$routeProvider.otherwise({redirectTo: '/'});
}]);
Here I am using $http to get a template file inside a controller and compile it to a div id like this:
function PagesController($scope, $http, $route, $routeParams, $compile) {
$route.current.templateUrl = 'partials/' + $routeParams.name + ".html";
$http.get($route.current.templateUrl).then(function (msg) {
$('#view-template').html($compile(msg.data)($scope));
});
}
In the template view, I have a div like this:
<div id="view-template" ng-view></div>
I thought the above code will compile and add the html data to the div but I am receiving the error that says: $ is not a function. What have I got wrong here?
EDIT: After the help from the comments and answers below
SOLUTION:: I was playing around with this a bit more and I went with another solution for this. I added the $route.current.templateUrl to the $scope.theTemplateUrl and then used ng-include in the template file. That did the trick and I also dont need to use the jquery $ function to manipulate the DOM.
Please make a fiddle. The limited scope of this snippet inhibits help :)
By just looking at what you are doing I can only make a few recommendations. But I think your issue lies in .html().
Stop using jQuery while you learn Angular.
Use $scope to change content on page. Instead of
$('#view-template').html($compile(msg.data)($scope));
do this
$scope.viewTemplate = msg.data
then use angular in your view :)
Only use the controller to coordinate the flow of information. There should not be and DOM manipulation happening here. The DOM should reflect a state of the controller.
Define routes in your app config. This is not correct.
$route.current.templateUrl = 'partials/' + $routeParams.name + ".html";
I have some example site in my github repo that you can look at if you want to see a few full sites working: https://github.com/breck421
It seems like you have missed some key parts of Angular. Make sure you take your time and learn it right. It will make you life much easier later.
Thanks,
Jordan
Added for a route provider example:
MyApp.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'js/views/index.html',
controller: 'AppController',
activeTab: 'home'
})
.when('/home', {
templateUrl: 'js/views/index.html',
controller: 'AppController',
activeTab: 'home'
})
.when('/thing1', {
templateUrl: 'js/views/thing1.html',
controller: 'Thing1Controller',
activeTab: 'thing1'
})
.otherwise({redirectTo: 'home'});
}]);
Then use links like this: Components
EDIT Adding a compile directive per request:
angular.module('CC.directive.Compile', [], function($compileProvider) {
$compileProvider.directive('compile', ['$compile', function($compile) {
// directive factory creates a link function
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.compile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
}]);
});
The $ function is defined by jQuery, not angular. Make sure that you have included the jQuery library in order to use $
Before use angular-ui-router, one controller always support several router&views, such as:
$routeProvider.
when('/posts', {
templateUrl: 'views/posts/list.html'
}).
when('/posts/create', {
templateUrl: 'views/posts/create.html'
}).
all views for one object share one controller:
app.controller('PostsCtrl', function($scope) {
$scope.create = function() {
// ...
}
$scope.list = function() {
// ...
}
});
and init data in view:
<div data-ng-controller="PostsController" data-ng-init="list()">
...
</div>
But in angular-ui-router, we use state, so we need create several controller for each state, such as:
$stateProvider
.state('posts', {
abstract: true,
url: '/posts',
templateUrl: 'views/posts/list.html',
controller: 'PostsCtrl'
})
.state('posts.detail', {
url: '/:postId',
templateUrl: 'views/posts/detail.html',
controller: 'PostsDetailCtrl'
})
Separating controller seems not a good design pattern.
So, is there any better suggestion to structure controllers?
It is a little late for an answer, but as I was struggling with it and found an answer, I might as well post it.
I agree with Nate's comment that in most circumstances you should keep the controllers as small as possible, i.e. you should write a separate controller for every state. However, if you find yourselves in a situation that really think that using the same controller for both the parent and child views is preferable you can simply omit the controller option in the child view. This view will then use the state of the parent. This can be read in the documentation. A little example:
app.js
app.controller('testController', ['$scope', function($scope){
$scope.message= 'This comes from testController';
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider ,$urlRouterProvider) {
$stateProvider
.state('test', {
url: '/test',
templateUrl: 'partials/test.html',
controller: 'testController'
})
.state('test.child', {
url: '/child',
templateUrl: 'partials/test2.html'
});
}]);
partials/test.html
<div>
Test.html: {{message}}
<div data-ui-view></div>
</div>
partials/test2.html
<div>
Test2.html: {{message}} as well.
</div>
This will produce the following (with url #/test/child)
Test.html: This comes from testController
Test2.html: This comes from testController as well.
I hope this helps anyone