Dec 7, 2018

Angular 7 + Ionic 4: No provider for NavController! (Clue: incorrect module name imported)

NullInjectorError: No provider for NavController!

After switching from AngularJs to Angular 7 and Ionic 4, I've came across a few new issues where led me to high dependence of StackOverflow
Which it also means, edge cases seldom has the proper answer yet, plus Ionic 4 is still in beta build. If SO don't has the answer, I'll have to spend more hours on the issue.

First thing came into my mind when I first look at the error message:

What is NavController has to do with my code while I've never imported it into any of my component. I didn't use NavController at all in my code as Ionic mentioned they are deprecated in Ionic4

After spent 2-3 days of clueless bug hunting, it's found how I imported IonicModule is the culprit!

Capital letter '@Ionic/angular' should be '@ionic/angular'
In short, the solution is to check your module name if they are written in correct case format (yes case sensitive, I've learned my painful lesson).

Quick Solution:
@Ionic/angular should be @ionic/angular

No provider for NavController

...
ERROR Error: StaticInjectorError(AppModule)[HrefDelegate -> NavController]: 
StaticInjectorError(Platform: core)[HrefDelegate -> NavController]: 
  NullInjectorError: No provider for NavController!
  at NullInjector.push../node_modules/@angular/core/fesm5/core.js.NullInjector.get (core.js:717)
  at resolveToken (core.js:954)
  at tryResolveToken (core.js:898)
  at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:795)
  at resolveToken (core.js:954)
  at tryResolveToken (core.js:898)
  at StaticInjector.push../node_modules/@angular/core/fesm5/core.js.StaticInjector.get (core.js:795)
  at resolveNgModuleDep (core.js:17656)
  at NgModuleRef_.push../node_modules/@angular/core/fesm5/core.js.NgModuleRef_.get (core.js:18345)
  at resolveDep (core.js:18716)
...

Like the error message above, Ionic4 didn't really provide very informative clue that helps debugger identify what is rootcause. So yeah, there are risks when we use beta library building our project...
I believe soon later, the error message would be clearer once it's near its official stable build release date.
There is also a slightly useful warning message obtained from browser console, posted as below

...
There are multiple modules with names that only differ in casing.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
// ... list of affected files ...
/node_modules/@ionic/angular/dist/providers/action-sheet-controller.js
/node_modules/@Ionic/angular/dist/util/overlay.js
/node_modules/@ionic/angular/dist/index.js
/node_modules/@Ionic/angular/dist/providers/action-sheet-controller.js
/node_modules/@Ionic/angular/dist/util/util.js
...

Well, the messages in debugger do shine some light about the ambiguous and conflicting module names, the list of affected files don't really tell what has gone wrong.
Hope this article could help some not-so-lucky guys out there who fell into traps set by ourselves.

Mar 14, 2018

Raspberry Pi: Send Email as your personal Notifier!

Configurations

you need smtp installed, to get it, you need (smtp and mail):
  
    sudo apt-get install smtp mail
  

Setup email server configuration (edit smtp config):
  
    /etc/ssmtp/smtp.config
  

into format as below:
  
    root=your@email.com
    mailhub=mail_service_provider #for example "smtp.gmail.com:465"
    FromLineOverride=YES
    rewriteDomain=gmail.com
    AuthUser=your@email.com
    AuthPass=yourpassword
    #UseSTARTTLS=YES
    UseTLS=YES
  

Sending Email in Plaintext

  
    echo "the content of the email body" | mail -s "This is the subject" your@email.com
  

Troubleshooting

Just in case you run into Gmail authorization problems:
1. mail: cannot send message: Process exited with a non-zero status
Probably your smtp.config has it configured incorrectly

2. ssmtp: Cannot open smtp.gmail.com:465
Check if your port number is entered correctly

3. ssmtp: Authorization failed (for Gmail, if you have 2FA activated)
You might have to follow this 2FA Gmail Solution to add an app-specified password for that particular app.

