Building an enterprise-grade app with AngularJS



Building an enterprise-grade app with AngularJS

0 0


pres-lucid-angular

Slides from a presentation that shares our experience building a large enterprise app at Lucidchart using AngularJS

On Github dpashkevich / pres-lucid-angular

Building an enterprise-grade app with AngularJS

by Dmitry Pashkevich

About Me

Dmitry Pashkevich

  • Internet citizen
  • Passionate about web apps
  • Love great user experience
  • Engineer at Lucid Software

We're hiring!www.golucid.co

Collaboration features

Team administration

Problem Statement

Lucidchart Team Admin App

Lucidchart Team Admin App

Lucidchart Team Admin App

Lucidchart Team Admin App

  • UI and code didn’t scale with growing functionality
  • Multiple places to manage account features
  • Poor performance on large team accounts

Solution: Redesign

New Lucidchart Team Admin App

We decided to use Angular

Why Angular?

  • Didn’t think too much about it
  • Popular, actively maintained
  • Supports modern web app principles
  • Known to work with Closure Compiler

What is Angular?

AngularJS is a new, powerful, client-side technology that provides a way of accomplishing really powerful things in a way that embraces and extends HTML, CSS and JavaScript

Source

^ Evolution of existing Web Standards

What Angular gives us and how we use it

1. The MVC* pattern

* actually, it’s MVVM (Model-View-ViewModel)**

** actually, it’s Model-View-Whatever

1. The MVC* pattern

2. HTML Templates

  • Not a new language
  • Easy to author & collaborate
  • Web Components ideology

2. HTML Templates

<context-menu menu-name="context-menu-options">
  <context-item
    ng-repeat="option in options"
    item-click="combo.selectedIndex = $index">
      {{option.label}}
  </context-item>
</context-menu>
                    

3. Two-way data binding

Your name: <input type="text" ng-model="firstName">


<p>Hello, {{firstName}}!</p>


<!-- That's it! No JavaScript written here! -->
                        

4. Routing (Deep Linking)

4. Routing (Deep Linking)

$routeProvider.
  when('/', {
    templateUrl: '/view/MainView.html',
    label: 'Admin'
  }).
  when('/users', {
    templateUrl: '/view/UsersView.html',
    label: 'Users'
  }).
  when('/users/create', {
    templateUrl: '/view/CreateUsersView.html',
    label: 'Add New'
  })
  // …
                        
<a href="#/users/create">New user</a>
                        
                            

5. Dependency injection

// Define account service
var AccountService = function() {
    // account service constructor body...
};

AccountService.prototype.getAccount = function() {
  // code for getting account...
};


// register it with Angular:
TeamApp.service('accountService', AccountService);

                        
// We need the account service
// in our apps controller!
var AppsController = function(accountService,
  samlService, gappsService) {

  // controller's constructor body...
  var account = accountService.getAccount();
}
                        

5. Dependency injection

  • Handles component discovery and import
  • Discourages use of globals
  • Loosely coupled modules

Recap: Angular Gives Us...

MVC HTML templates Two-way data binding Routing Dependency injection

What Angular doesn't give us or what we've built

1. Code architecture

2. Code loading

<!-- Development -->
<script src="/controller/LicensingController.js"></script>
<script src="/service/RestService.js"></script>
<script src="/model/UserRole.js"></script>
<script src="/model/User.js"></script>
<script src="/service/UserService.js"></script>
<script src="/model/License.js"></script>
<script src="/model/LicenseRequest.js"></script>
<script src="/model/Consumer.js"></script>
<script src="/model/Saml.js"></script>
<script src="/model/GAppsDomain.js"></script>
<script src="/model/Account.js"></script>
...
                        
<!-- Production -->
<script src="/teamAdmin.js"></script>
                        

3. Data model

/**
 * @constructor
 * @param {lucid.services.RestLink} userLink
 */
lucid.team.model.User = function(userLink) {
    this.selfLink = userLink;

    /**
     * @type {?string}
     */
    this.username = null;

    /**
     * @type {?string}
     */
    this.email = null;

    /**
     * @type {?Date}
     */
    this.created = null;
}

/**
 * @param {string} productName
 * @return {angular.$q.Promise}
 */
lucid.team.model.User.prototype.getDocuments = function() {
    return lucid.team.model.UserDocuments.getUserDocuments(this);
}

4. Server communication

{
    "uri": "https://localhost/accounts/2",
    "name": "My Team Name",
    "owner": "https://localhost/accounts/2/owner",
    "size": 54,
    "users": "https://localhost/accounts/2/users",
    "groups": "https://localhost/groups?accountId=2",
    "roles": "https://localhost/userRoles?account=2",
    "metadata": "https://localhost/accounts/2/metadata",
    "created": "2014-06-23T17:30:53Z",
    "updated": "2014-07-18T17:58:34Z"
}
                        
AccountService.prototype.accountUsers = function() {
  return this.getAccount()
    .then(function(account) {
        return account.users.get();
      }
    );
}


RoleService.prototype.assignUserRole = function(
userRow, roleName) {

  return userRow.user.roles.post({
    'role': this.getRoleID(roleName)
  });
}
                        

5. UI components

<section class="filters-container" id="filtersPanel">
  <h2>Products &amp; Users</h2>

  <combo-box model="currentProductFilter" options="productFilterOptions">
  </combo-box>
  <combo-box model="currentLicenseFilter" options="licenseFilterOptions">
  </combo-box>
</section>
                        
.component-combo-box {
  .component-pill-with-text;
  .box-sizing(border-box);
  line-height: 18px;

  &-primary-gray {
    .component-combo-box-variant(
      @component-primary-gray,
      @component-primary-gray-hover
    )
  }

  &-secondary-gray {
    .component-combo-box-variant(
      @component-secondary-gray,
      @component-secondary-gray-hover
  }

  ...
}
                        

Shipping to the world

Takeaways

Should I use Angular?

  • Probably
  • Not a monolithic framework
  • What is your current stack?

So how do I make a big app?

  • You don't!
  • Develop small modules
  • Refactor the code as you go
  • Release parts of your app regularly

Bonus: how do you make it fast?

  • It probably already is!
  • Identify the bottleneck
  • It may not be inside Angular(e.g. network communication)

Thanks!