Angular is a great framework for building Single Page Applications (SPA), especially if you're starting a new project from scratch. Often times, however, we are working with an existing codebase that may have been around for years. In this post I'll describe how we can take advantage of Angular in an existing non-Angular project.
Easy Mode
Angular allows you to specify which part of the DOM it should be managing with the ng-app
directive. So one of the easiest ways to Angularize part of the page is to add the ng-app
attribute to a top-level DOM element that you want managed by Angular:
2 | < p >This paragraph is not managed by Angular.</ p > |
4 | < p >This paragraph is managed by Angular.</ p > |
Most likely you'll need a controller, so specify one via the ng-controller
attribute. Note: most likely your app won't play nicely with angular routes, so using the $routeProvider
service along with ng-view
is not going to be a good idea. Keep it simple and assign a controller manually any of the elements under your ng-app
element:
2 | < p >This paragraph is not managed by Angular.</ p > |
4 | < p >This paragraph is managed by Angular.</ p > |
5 | < div ng-controller = "MyController" > |
6 | < p >This paragraph is managed by Angular and its scope is tied to the MyController controller.</ p > |
At this point, you are now ready to code up MyController
:
1 | function MyController($scope) { |
2 | // controller logic goes here |
Modularizing your Angular Code
Once your app starts to grow in complexity, you may benefit from using Angular modules. To use modules with an existing website, simple specify the module you want to load using the ng-app
attribute:
2 | < p >This paragraph is not managed by Angular.</ p > |
3 | < div ng-app = "CoolModule" > |
4 | < p >This paragraph is managed by Angular.</ p > |
5 | < div ng-controller = "MyController" > |
6 | < p >This paragraph is managed by Angular and its scope is tied to the MyController controller.</ p > |
In JavaScript, you can wire up your module like this:
1 | var module = angular.module( 'CoolModule' , []); |
2 | module.controller( 'MyController' , function ($scope) { |
Advanced Angularizations
Sometimes you won't want to load the angular module immediately on page load. For example, if you have a tabbed interface and one of the tabs needs to be powered by Angular, then you might not want to run any of the Angular code until that tab is selected. To do that, you'll need to fire up your favorite JavaScript editor and manually bootstrap your Angular module when the tab is selected.
To demonstrate this, let's assume a simple tabbed interface where the first tab (non-Angular) is shown by default and the second tab needs to be powered by Angular. A simple jQuery implementation might look something like this:
02 | < li id = "tab1" >Tab 1 (non-Angular)</ li > |
03 | < li id = "tab2" >Tab 2 (Angular)</ li > |
05 | < div class = "tab" rel = "tab1" > |
06 | Some non-Angular stuff |
08 | < div class = "tab" rel = "tab2" > |
14 | $('ul.tabs > li').click(function(event) { |
15 | var id = $(this).attr('id'); |
16 | $('div.tab:visible').hide(0); |
17 | $('div[rel="' + id + '"]).show(); |
With this code, we have a basic working tabbed interface but tab2 is not managed by Angular like it should. To Angularize tab2 and only when it's selected, the code above can be updated like this:
02 | < li id = "tab1" >Tab 1 (non-Angular)</ li > |
03 | < li id = "tab2" >Tab 2 (Angular)</ li > |
05 | < div class = "tab" rel = "tab1" > |
06 | Some non-Angular stuff |
08 | < div class = "tab" rel = "tab2" > |
09 | < div ng-controller = "MyController" > |
16 | $('ul.tabs > li').click(function(event) { |
17 | var id = $(this).attr('id'); |
18 | $('div.tab:visible').hide(0); |
19 | $('div.tab[rel="' + id + '"]).show(); |
21 | // Angularize the tab if tab2 was clicked |
23 | var $tab = $('div.tab[rel="tab2"]'); |
24 | // Only bootstrap this tab if it has not already been bootstrapped |
25 | if (!$tab.hasClass('ng-scope')) { |
26 | angular.bootstrap($tab[0], ['coolModule']); |
The main changes include:
- Add a child DIV with an
ng-controller
attribute to bind it with an Angular controller
- Manually bootstrap the div via
angular.bootstrap()
. This method accepts a DOM node and an array of Angular modules as arguments.
- Add logic to only bootstrap the DIV once. We can take advantage of the fact that Angular adds the class
ng-scope
to the element after it is bootstrapped, so we need only check the presence of this class to determine if we need to bootstrap the element.
Communicating with Angular apps from outside of Angular code
Sometimes you'll need to hint to Angular to enter a digest phase from your non-Angular code. If you are within Angular code, you can always use $scope.$apply()
or $rootScope.$apply()
, but how do you get access to those variables from outside of Angular code?
To get access to the $scope
, simple call angular.element()
and pass in the element that has your ng-controller
attribute. That will return an object with a scope()
method, which will return the $scope
for that element:
1 | var elem = $( 'div[ng-controller]' )[0]; |
2 | var $scope = angular.element(elem).scope(); |
To get the $rootScope
, you can use the same technique, but this time pass in the element that has your ng-app
attribute:
1 | var elem = $( 'div[ng-app]' )[0]; |
2 | var $rootScope = angular.element(elem).scope(); |
Conclusion
Angular isn't just for fresh new green field projects. You can easily introduce your existing website to Angular and take advantage of all its two-way binding, clear separation of concerns, and declarative markup one section at a time.
reference : http://www.virgentech.com/blog/2013/08/how-to-include-angularjs-on-your-existing-website.html
沒有留言:
張貼留言