Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Going mobile with Angular

0ec7fe2c17900b71bd85ff63fc9d8a17?s=47 Ari
December 10, 2013

Going mobile with Angular

Talk at AngularJS Mountain View and San Francisco, 12/9 and 12/10

0ec7fe2c17900b71bd85ff63fc9d8a17?s=128

Ari

December 10, 2013
Tweet

Transcript

  1. Going mobile on AngularJS Ari Lerner ari@fullstack.io

  2. crowd.so/auser

  3. Ari Lerner Co-founder of Fullstack.io Author of ng-book, ng-newsletter.com, The

    Rails 4 Way, and Riding Rails on AngularJS Instructor at HackReactor Mentor at HackBright, Dev Bootcamp Background in systems architecture and distributed systems
  4. Let's build a mobile app

  5. [demo time of desktop version]

  6. Challenges we face

  7. We need to consider: Getting that native app feeling Match

    well-trained user expectations Keep it speedy Make it beautiful
  8. We have to battle: Device fragmentation Platform-specific features Style-PITA

  9. Thinking mobile

  10. User experience

  11. Users hate waiting 2000 4000 6000 8000 10000 Time (ms)

    Users lost interest (%)
  12. Make our apps appear to load fast

  13. Use a splash screen TTP (Time To Pixel)

  14. < ! - - < h e a d >

    < / h e a d > < b o d y > - - > < ! - - i n l i n e s t y l e s h e r e - - > < d i v n g - a p p = " b d a y A p p " n g - c o n t r o l l e r = " F r a m e C t r l " > < ! - - S p l a s h s c r e e n - - > < d i v c l a s s = " s p l a s h " n g - c l o a k = " " > < p > L o a d i n g . . . < / p > < / d i v > < ! - - T h e r e s t o f t h e a p p - - > < / d i v >
  15. [ n g - c l o a k ]

    . s p l a s h { d i s p l a y : b l o c k ! i m p o r t a n t ; } [ n g - c l o a k ] { d i s p l a y : n o n e ; } . s p l a s h { b a c k g r o u n d - c o l o r : b l u e ; }
  16. Load only necessary files on mobile

  17. < l i n k r e l = "

    s t y l e s h e e t " m e d i a = " s c r e e n a n d ( m a x - w i d t h : 9 7 9 p x ) " h r e f = " s t y l e s / m o b i l e . c s s " >
  18. < l i n k r e l = "

    s t y l e s h e e t " d a t a - m e d i a = " s c r e e n a n d ( m a x - w i d t h : 9 7 9 p x ) " d a t a - h r e f = " s t y l e s / m o b i l e . c s s " c l a s s = " m q " >
  19. f u n c t i o n m e

    d i a q u e r y l o a d ( ) { v a r m q s = d o c u m e n t . q u e r y S e l e c t o r A l l ( ' . m q ' ) , a l l = m q s . l e n g t h , c u r r = n u l l , a t t r = n u l l ; w h i l e ( a l l - - ) { c u r r = m q s [ a l l ] ; i f ( c u r r . d a t a s e t . m e d i a & & w i n d o w . m a t c h M e d i a ( c u r r . d a t a s e t . m e d i a ) . m a t c h e s ) { f o r ( a t t r i n c u r r . d a t a s e t ) { i f ( a t t r ! = = ' m e d i a ' ) { c u r r . s e t A t t r i b u t e ( a t t r , c u r r . d a t a s e t [ a t t r ] ) ; } } } } } m e d i a q u e r y l o a d ( ) ;
  20. Lazy loading with Angular

  21. < l i n k r e l = "

    s t y l e s h e e t " n g - i f = " t o u c h " h r e f = " s t y l e s / m o b i l e . c s s " >
  22. CSS images

  23. . p a g e { b a c k

    g r o u n d - i m a g e : u r l ( ' / i m a g e s / d u c k s . p n g ' ) ; } @ m e d i a ( m a x - w i d t h : 3 2 0 p x ) { . p a g e { b a c k g r o u n d - i m a g e : u r l ( ' / i m a g e / s m a l l d u c k s . p n g ' ) ; } }
  24. None
  25. Latency

  26. Minimize payload size CSS JavaScript HTML Images (CSS sprites)

  27. JavaScript uglify yui-compressor

  28. Minify Angular code Post minification a n g u l

    a r . m o d u l e ( ' m y A p p ' ) . c o n t r o l l e r ( ' H o m e C o n t r o l l e r ' , f u n c t i o n ( $ s c o p e ) { $ s c o p e . n a m e = ' A r i ' ; } ) ; a n g u l a r . m o d u l e ( " m y A p p " ) . c o n t r o l l e r ( " H o m e C o n t r o l l e r " , f u n c t i o n ( e ) { e . n a m e = " A r i " } )
  29. Solution: [ ] notation a n g u l a

    r . m o d u l e ( ' m y A p p ' ) . c o n t r o l l e r ( ' H o m e C o n t r o l l e r ' , [ ' $ s c o p e ' , f u n c t i o n ( $ s c o p e ) { / / C o n t r o l l e r d e f i n i t i o n } ] )
  30. ngMin Closure compiler (@ngInject)

  31. CSS cssmin YUI compressor

  32. Inline Images or at least minify them imagemin grunt-base64 /

    grunt-data-uri smush-it
  33. HTML htmlmin html-minify html-minifier

  34. Concatenate files to reduce number of requests Although this will

    change with HTTP 2
  35. Use y e o m a n . i o

    when possible
  36. $ y o a n g u l a r

    $ g r u n t b u i l d
  37. Defer JS execution

  38. Non-angular d o c u m e n t .

    o n r e a d y s t a t e c h a n g e = f u n c t i o n ( ) { i f ( d o c u m e n t . r e a d y S t a t e = = = ' c o m p l e t e ' ) { / / B r o w s e r l o a d e d } }
  39. Angular content a n g u l a r .

    m o d u l e ( ' m y A p p ' , [ ] ) . r u n ( f u n c t i o n ( ) { / / E x e c u t e p a g e f u n c t i o n s h e r e / / a s t h i s i s n o t i n t h e c r i t i c a l / / p a t h a n d i s l o a d e d / / a f t e r t h e D O M i s r e a d y } ) ;
  40. Use promises to load service scripts

  41. a n g u l a r . m o

    d u l e ( ' m y A p p ' ) . p r o v i d e r ( ' F B S e r v i c e ' , f u n c t i o n ( ) { f u n c t i o n c r e a t e S c r i p t ( $ d o c u m e n t , c a l l b a c k , s u c c e s s ) { v a r s c r i p t T a g = $ d o c u m e n t . c r e a t e E l e m e n t ( ' s c r i p t ' ) ; s c r i p t T a g . a s y n c = t r u e ; s c r i p t T a g . s r c = ' / / c o n n e c t . f a c e b o o k . n e t / e n _ U S / a l l . j s ' ; s c r i p t T a g . o n r e a d y s t a t e c h a n g e = f u n c t i o n ( ) { i f ( t h i s . r e a d y S t a t e = = ' c o m p l e t e ' ) { c a l l b a c k ( ) ; } } s c r i p t T a g . o n l o a d = c a l l b a c k ; $ d o c u m e n t . g e t E l e m e n t s B y T a g N a m e ( ' b o d y ' ) [ 0 ] . a p p e n d C h i l d ( s c r i p t T a g ) ; } t h i s . $ g e t = f u n c t i o n ( $ d o c u m e n t , $ t i m e o u t , $ q , $ w i n d o w ) { v a r d e f e r r e d = $ q . d e f e r ( ) ; c r e a t e S c r i p t ( $ d o c u m e n t [ 0 ] , f u n c t i o n ( c a l l b a c k ) { F B . i n i t ( { a p p I D : " 1 2 3 4 2 3 4 2 3 4 1 " } ) ; $ t i m e o u t ( f u n c t i o n ( ) { d e f e r r e d . r e s o l v e ( $ w i n d o w . F B ) ; } ) ; } ) ; r e t u r n d e f e r r e d . p r o m i s e ; } } ) ;
  42. a n g u l a r . m o

    d u l e ( ' m y A p p ' ) . f a c t o r y ( ' F a c e b o o k ' , f u n c t i o n ( $ q , F B S e r v i c e ) { r e t u r n { g e t U s e r P r o f i l e : f u n c t i o n ( ) { r e t u r n F B S e r v i c e . t h e n ( f u n c t i o n ( F B ) { F B . a p i ( ' / m e ' , f u n c t i o n ( u s e r ) { i f ( u s e r . e r r o r ) r e t u r n $ q . r e j e c t ( u s e r . e r r o r ) ; e l s e r e t u r n u s e r ; } ) } ) ; } } } ) ;
  43. Don't use @ i m p o r t in

    CSS
  44. < l i n k r e l = "

    s t y l e s h e e t " h r e f = " / m a i n . c s s " > < l i n k r e l = " s t y l e s h e e t " h r e f = " / m o b i l e . c s s " >
  45. Use sprite images (http://spriteme.org) or glue / grunt-glue or Google's

    PageSpeed service
  46. Use l o c a l S t o r

    a g e for multiple sessions and offline use
  47. a n g u l a r . m o

    d u l e ( ' m y A p p ' ) . c o n t r o l l e r ( ' M a i n C o n t r o l l e r ' , f u n c t i o n ( F B ) { v a r c u r r O f f s e t = 0 ; v a r l s = l o c a l S t o r a g e . g e t I t e m ( ' f r i e n d s - ' + c u r r O f f s e t ) ; i f ( ! l s ) { u p d a t e F r i e n d s ( c u r r O f f s e t ) . t h e n ( f u n c t i o n ( f r i e n d s ) { $ s c o p e . f r i e n d s = f r i e n d s ; } ) ; } e l s e { $ s c o p e . f r i e n d s = J S O N . p a r s e ( l s ) ; } v a r u p d a t e F r i e n d s = f u n c t i o n ( o f f s e t ) { F B . g e t F r i e n d s ( { o f f s e t : o f f s e t } ) . t h e n ( f u n c t i o n ( f r i e n d s ) { l o c a l S t o r a g e . s e t I t e m ( ' f r i e n d s - ' + o f f s e t , J S O N . s t r i n g i f y ( o u t p u t ) ) ; } ) } } ) ;
  48. angular-cache < s c r i p t s r

    c = " b o w e r _ c o m p o n e n t s / a n g u l a r - c a c h e / d i s t / a n g u l a r - c a c h e . j s " > < / s c r i p t > a n g u l a r . m o d u l e ( ' m y A p p ' , [ ' j m d o b r y . a n g u l a r - c a c h e ' ] )
  49. a n g u l a r . s e

    r v i c e ( ' F B ' , f u n c t i o n ( $ a n g u l a r C a c h e F a c t o r y ) { v a r c a c h e = $ a n g u l a r C a c h e F a c t o r y ( ' f b C a c h e ' , { s t o r a g e M o d e : ' l o c a l S t o r a g e ' , d e l e t e O n E x p i r e : ' a g g r e s s i v e ' , m a x A g e : 6 0 0 0 0 , o n E x p i r e : f u n c t i o n ( k e y , v a l ) { F B . g e t F r i e n d s ( ) . t h e n ( f u n c t i o n ( f r i e n d s ) { / / r e f r e s h c a c h e . p u t ( k e y , f r i e n d s ) ; } ) } } ) ; } ) ;
  50. Design

  51. HTML viewport

  52. < m e t a n a m e =

    " v i e w p o r t " c o n t e n t = " w i d t h = d e v i c e - w i d t h , i n i t i a l - s c a l e = 1 . 0 , m a x i m u m - s c a l e = 1 . 0 , u s e r - s c a l a b l e = n o " >
  53. Use SVG fonts

  54. CSS Frameworks

  55. Ionic framework Topcoat Cardinal purecss.io

  56. ChocolateChip-UI Clank Ratchet jQuery mobile / Angular adapter

  57. angular-mobile-nav lungo / lungo-angular-bridge bootstrap zurb and all the other

    ones I missed
  58. How to integrate with them? Custom JavaScript that's dependent upon

    jQuery and angular doesn't know about them
  59. Use libraries not dependent upon jQuery

  60. Custom actions for different viewports

  61. Modernizr (custom)

  62. a n g u l a r . m o

    d u l e ( ' a l T o u c h D e v i c e ' , [ ] ) . p r o v i d e r ( ' T o u c h D e v i c e ' , f u n c t i o n ( ) { v a r d e v i c e T y p e = f a l s e ; t h i s . s e t T o u c h D e v i c e = f u n c t i o n ( t ) { d e v i c e T y p e = t r u e ; } t h i s . $ g e t = f u n c t i o n ( ) { r e t u r n d e v i c e T y p e ; } } ) ;
  63. a n g u l a r . m o

    d u l e ( ' m y A p p ' ) . c o n f i g ( f u n c t i o n ( T o u c h D e v i c e P r o v i d e r ) { i f ( M o d e r n i z r . t o u c h ) T o u c h D e v i c e P r o v i d e r . s e t T o u c h D e v i c e ( ) ; } ) ;
  64. Animations

  65. Prefer CSS animations over JavaScript animations

  66. / / S a s s . l e f

    t { & . n g - e n t e r , & . n g - l e a v e { b a c k g r o u n d : i n h e r i t ; - w e b k i t - t r a n s i t i o n : . 5 5 s e a s e - i n - o u t ; } & . n g - e n t e r { z - i n d e x : 1 0 1 ; - w e b k i t - t r a n s f o r m : t r a n s l a t e X ( 1 0 0 % ) ; & . n g - e n t e r - a c t i v e { - w e b k i t - t r a n s f o r m : t r a n s l a t e X ( 0 ) ; } } & . n g - l e a v e { z - i n d e x : 1 0 0 ; - w e b k i t - t r a n s f o r m : t r a n s l a t e X ( 0 ) ; & . n g - l e a v e - a c t i v e { - w e b k i t - t r a n s f o r m : t r a n s l a t e X ( - 1 0 0 % ) ; } } }
  67. . r u n ( f u n c t

    i o n ( $ r o o t S c o p e , $ w i n d o w ) { $ r o o t S c o p e . a n i m a t i o n s . p a g e D i r e c t i o n = ' ' ; $ r o o t S c o p e . $ o n ( ' $ r o u t e C h a n g e S t a r t ' , f u n c t i o n ( e v t , n e x t , c u r r ) { i f ( c u r r & & n e x t ) { i f ( c u r r . d e p t h < n e x t . d e p t h ) $ r o o t S c o p e . a n i m a t i o n s . p a g e D i r e c t i o n = ' l e f t ' ; e l s e $ r o o t S c o p e . a n i m a t i o n s . p a g e D i r e c t i o n = ' r i g h t ' ; } } ) } )
  68. < d i v c l a s s =

    " c o n t e n t " > < d i v n g - c l a s s = " a n i m a t i o n . p a g e D i r e c t i o n " n g - v i e w = " " > < / d i v > < / d i v >
  69. What about non-hardware accelerated devices?

  70. Use modernizr

  71. Pretending we're a mobile app

  72. ngTouch < s c r i p t s r

    c = " b o w e r _ c o m p o n e n t s / a n g u l a r - t o u c h / a n g u l a r - t o u c h . j s " > < / s c r i p t > a n g u l a r . m o d u l e ( ' m y A p p ' , [ ' n g T o u c h ' ] )
  73. < d i v n g - s w i

    p e - l e f t = " n e x t P a g e ( ) " n g - s w i p e - r i g h t = " p r e v P a g e ( ) " n g - c l a s s = " { ' s w i p e - a n i m a t i o n ' : t o u c h , ' s l i d e - l e f t ' : l o a d i n g = = = ' n e x t ' , ' s l i d e - r i g h t ' : l o a d i n g = = = ' p r e v ' } " > < / d i v >
  74. / / S a S S . s w i

    p e - a n i m a t i o n { & . n g - a n i m a t e . s l i d e - l e f t { a n i m a t i o n : s l i d e L e f t E n t e r 0 . 5 s ; - w e b k i t - a n i m a t i o n : s l i d e L e f t E n t e r 0 . 5 s ; } & . n g - a n i m a t e . s l i d e - l e f t . s l i d e - l e f t - r e m o v e { a n i m a t i o n : s l i d e L e f t L e a v e 0 . 5 s ; - w e b k i t - a n i m a t i o n : s l i d e L e f t L e a v e 0 . 5 s ; } }
  75. Other options angular-gestures/Hammer.js

  76. angular-carousel

  77. angular-snap

  78. Slow(er) DOM parsing

  79. Sensitivity for the DOM

  80. Angular optimizations

  81. Limit filters in the view In a future version of

    angular, this will not apply
  82. < s p a n > { { f r

    i e n d . b i r t h d a y | d a t e : ' M M M M d d ' } } < / s p a n >
  83. a n g u l a r . m o

    d u l e ( ' m y A p p ' ) . c o n t r o l l e r ( ' M a i n C o n t r o l l e r ' , f u n c t i o n ( $ s c o p e , $ f i l t e r ) { v a r u p d a t e F r i e n d s = f u n c t i o n ( ) { F B . g e t F r i e n d s ( ) . t h e n ( f u n c t i o n ( f r i e n d s ) { v a r a r r = [ ] ; a n g u l a r . f o r E a c h ( f r i e n d s , f u n c t i o n ( v a l u e , k e y ) { i f ( v a l u e . b i r t h d a y ) { v a l u e [ ' f o r m a t t e d _ b i r t h d a y ' ] = $ f i l t e r ( ' d a t e ' ) ( v a l u e [ ' b i r t h d a y ' ] , ' M M M M d d ' ) a r r . p u s h ( v a l u e ) ; } } ) ; $ s c o p e . f r i e n d s = $ f i l t e r ( ' o r d e r B y ' ) ( a r r , ' b i r t h d a y ' ) ; } ) } } ) ;
  84. Limit number of watches

  85. Use bindonce for static information

  86. < s c r i p t s r c

    = " b o w e r _ c o m p o n e n t s / a n g u l a r - b i n d o n c e / b i n d o n c e . j s " > < / s c r i p t > a n g u l a r . m o d u l e ( ' m y A p p ' , [ ' p a s v a z . b i n d o n c e ' ] ) ;
  87. < u l c l a s s = "

    l i s t l i s t - g r o u p " > < d i v c l a s s = " s p i n n e r " > < / d i v > < l i b i n d o n c e = " " c l a s s = " t i l e - p h o t o l i s t - g r o u p - i t e m " n g - r e p e a t = " f r i e n d i n f r i e n d s t r a c k b y f r i e n d . i d " > < a b o - h r e f = " ' # / s h a r e / ' + f r i e n d . i d " > < i m g b o - s r c = " ' h t t p s : / / g r a p h . f a c e b o o k . c o m / ' + f r i e n d . i d + ' / p i c t u r e ' " > < d i v c l a s s = " d e t a i l s " > < h 3 b o - t e x t = " f r i e n d . n a m e " > < / h 3 > < s m a l l b o - t e x t = " f r i e n d . f o r m a t t e d _ b i r t h d a y " > < / s m a l l > < / d i v > < s p a n c l a s s = " c h e v r o n " > < / s p a n > < / a > < / l i > < / u l >
  88. Implement paging

  89. v a r u p d a t e F

    r i e n d s = f u n c t i o n ( ) { F B . g e t F r i e n d s ( { l i m i t : l i m i t , o f f s e t : o f f s e t } ) . t h e n ( f u n c t i o n ( f r i e n d s ) { / / . . . } ) } u p d a t e F r i e n d s ( ) ;
  90. $ s c o p e . n e x

    t P a g e = f u n c t i o n ( ) { o f f s e t + = l i m i t ; u p d a t e F r i e n d s ( ) ; } $ s c o p e . p r e v P a g e = f u n c t i o n ( ) { o f f s e t - = l i m i t ; u p d a t e F r i e n d s ( ) ; }
  91. Slow click By default, mobile browsers implement about a 300ms

    delay between touches and firing an event
  92. ngTouch It only rewrites n g - c l i

    c k and is a bit buggy...
  93. Use FastClick < s c r i p t s

    r c = " b o w e r _ c o m p o n e n t s / f a s t c l i c k / l i b / f a s t c l i c k . j s " > < / s c r i p t > a n g u l a r . m o d u l e ( ' m y A p p ' ) . r u n ( f u n c t i o n ( ) { F a s t C l i c k . a t t a c h ( d o c u m e n t . b o d y ) ; } )
  94. Network performance

  95. Limit & batch number of network requests

  96. a n g u l a r . m o

    d u l e ( ' m y A p p . s e r v i c e s ' ) . f a c t o r y ( ' N e t w o r k B a t c h e r ' , f u n c t i o n ( $ h t t p ) { v a r s e r v i c e = { p r o m i s e s : [ ] , a d d C a l l : f u n c t i o n ( o b j ) { v a r p r o m i s e = $ h t t p ( o b j ) ; s e r v i c e . p r o m i s e s . p u s h ( p r o m i s e ) ; r e t u r n p r o m i s e ; } , r u n A l l : f u n c t i o n ( ) { r e t u r n $ q . a l l ( s e r v i c e . p r o m i s e s ) ; } } r e t u r n s e r v i c e ; } )
  97. a n g u l a r . m o

    d u l e ( ' m y A p p . s e r v i c e s ' ) . f a c t o r y ( ' A c c o u n t ' , f u n c t i o n ( N e t w o r k B a t c h e r ) { r e t u r n { g e t A c c o u n t s : f u n c t i o n ( ) { r e t u r n N e t w o r k B a t c h e r . a d d C a l l ( { m e t h o d : ' G E T ' , u r l : ' / v 1 / a c c o u n t s . j s o n ' } ) } } } ) ;
  98. Preload templates # C a c h e a n

    d p r e l o a d t e m p l a t e s $ n p m i n s t a l l - - s a v e - d e v n g t e m p l a t e s $ g r u n t n g t e m p l a t e s
  99. Transfer as little as possible

  100. Native mobile tooling

  101. Phonegap / Cordova

  102. Test on real devices

  103. Mobile Safari remote debugging Mobile Chrome remote debugging

  104. ng-book.com: The Complete Book on AngularJS

  105. Questions?

  106. Thanks!