Send Email with attachment (Coming soon...)

Content coming soon... (be patient)

Jan 12, 2018

Wobbling Pokeball with SnapSVG

Creating SVG from ground up

If you've just gotten started, then the first dirty tips to answer your questions (which you might have in mind as a beginner):
  • Yes, you can import external SVG file/code into your SnapSVG Javascript code
  • SnapSVG can help writing pure SVG code from scratch (yes, for simple SVG, but I don't think this is a proper way to write your big animated SVG project from scratch)
  • (my gut tells me) For large SVG project, start SVG project with editor like Adobe Illustrator or Affinity Designer (follow your preferences) then export as SVG file
  • Yes, for every tiny movement/animation has to be coded from scratch (no shortcut, so try to reuse code if possible)
  • Speed development - utilise handy tool like CodePen to help your development!

Steps required (when you get started):

  1. (If you plan to start from scratch) Start learning adding shape/line/text into your element
  2. Animating SVG (mostly achievable by using the ELEMENT.animate())
See the Pen Wobbling pokeball (Snap.svg) by trtshen chaw (@trtshen) on CodePen.

Simple SVG Project with SnapSVG, HTML is not the first priority

As you can see from the example above, there is no HTML code available. It starts right into Javascript code with Snap() and canvas size for your svg

  
    var poke = Snap(230,230)
  

Once you are done declaring required elements, the rest of the code is written to style up your elements and properly group related elements for animating purpose.

Why Group Up Elements?
As you animate element, you have to animate related elements together, so grouping them in a "g" makes our job easier to animate them altogether and more useful for chaining a sequence of continuous animations.

Thanks SnapSVG for the great tool!

Sep 27, 2017

Mount/Unmount external drives through command on Mac (OSX)

Once you have your external device connected to your Mac, the very first step would be making sure your Mac is aware of it, by running:
  
    diskutil list
  
Sample Volumes after running diskutil list

Let's say Mounting the target external drive 1stHDD,

    diskutil mount disk4s1
  

To unmount, run:
  
    diskutil mount disk4s1
  

Working with WinWheel.js and GreenSock's GSAP in Angular

I was assigned a task to make a spinwheel for one of the gamification feature in an webapp which was build with Angular 4.

If you've been working with Angular in Typescript for a while, especially those require 3rd party libraries integrations, you may already experienced the pain integrating stuff which was build to fully compatible with Typescript.

This post is not about solving library integration to your typescript codebase, but how do you get Winwheel.js work in your Angular code which has cost me few amount of trial and error effort, hope my sample code saves some of your development time if you are facing the same issue as I gone through.




As you can see from the typescript code above, the integration of winwheel.js and tweenlite (GSAP) just requires:
  
    import * as Winwheel from 'Winwheel';
    import * as tweenlite from 'tweenlite';
  
Next step is to create a new instance of Winwheel object with:

    this.wheel = new Winwheel.Winwheel(/* your custom config goes here */);

In the rest of your code, you can then start using Winwheel.js API through this.wheel declared above, as the example above does not require data manipulation after the spinning is complete.

GSAP integration

Working with GSAP isn't very hard, but that's one thing I've overlooked at very first time and did aware of the existence of changing target scope to a correct one.

Most of my time spent on fixing the this scope, and found out we have an option to change the this scope with onUpdateScope and this solve most of my headache integrating tweenlite to my winwheel.js.

* In the sample code above is using tweenlite, you can use tweenmax as long as you are pointing to the correct scope.

Improvement/Enhancement

If the spin's end result is required for further calculation, you'll need the service of NgZone to tell Angular to keep an eye on the changes of state.

To do so, you can add following enhancement to the onComplete function:
  
    let onComplete = () => {
      console.log('Done spinning!');
      ngZone.run(() => {
        // do something with the data obtained from the spinwheel
        let prize = this.wheel.getIndicatedSegment();
        console.log(prize);
      });
    };
  

Docs: getIndicatedSegment