Materials Angular is Destroying Tab Content

By default materials angular library (materials.angular.io) will destroy the tab content between tab selection changes.  This is usually what people would want though in our case we have a webview (essentially an iframe) that needed to be embedded into the tab.  Every time a user switched tabs the iframe content would be lost as it re-renders from scratch.  We also didn’t like how the scroll bar was reset because of the re-rendering of the tab.

Here is a demo using the materials mat-group and mat-tab components.  To show that the tab doesn’t get reset you can click “play” on the video and then click the other tab and go back.

Working demo: https://angular-tabs-material.stackblitz.io

The main code is having a separate ngFor loop which sits outside of the mat-tab-group component.  In the future, I hope to understand if there is a configuration or override option to the mat-tab-group though this allows us to continue to move forward.

<mat-tab-group (selectedTabChange)="selectionChange($event)">
  <mat-tab *ngFor="let tab of tabs; trackBy:trackByFunction"label="{{tab.tabName}}">
  </mat-tab>
</mat-tab-group>

<div>
  <div *ngFor="let tab of tabs; trackBy:trackByFunction">
  <div [hidden]="currentTab.tabName !== tab.tabName">
    {{tab.tabName}}
    <iframewidth="100%"height="100%" [src]="url"></iframe>
  </div>
</div>

The [hidden] will hide the content for the tabs that should not display. This is different from the *ngFor which will remove the content out of the page.  We have to be careful about how the [hidden].  This is true for browser memory, page rendering time, etc.

Day 1 of NG-CONF 2016 – Links!

I haven’t decide which I like more – Angular 2 or React – both amazing frameworks!  Here are the links I recorded down from the many sessions on Day 1.

Chrome tool
https://augury.angular.io/

Style guide & Resources
https://angular.io/styleguide
https://angular.io/resources/

Mobile
http://mobile.angular.io/

Electron App
https://www.npmjs.com/package/electron-angular-boilerplate

Material 2
https://github.com/angular/material2
http://valor-software.com/ng2-bootstrap/
https://material.angularjs.org/latest/

Kiva – Micro Loans
https://www.kiva.org

Native Script
https://www.nativescript.org/ng-conf

Lucid software chart
http://lucidchart.com/ngconf

Angular Server Side Rendering
https://github.com/angular/universal

Microsoft Code
https://code.visualstudio.com/Docs/?dv=win

Angular CLI
http://cli.angular.io/

Angular Style Guide
jpapa.me/ng2styleguide

Dan Wahlin
Type script snippets: http://tinyurl.com/ng2Snippets

Google says 250ms can make a big difference in user bounce rate

Horizon / Rethinkdb
http://horizon.io/
https://www.npmjs.com/package/horizon

Angular 1.5.x Components
https://github.com/petebacondarwin/ng1-component-demo

SailsJS with Swagger UI

Recently wanted to use Sails JS framework which runs on Node.  I have heard a lot about it and wanted to check it out.  I also wanted to be able to use swagger-ui with it.  I saw the plugin available but ran into a glitch.  This should be resolved *hopefully* soon.  In the meantime if you want them to work together here is what one will need to do:

—–

Since I am not running off master for either project. I combined comments from this thread and hope it helps others:

1) npm install –save lodash

2) Created the controller


1
2
3
4
5
6
7
8
9
// api/controllers/SwaggerController.js

var _ = require('lodash');
var _super = require('sails-swagger/dist/api/controllers/SwaggerController');

_.merge(exports, _super);
_.merge(exports, {
  // Extend with custom logic here by adding additional fields, methods, etc.
});

3) Created the installHooks.js


1
2
3
4
5
6
7
// config/installedHooks.js
module.exports.installedHooks = {
  "sails-swagger": {
    // load the hook into sails.hooks.swagger instead of sails.hooks['sails-swagger']
    "name": "swagger"
  }
};

4) Create swagger.js hook:


1
2
3
4
5
6
7
8
9
10
// config/swagger.js
module.exports.swagger = {
  /**
   * require() the package.json file for your Sails app.
   */
  pkg: require('../package'),
  ui: {
      url: 'http://swagger.balderdash.io'
  }
};

5) Downloaded swagger-ui and copy the dist/ contents to assets/docs under my current webapp. I am sure using docker works well but I didn’t try it as I wanted it all under the same app.

6) Done. (At least I hope I got everything).

AngularU Learning Notes (brain dump)

Day 1

