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

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.

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!