Main Thread <– message passing –> web worker
  • restrictions using web worker – no access to dom
  • can angular and the app business logic could be in a web worker
  • Ex. ionic / onsen ui
Angular 2
  • Angular Renderer (can be ios/angular u) <– —> main js thread
  • Ex. NativeScript (cross platform framework for js applications)
  • React Native — integration between it and A2
  • Angular 1 – DOM template + Data = DOM View
  • Server-side rendering
    • Faster startup
    • SEO
    • Previews
TypeScript
Next milestones
  • Finish Core / API Sugaring / Perf+ / Docs
  • Migration Support / Animate / Material Design / CLI
    • CLI – scaffolding
  • Server Render / Native / Web Workers / ?
Slides
Q/A
Random:
Tomorrow
Angular / TypeScript
egghead.io
Showing Angular2 application
ng6 Why You Should Use ES6 with Angular Today
  • https://angularclass.com/
  • ES2015 now with Angular 1.x
  • ES6 spec is final (ES7 in the works)
  • Angular 1.x is going to be here for a while
  • JSPM uses SystemJS
    • compatible with Babel and Traceur
Webpack
  • Loads and bundles modules
  • Plugin system
  • easy setup
Useful features for Angular
  • Example ES6 class for angular today
    • export default class { constructor($http, myService) { } …
  • Gotchas…
  • It’s not completely ready and depending on the trans-compiler – need to be aware of what works and what doesn’t.
  • ebook – http://exploringjs.com/
Dominating Shadow DOM
Migrating to Angular2
  • Directive that is made more component based
  • Directive sets the things it needs through the attributes of the element controller
  • Directive does not fetch data from server.
  • Observable –
    • new Rq.ReplaySubject();
    • onNext(this.boxes);
  • David East – https://github.com/davideast/angularu-a2-migration
Dominating Shadow DOM
  • Chrome Dev Tools – setting to “Show user agent shadow DOM” (under Elements)
  • Disecting Custom Elements – Element –> Shadow Host
  • Use the createShadowDom() to create shadow dom element.
  • Terminology
    • Shadow host
    • Shadow root
    • etc.
HackStack.js: AngularJS Applications Broken APIs Delivered Late
MEAN stack
Upgrading the Web
Security
  • RFC 1738 – // user : password @ host : port / url-path (1994)
  • “The use of URLs containing passwords that should be secret is clearly uswise”
What’s wrong with the Web?
  • Insecure / Complex
  • Certificate Authorities – many have been hacked / versign, etc.
  • Document Object Model – “Worst API ever” – very hackable if you have access.
    • “I feel confident in saying the the DOM is the worst API ever created.”
  • CSS – Crappy Stylesheets
  • JavaScript – is a hot mess and it’s getting messier
  • Many Have Tried – Microsoft, Apple, Adobe, Oracle – In most cases, the technology was better but was not open and there was no transition.
  • Upgrade the Web – Keep the things it does well.
  • HDTV
  • Helper App – could have an element that if the browser doesn’t know what it is – they call another app that knows what to do with it.
  • Transition Plan
    • convince one progressive browser maker to integrate.
    • convince one secure site to require its customers to use that browser
    • Strong Cryptography – ECC 521 / AES 256 / SHA 3-256
    • ECC521 public keys as unique identifiers
    • Secure JSON over TCP – much more flexible – not just request/response
    • URL – web: publickey @ ipaddress / capability
    • Trust Management – Petnames
    • Vat – would create “Cooperation under mutual suspicion”
    • JavaScript Message Server <–> Qt
  • The Old Web: Promiscuity
  • The new Web: Commitment

Day 2

– Angular 2 Forms
– Building Office 365 SPAs with Angular and CORS
– Directives in Angular: 1 + 2 = WIN
– Componentize your app with Polymer Elements OR Foundation for Apps: Integrating AngularJS with Responsive Web Apps
– Creating d3 Components with Angular2 and TypeScript OR Angular + Cordova + Monaca = Killer mobile app development
– A Token Walks into a SPA…
TypeScript and Angular 2.0
Moving to TypeScript
  • mv files from *.js –> *.ts
  • create tsconfig.json
  • fix errors (still produces correct js files)
  • add classes
  • move to using es6 modules
Angular 2 Forms
Building Office 365 SPAs with Angular and CORS
  • Azure AD – OAuth
  • ADAL JS
    • 2 components – adal.js / adal.angular.js

From the other – https://github.com/jacobscarter/angular-crazy-fast-prototyping

Directives in Angular: 1 + 2 = WIN
Decorators
  • <div ng-click=”handlePaused()”>
  • Gives new functionality to the dom node.
Directives (A2)
  • @Directive({
    selector: ‘[blue]’
    })
    export class Blue { … }
Componentize your app with Polymer Elements
Polymer Starter Kit
Offline
A Token Walks into a SPA…
  • jwt – json web token decode/encode
  • Used decodeJWT filter
  • No state on the server for logged in or not instead we have a jwt or we don’t.
  • RouterOutlet for Angular2
  • Change from RouterOutlet to LoggedInOutlet from ‘./LoggedInOutlet’
  • http://jwt.io/
  • http://auze.ro/auth-angularu
  • @mgonto
Design+Performance

  • Speed is more important than design embellishment
  • Users expect sites to render in under 2 seconds
  • Added in-page reminders for how things are going with performance – example etsy.
  • window.onload is not the best metric for measuring website speed
  • http://www.webpagetest.org/
  • Good metrics
    • Start Render
    • Speed Index
  • Scripts and css have a higher priority
  • Can have scripts labeled async and then they won’t have the high priority to the browser
  • gzipped?
  • custom metrics – define most important elements on the page
  • http://stevesouders.com/talks
  • @souders
  • https://developers.google.com/web/fundamentals/performance/critical-rendering-path/index?hl=en

 

Protractor – Timed Out After 11 Seconds – Continuous Polling

Timed out waiting for Protractor to synchronize with the page after 11 seconds. Please see https://github.com/angular/protractor/blob/master/docs/faq.md

When first trying to setup protractor, I came across this error which is well described in the aforementioned link.

It mentions, if your application continuously polls $timeout or $http, it will never be registered as completely loaded. You should use the $interval service (interval.js) for anything that polls continuously (introduced in Angular 1.2rc3).

This makes complete sense.  In the app that I am currently working on, there is legacy code which needs to be rewritten in order to achieve the use of $interval.

For now, I want to do a proof-of-concept without re-writing code.  So in this case, a timeout is used in order to do continuous pulling.

The temporary solution I used was “browse.executeScript()” function available with protractor.  Since we are able to access angular, we can go and get the service and stop that service.  This then allows protractor to know the page is initially done loading since there are no longer any JavaScript timeouts left.

 

var injector = angular.element(document.body).injector()
var service = injector.get(‘SomeService’);
service.stop();

 

Story for backlog – using $interval instead of $timeout in polling service.

Parsley Validation with AngularJS

This is a quick post on how I was able to get ParsleyJS validation working with AngularJS.  I used Parsley.js 2.x and Angular.js 1.3.x and 1.4.x.

How to use it

Then all you need is to add “parsley-validate” to your form element as an attribute and then parsley will be tied to that form.

For example:
<form parsley-validate>
  <!-- normal form elements here -->
</form>

Note

  • Will load parsley when the form is loaded from angularjs.
  • This takes care of the dynamic inserting of forms into the page.

Code

var angularParselyModule = angular.module('parsley', []);

angularParselyModule.parsleyOptions = {
  priorityEnabled: false,
  errorsWrapper: '<ul class="parsley-error-list"></ul>'
};

angularParselyModule.directive('parsleyValidate', ['$timeout', function($timeout) {
  return {
    restrict: 'A',
    require: '?form',
    link: function(scope, elm, attrs, formController) {
      elm.bind('$destroy', function() {
        formController.parsley.destroy();
      });

      if(!formController.parsley) {
        formController.parsley = new Parsley(elm[0], angularParselyModule.parsleyOptions);
        $timeout(function() {formController.parsley.validate()}, 100);
      }

      scope.$on('feedReceived', function() {
        if(!formController.parsley) {
          formController.parsley = new Parsley(elm[0], angularParselyModule.parsleyOptions);
        }
        formController.parsley.validate();
      });
    }
};
}]);

//We register our parsley logic for various element types.
angularParselyModule.directive('input', parsleyFieldDirective);
angularParselyModule.directive('textarea', parsleyFieldDirective);
angularParselyModule.directive('select', parsleyFieldDirective);

function parsleyFieldDirective($timeout) {
  return {
    restrict: 'E',
    require: '^?form',
    link: function (scope, elm, attrs, formController) {

      if(formController.parsley) {
        $timeout(function() {formController.parsley.validate()}, 150); // Need to validate after the data is in the dom.
      }
    }
  };
}

Final Thoughts

Now with AngularJS 1.3+ we have much better form validation. The benefit of ParsleyJS is that it allows for messages to be reused much more easily then even in the new Angular 1.3. Since the Angular forms are much better integrated with HTML5, I ended up moving in that direction. In order to reuse the messages, ngMessage module can be used with Angular and is much better at reuse though, I made some slight changes to make it even more reusable. Hopefully I will share that in another post.

Default Visual Studio to Local IIS For Debugging

If you have a visual studio project setup in a way that you need to run from a local IIS rather then the IIS express that starts within Visual Studio then there are some quick ways to default your debugging to automatically point to the local.

  • On your project, right-click and go to “Properties”
  • Click on “Web”
  • (optional) Click on “Don’t open a page.”
  • Under servers, Select “Local IIS”
  • In the text box “Project Url”, make sure the correct url is entered for your website.
  • Save and you’re done.

Visual Studio Debug Local IIS

Using Genymotion with Ionic (Cordova, PhoneGap)

Just a quick tutorial here.  I have been using this great Ionic Framework for creating hybrid apps with AngularJS.

Genymotion is a piece of great software for Android developers. It has one of the fastest emulators out there, and now that I know about it, I can’t even imagine developing Android apps without using it. One of the things that make it stand out is that it uses Virtual Box under the hood.

Ionic has a command line for running the app on the emulator, iOS or Android. Just call ionic emulate android and it will fire up an emulator and install the app. Unfortunately, it couldn’t find the Genymotion emulator! Whhhhyyyyy?!

The issue is that it’s not Ionic or Cordova, the real thing that is called behind the scenes.  It’s because Genymotion doesn’t tell android-adb that it is an emulator. It tells that it is a device.  After realizing this – it makes sense.

We need to run as a “real device”. To do that we need to run:

ionic run android

Now it will find the Genymotion Virtual Android and everything will work.

Enjoy!

Ionic Framework / AngularJS Problem Using Infinite Scroll & Refresher

I started using the Ionic Framework for an app that I am creating.  It uses Angular.JS and allows one to really get a simple app up and running fairly quickly.  Part of the app that I am working on needs to use the ion-refresher and the ion-infinite-scroll.

Unfortunately, adding the two together hasn’t proven to work out so well.  It might be because of the beta version of Ionic or it is the originally intention.  Whatever the case may be, I hope this helps others if they come across what I was originally seeing.

Once I added the ion-refresher div and the ion-infinite-scroll div with obvious controller code – I was seeing the following:

Ionic Infinite Scroll Issue

You may need to zoom into the picture but essentially what was happening is the infinite scroll would appear right away as it saw that it was “at the bottom” and needed to load more records.  Really what we want is to have the main “loading data” shown and then the infinite scroll to check to scroll after all data has been loaded and ready.

To fix this – I added a ng-show=’tweets.length’. This just checked to see if the tweets had been loaded yet or not. If they load then show this piece and thus show the loading.

Here is my new current code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <ion-content>
        <ion-refresher on-refresh="refresh()"></ion-refresher>

        <div style="text-align: center" class="padding" ng-show="!tweets.length">
            <i class="icon ion-looping" style="font-size:25px"></i><br />Loading data
        </div>

        <ion-list>
            <ion-item class="item item-avatar" ng-repeat="tweet in tweets">
                <img ng-src="{{tweet.user.profile_image_url}}" />
                <h2>{{tweet.user.name}}</h2>
                <p>{{tweet.text}}</p>
            </ion-item>
        </ion-list>

        <ion-infinite-scroll ng-show="tweets.length"
                on-infinite="loadMore()"
                distance="5%">
        </ion-infinite-scroll>

Now my loading screen looks like the following:

IonicInfiniteScrollIssueResolved

Hope this helps someone else!

Enable NHibernate Logging to see the SQL Statements Executed

We had a need to see the SQL statements that NHibernate was producing.

You can put something like this in your app.config/web.config file :

in the configSections node:

1
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>

In the configuration node:

1
2
3
4
5
6
7
8
9
10
11
12
13
<log4net>
  <appender name="NHibernateFileLog" type="log4net.Appender.FileAppender">
    <file value="logs/nhibernate.txt" />
    <appendToFile value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n"  />
    </layout>
  </appender>
  <logger name="NHibernate.SQL" additivity="false">
    <level value="DEBUG"/>
    <appender-ref ref="NHibernateFileLog"/>
  </logger>
</log4net>

In the assembly.cs file add:

1
[assembly: log4net.Config.XmlConfigurator(Watch=true)]